1. 程式人生 > >對H.264幀型別判斷方法

對H.264幀型別判斷方法

背景描述

我們經常在網路直播推流或者客戶端拉流的時候,需要對獲取到的H.264視訊幀進行判斷後處理,我們經常獲取到各種不同的視訊資料0x67 0x68 0x65 0x61,0x27 0x28 0x25 0x21,0x47 0x48 0x45 0x41,各種不同的編碼晶片有時間出來的NAL Header規則不大一樣,那麼我們怎麼來以統一的方式判斷幀的型別呢:sps、pps、IDR、P

H264

H264在網路傳輸的是NALU,NALU的結構是:NAL頭+RBSP,實際傳輸中的資料流如圖所示:
這裡寫圖片描述

其中NAL頭佔一個位元組,其低5個bit位表示NAL type,具體如下表:

NAL type NAL型別
0 未使用
1 非IDR的片
2 片資料A分割槽
3 片資料B分割槽
4 片資料C分割槽
5 IDR影象的片
6 補充增強資訊單元(SEI)
7 序列引數集(SPS)
8 影象引數集(PPS)
9 分界符
10 序列結束
11 碼流結束
12 填充
13..23 保留
24..31 不保留

RBSP 為原始位元組序列載荷。

NAL type為5,則此幀為I幀即關鍵幀,type為1時為非關鍵幀(P幀…)。
在實際的H264資料幀中,往往幀NAL type前面帶有00 00 00 01 或 00 00 01分隔符,一般來說編碼器編出的首幀資料為PPS與SPS,接著為I幀,然後是P幀…

EasyPusher/EasyRTMP 視訊流推送

EasyPusher和EasyRTMP是通過呼叫攝像機SDK、拉取RTSP流、讀mp4檔案等方式獲取到H264視訊流及音訊視訊流到本地作為視訊源,再以RTSP、RTMP方式推送給流媒體伺服器。它們都是支援Windows、Linux、Android、iOS、ARM等全平臺的視訊流推送程式。
下面介紹下它們在獲取到視訊流到本地後如何區分I幀和P幀等,然後推送的:

//這段程式碼是從檔案中讀取h264資料,然後推送給伺服器
unsigned char *ptr=new unsigned char [sample_size];
fread(ptr, sample_size, 1
, g_fin); //寫一幀資料 --- 可以直接進行網路推送 //fwrite(ptr, sample_size, 1, fout); EASY_AV_Frame avFrame; memset(&avFrame, 0x00, sizeof(EASY_AV_Frame)); /* *ptr的前4位元組是幀分分割符00 00 00 01 , 第5個位元組是NAL type */ unsigned char naltype = ( (unsigned char)ptr[4] & 0x1F); avFrame.pBuffer = (unsigned char*)ptr; avFrame.u32AVFrameLen = sample_size; avFrame.u32VFrameType = (naltype==0x05)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P; avFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG; avFrame.u32TimestampSec = lTimeStamp/1000000; avFrame.u32TimestampUsec = (lTimeStamp%1000000);

如果視訊源不是檔案,而是IPCamera 或者RTSP流等,在他們的視訊流回調中可能已經告知當前幀是I幀還是P幀,就省去了判斷NAL type的步驟。

HI_S32 NETSDK_APICALL OnStreamCallback(HI_U32 u32Handle,/* 控制代碼 */
                                HI_U32 u32DataType,     /* 資料型別,視訊或音訊資料或音視訊複合資料 */
                                HI_U8* pu8Buffer,       /* 資料包含幀頭 */
                                HI_U32 u32Length,       /* 資料長度 */
                                HI_VOID* pUserData      /* 使用者資料*/
                                )
{
    HI_S_AVFrame* pstruAV = HI_NULL;
    HI_S_SysHeader* pstruSys = HI_NULL;

    if (u32DataType == HI_NET_DEV_AV_DATA)
    {
        pstruAV = (HI_S_AVFrame*)pu8Buffer;

        if (pstruAV->u32AVFrameFlag == HI_NET_DEV_VIDEO_FRAME_FLAG)
        {
            if(fPusherHandle == 0 ) return 0;

            if(pstruAV->u32AVFrameLen > 0)
            {
                unsigned char* pbuf = (unsigned char*)(pu8Buffer+sizeof(HI_S_AVFrame));

                EASY_AV_Frame  avFrame;
                memset(&avFrame, 0x00, sizeof(EASY_AV_Frame));
                avFrame.u32AVFrameLen = pstruAV->u32AVFrameLen;
                avFrame.pBuffer = (unsigned char*)pbuf;
                avFrame.u32VFrameType = (pstruAV->u32VFrameType==HI_NET_DEV_VIDEO_FRAME_I)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P;
                avFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG;
                avFrame.u32TimestampSec = pstruAV->u32AVFramePTS/1000;
                avFrame.u32TimestampUsec = (pstruAV->u32AVFramePTS%1000)*1000;
                EasyPusher_PushFrame(fPusherHandle, &avFrame);
            }   
        }
        else
        if (pstruAV->u32AVFrameFlag == HI_NET_DEV_AUDIO_FRAME_FLAG)
        {
            if(fPusherHandle == 0 ) return 0;

            if(pstruAV->u32AVFrameLen > 0)
            {
                //不同IPCamera,這裡資料頭不一樣,需要根據各自的SDK跳過對應的大小。有些可能沒有自定義資料
                unsigned char* pbuf = (unsigned char*)(pu8Buffer+sizeof(HI_S_AVFrame));

                EASY_AV_Frame  avFrame;
                memset(&avFrame, 0x00, sizeof(EASY_AV_Frame));
                avFrame.u32AVFrameLen = pstruAV->u32AVFrameLen-4;//去掉廠家自定義的4位元組頭
                avFrame.pBuffer = (unsigned char*)pbuf+4;
                avFrame.u32AVFrameFlag = EASY_SDK_AUDIO_FRAME_FLAG;
                avFrame.u32TimestampSec = pstruAV->u32AVFramePTS/1000;
                avFrame.u32TimestampUsec = (pstruAV->u32AVFramePTS%1000)*1000;
                EasyPusher_PushFrame(fPusherHandle, &avFrame);
            }           
        }
    }
    else
    if (u32DataType == HI_NET_DEV_SYS_DATA)
    {
        pstruSys = (HI_S_SysHeader*)pu8Buffer;
        printf("Video W:%u H:%u Audio: %u \n", pstruSys->struVHeader.u32Width, pstruSys->struVHeader.u32Height, pstruSys->struAHeader.u32Format);
    }
    return HI_SUCCESS;
}

有了資料來源,只需要呼叫libEasyPusher或libEasyRTMP就可以完成RTSP、RTMP直播推送了!

下載

獲取更多資訊

Copyright © EasyDarwin.org 2012-2017

EasyDarwin