日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

H264码流结构分析

發布時間:2025/3/21 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 H264码流结构分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、碼流總體結構:

h264的功能分為兩層,視頻編碼層(VCL)和網絡提取層(NAL)。H.264 的編碼視頻序列包括一系列的NAL 單元,每個NAL 單元包含一個RBSP。一個原始的H.264 NALU 單元常由 [StartCode] [NALU Header] [NALU Payload] 三部分組成,其中 Start Code 用于標示這是一個NALU 單元的開始,必須是"00 00 00 01" 或"00 00 01"。

?

其中RBPS有分為幾種類型:



NAL的解碼單元的流程如下:


?

2、?NAL Header

占一個字節,由三部分組成forbidden_bit(1bit),nal_reference_bit(2bits)(優先級),nal_unit_type(5bits)(類型)。

forbidden_bit:禁止位。

nal_reference_bit:當前NAL的優先級,值越大,該NAL越重要。

nal_unit_type?:NAL類型。參見下表

?

幾個例子:



3、?ffmpeg解析H264流程分析

這是一段實際的碼流


在上面的圖片中,共有三個起始碼:0x00 0000 01

const uint8_t*ff_h264_decode_nal(H264Context*h, const uint8_t *src,int *dst_length, int*consumed, int length)中分析過程為:

h->nal_ref_idc= src[0] >> 5;

h->nal_unit_type= src[0] & 0x1F;

此處src[0]即為06,寫成二進制位0000 0110,則h->nal_ref_idc = 0,h->nal_unit_type = 6

可以判斷這個NALU類型為SEI,重要性優先級為0。

src++;src向后移動一個位置,此時src指向圖中第一行第五列的數據05

length--;未處理數據長度減1

#defineSTARTCODE_TEST?????????????????????????????????????????????????\

???????if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){???? \

???????????if(src[i + 2] != 3){?????????????????????????????????????\

???????????????/* startcode, so we must bepast the end*/???????????? \

???????????????length =i;????????????????????????????????????????????\

???????????}??????????????????????????????????????????????????????????\

???????????break;?????????????????????????????????????????????????????\

???????}

???for(i = 0; i + 1 < length; i += 2) {

???????if(src[i])

???????????continue;

???????if(i > 0 && src[i - 1] == 0)

???????????i--;

???????STARTCODE_TEST;

???}

上述分析:

????????1)如果src[i] !=0 ,則繼續下一次循環,直到src[i] == 0,即等于下一個起始碼的第二個00為止,即圖中地址為000001a0h行的第3列,地址為0x00 00 01 a3(十進制為419),此時i為414,因為是從0x00 00 00 05地址開始的,此時則第一次執行continue下面的if語句,而且src[i-1]==0,則i--,此時i=413

????????2)執行宏定義STARTCODE_TEST,進行起始碼檢測:src[i+ 1] =src[414]=0,src[i+2]=src[415]=0;

繼續執行,src[i+2] != 3,這里是進行競爭檢測(前面有講述,如果在編碼的過程中出現連續00 00 00時,將會在第三個00前插入一個03,避免和起始碼造成沖突),既然沒有發生競爭現象,則表明這個確實是下一個NALU的起始碼

????????3)length是指當前NALU單元長度,這里不包括nalu頭信息長度,即一個字節,

length = i =413

后面的SEI解析不再贅述

(2)第二個 0x00 00 00 01:

?

h->nal_ref_idc= src[0] >> 5;

h->nal_unit_type= src[0] & 0x1F;

此時src[0]是指起始碼后面第一個數據67(地址為0x00 00 01a6h),寫成二進制為0110 0111,則h->nal_unit_type = 7,h->nal_ref_idc =3

則這一個NALU單元類型為SPS

下面開始分析SPS的長度,這一段數據很巧,包含了競爭檢測

依然是下面這段代碼:

src++;src向后移動一個位置,此時src指向圖中地址為00 0001 a7h的數據

length--;未處理數據長度減1

#defineSTARTCODE_TEST?????????????????????????????????????????????????\

???????if(i + 2 < length && src[i + 1] == 0 && src[i + 2]<= 3){???? \

???????????if(src[i + 2] != 3){?????????????????????????????????????\

???????????????/* startcode, so we must bepast the end*/???????????? \

???????????????length =i;????????????????????????????????????????????\

???????????}??????????????????????????????????????????????????????????\

???????????break;?????????????????????????????????????????????????????\

???????}

???for(i = 0; i + 1 < length; i += 2) {

???????if(src[i])

???????????continue;

???????if(i > 0 && src[i - 1] == 0)

???????????i--;

???????STARTCODE_TEST;

???}

分析:

1)自地址0x00 00 01 a7h開始查找src[i] ==0,此時src[i]的地址為0x00 00 01 b1h,此時src[i]==0,而src[i-1]=80 != 0,此時i = 10 ;

2)執行宏定義STARTCODE_TEST:if(i+2 <lengthn&&src[i+1]==0&&src[i+2]<=3)其中src[i+1]=src[11]=0,且src[i+2]=src[12]=3,繼續執行,if(src[i+2] !=3),顯然不滿足,執行break,跳出循環,此時 i=10

此時尚不能確定下一個nalu單元的起始碼在何處,因此當前nalu單元的長度也是未定的,

?

bufidx =h->nal_unit_type == NAL_DPC ? 1: 0;bufidx=0

si =h->rbsp_buffer_size[bufidx];

av_fast_padded_malloc(&h->rbsp_buffer[bufidx],&h->rbsp_buffer_size[bufidx],length+MAX_MBPAIR_SIZE);

dst =h->rbsp_buffer[bufidx];

以上是為當前NALU分配緩存,并清零

memcpy(dst,src, i);將當前確定的nalu內容拷貝到dst緩存中

si = di = i;目的緩存長度和源緩存長度=i=10

while (si + 2< length) {

???????//remove escapes (very rare 1:2^22)

??????if(src[si + 2] > 3) {

???????????dst[di++]= src[si++];

???????????dst[di++]= src[si++];

???????}else if (src[si] == 0 && src[si + 1] == 0) {

???????????if(src[si + 2] == 3) { // escape

???????????????dst[di++]? = 0;

???????????????dst[di++]? = 0;

???????????????si??????? += 3;

???????????????continue;

???????????}else // next start code

???????????????goto nsc;

???????}

???????dst[di++]= src[si++];

???}

nsc:

???memset(dst+ di, 0, FF_INPUT_BUFFER_PADDING_SIZE);

???*dst_length= di;

???*consumed??= si + 1; // +1 forthe header

???/*FIXME store exact number of bits in the getbitcontext

????*(it is needed for decoding) */

???returndst;

分析:src[0]=4D位于0x00 00 01a7h,src[10]=0位于0x00 00 01b1h后面的地址不再贅述,依次為起始位置

從圖中可以看出:


src[10]=00,src[11]=00,src[12]=03,src[13]=00,src[14]=80,src[15]=00,src[16]=00,src[17]=19,src[18]=47,src[19]=8C,src[20]=19,src[21]=50,src[22]=00,src[23]=00,src[24]=00

1)進入while循環,

?

此時si=10:

src[si+2] =src[12]=03且src[11]=00,src[10]=00,則執行:

dst[di++]=0:即dst[10]=0,di=11,dst[11]=0,di=12;

si+=3:si=13,然后continue,開始下一次循環

注:此處的03即為競爭檢測,最后將03跳過了

?

此時si=13:

src[15]=00,src[13]=00,src[14]=80,則執行

dst[di++]=src[si++];

即:dst[12]=src[13]=00,di=13,si=14;

?

此時si=14:

src[16]=00,src[15]=00,src[14]=80

則執行dst[di++]=src[si++];

即:dst[13]=src[14]=00,di=14,si=15

?

此時si=15:

src[15]=00,src[16]=00,src[17]=19

則執行:dst[di++] = src[si++];

????????dst[di++]= src[si++];

??????????????????dst[di++]= src[si++]

即:dst[14]=src[15]=00,di=15,si=16;

???dst[15]=src[16]=00,di=16,si=17;

???dst[16]=src[17]=19,di=17,si=18;

此時si=18:

src[18]=47,src[19]=8C,src[20]=19

執行:

???dst[di++] = src[si++];

????dst[di++]= src[si++];

dst[di++] =src[si++]

即:dst[17]=src[18]=47,di=18,si=19

???dst[18]=src[19]=8C,di=19,si=20

???dst[19]=src[20]=19,di=20,si=21

?

此時si=21:

src[21]=50,src[22]=00,src[23]=00

執行:

????????dst[di++]= src[si++];

即dst[20]=src[21]=50,di=21,si=22

?

?

此時si=22:

src[22]=00,src[23]=00,src[24]=00

執行:

????????? goto nsc;

?

那就看一下nsc:

nsc:

????????memset(dst+ di, 0, FF_INPUT_BUFFER_PADDING_SIZE);

????????*dst_length= di;

???*consumed??= si + 1; // +1 forthe header

???/*FIXME store exact number of bits in the getbitcontext

????*(it is needed for decoding) */

???returndst;

此時di=21,si=22

memset()函數是向SPS單元尾部補零

dst_length=di=21,;即SPS單元的RBSP長度為21,不包括起始碼

consumed=si+1=23;表示SPS單元共消耗的字節數,加1表示SPS單元信息頭占一個字節,注意consumed比dst_length大1,這是由于競爭檢測時,將編碼時添加的一個字節的03過濾掉造成的。

總結

以上是生活随笔為你收集整理的H264码流结构分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。