對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