H264碼流分析
title H264BitstreamParser
APP->H264DecoderImpl: Decode(input_image)
H264DecoderImpl->H264BitstreamParser:ParseBitstream(bitstream)
H264BitstreamParser->H264BitstreamParser:H264::indNaluIndices
H264BitstreamParser->H264BitstreamParser:(for) ParseSlice
H264BitstreamParser->SpsParser:ParseSps
H264BitstreamParser->PpsParser:ParsePps
H264BitstreamParser->H264BitstreamParser:ParseNonParameterSetNalu
H264BitstreamParser->H264BitstreamParser:ParseNonParameterSetNalu
H264BitstreamParser->H264BitstreamParser:H264::ParseRbsp
H264BitstreamParser->BitstreamReader:ConsumeBits
H264BitstreamParser->BitstreamReader:ReadExponentialGolomb
H264BitstreamParser->H264BitstreamParser:
H264BitstreamParser->H264BitstreamParser:

1. Sps 結(jié)構(gòu):



2. Pps結(jié)構(gòu)
3.Slice Header結(jié)構(gòu)圖

0. Nalu Type & Slice Type
enum NaluType : uint8_t {
kSlice = 1,
kIdr = 5,
kSei = 6,
kSps = 7,
kPps = 8,
kAud = 9,
kEndOfSequence = 10,
kEndOfStream = 11,
kFiller = 12,
kPrefix = 14,
kStapA = 24,
kFuA = 28
};
enum SliceType : uint8_t { kP = 0, kB = 1, kI = 2, kSp = 3, kSi = 4 };
1. frame_num的檢測
frame_num被用作圖片的標識符,應由比特流中的 log2_max_frame_num_minus4 + 4 位表示。frame_num的約束如下:
變量 PrevRefFrameNum 的導出方式如下:
- 如果當前圖片是IDR圖片,PrevRefFrameNum 被設(shè)為0。
- 否則(當前圖片不是IDR圖片),PrevRefFrameNum 被設(shè)為:
- 如果在8.2.5.2條款規(guī)定的frame_num間隙的解碼過程是由包含在解碼順序中跟隨前一個包含有參考圖片的訪問單元的非參考圖片的解碼過程調(diào)用的,PrevRefFrameNum 被設(shè)為由8.2.5.2條款規(guī)定的frame_num間隙的解碼過程推斷的“不存在的”參考幀的frame_num的值中的最后一個。
- 否則,PrevRefFrameNum 被設(shè)為前一個解碼順序中包含有參考圖片的訪問單元的frame_num的值。
frame_num的值受以下約束:
- 如果當前圖片是IDR圖片,frame_num必須等于0。
- 否則(當前圖片不是IDR圖片),以前一個解碼順序中包含有參考圖片的訪問單元中的主編碼圖片作為前一個參考圖片,當前圖片的frame_num的值不得等于PrevRefFrameNum,除非以下三個條件全部為真:
- a) 當前圖片和前一個參考圖片屬于解碼順序中的連續(xù)訪問單元。
- b) 當前圖片和前一個參考圖片是具有相反奇偶校驗的參考場。
- c) 以下條件之一或多個為真:
- 前一個參考圖片是IDR圖片,
- 前一個參考圖片包含一個memory_management_control_operation語法元素等于5, 注意3 – 當前一個參考圖片包含一個memory_management_control_operation語法元素等于5時,PrevRefFrameNum等于0。
- 在前一個參考圖片之前存在一個主編碼圖片,且該主編碼圖片的frame_num不等于PrevRefFrameNum,
- 在前一個參考圖片之前存在一個主編碼圖片,且該主編碼圖片不是參考圖片。
當frame_num的值不等于PrevRefFrameNum時,比特流符合性要求將遵守以下約束:
a) 在解碼順序中,當前被標記為“用于短期參考”的任何先前場或幀,其frame_num的值不能等于以下變量取得的任何值:
UnusedShortTermFrameNum = (PrevRefFrameNum + 1) % MaxFrameNum while (UnusedShortTermFrameNum != frame_num) UnusedShortTermFrameNum = (UnusedShortTermFrameNum + 1) % MaxFrameNum
b) frame_num的值受以下約束:
- 如果gaps_in_frame_num_value_allowed_flag等于0,則當前圖片的frame_num值必須等于(PrevRefFrameNum + 1) % MaxFrameNum。
- 否則(gaps_in_frame_num_value_allowed_flag等于1),適用以下規(guī)定:
- 如果frame_num大于PrevRefFrameNum,則在比特流中不得有在解碼順序中跟隨前一個參考圖片且在當前圖片之前的非參考圖片,其中以下條件之一為真:
- 非參考圖片的frame_num值小于PrevRefFrameNum,
- 非參考圖片的frame_num值大于當前圖片的frame_num值。
- 否則(frame_num小于PrevRefFrameNum),在比特流中不得有在解碼順序中跟隨前一個參考圖片且在當前圖片之前的非參考圖片,其中以下兩個條件都為真:
- 非參考圖片的frame_num值小于PrevRefFrameNum,
- 非參考圖片的frame_num值大于當前圖片的frame_num值。
- 如果frame_num大于PrevRefFrameNum,則在比特流中不得有在解碼順序中跟隨前一個參考圖片且在當前圖片之前的非參考圖片,其中以下條件之一為真:
包括memory_management_control_operation等于5的圖片應滿足上述frame_num的約束,并且在當前圖片的解碼和內(nèi)存管理控制操作的處理之后,該圖片應被推斷為在解碼過程中所有后續(xù)使用的frame_num等于0,除非在7.4.1.2.4條款中另有規(guī)定。
注意4 – 當主編碼圖片不是IDR圖片且不包含memory_management_control_operation語法元素等于5時,相應的冗余編碼圖片的frame_num值與主編碼圖片中的frame_num值相同。
或者,冗余編碼圖片包括一個memory_management_control_operation語法元素等于5,并且相應的主編碼圖片是IDR圖片。
2.檢查參考幀是否存在
ffmpeg中的h264parser,有一個 GetBitContext,它以bit計數(shù),并留有一個byte的防越界緩沖 size_in_bits_plus8,其初始化是在這里
1 consumed = ff_h2645_extract_rbsp(buf + buf_index, src_length, &rbsp, &nal, 1); 2 if (consumed < 0) 3 break; 4 5 buf_index += consumed; 6 7 ret = init_get_bits8(&nal.gb, nal.data, nal.size);
1 /** 2 * Initialize GetBitContext. 3 * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes 4 * larger than the actual read bits because some optimized bitstream 5 * readers read 32 or 64 bit at once and could read over the end 6 * @param bit_size the size of the buffer in bits 7 * @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow. 8 */ 9 static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer, 10 int bit_size) 11 { 12 int buffer_size; 13 int ret = 0; 14 15 if (bit_size >= INT_MAX - FFMAX(7, AV_INPUT_BUFFER_PADDING_SIZE*8) || bit_size < 0 || !buffer) { 16 bit_size = 0; 17 buffer = NULL; 18 ret = AVERROR_INVALIDDATA; 19 } 20 21 buffer_size = (bit_size + 7) >> 3; 22 23 s->buffer = buffer; 24 s->size_in_bits = bit_size; 25 s->size_in_bits_plus8 = bit_size + 8; 26 s->buffer_end = buffer + buffer_size; 27 s->index = 0; 28 29 return ret; 30 }
檢測參考幀是否存在,要從P幀的slice_header里解析一個num_ref_idx_active_override_flag,通常它是0時ref_count從pps中獲得。如果它不是0,則從碼流中讀一個無符號的指數(shù) Golomb碼作為ref_count[0],最后檢查一下ref_count[0] - 1是否大于15,大于15則出錯。
而如果 num_ref_idx_active_override_flag 被設(shè)置為 0,那么就會使用默認的參考圖像索引數(shù)量,而不是覆蓋它。如果 num_ref_idx_active_override_flag 被設(shè)置為 1,那么當前 slice 中的參考圖像索引數(shù)量將被覆蓋,并且會使用在當前 slice 中指定的數(shù)量。這個數(shù)量通常是通過 slice header 中的額外信息指定的。
if (num_ref_idx_active_override_flag) { ref_count[0] = get_ue_golomb(gb) + 1;
}
//ref_count[0]即它的參考幀數(shù)
然后從碼流中讀取一個bit的ref_pic_list_reordering_flag,當它不為0時繼續(xù)讀一個指數(shù)哥倫布編碼的reordering_of_pic_nums_idc,判斷它的值是否為3。當它的值是0,1,2時,參考圖像的編號要重新排列,重排會使index ++,然而ref_count[0]中經(jīng)常為1,一旦index為1,reference count overflow的錯誤就發(fā)生了。
-
0: 重新排序參考圖像的編號。這意味著需要對參考圖像的編號進行重新排列,以便更有效地利用存儲或者以便于更好地執(zhí)行某些處理。
-
1: 對參考圖像的編號進行重排列,并且交換兩個編號的位置。這個操作通常用于交換兩個相鄰的參考圖像的位置。
-
2: 將參考圖像的編號復制并插入到指定的位置。這個操作通常用于復制參考圖像并將其插入到編碼序列中的某個特定位置。
-
3: 表示結(jié)束MMCO操作(Memory Management Control Operation)的指令。
1 int index; 2 for (index = 0; ; index++) { 3 unsigned int reordering_of_pic_nums_idc = get_ue_golomb_31(gb); 4 5 if (reordering_of_pic_nums_idc < 3) 6 get_ue_golomb_long(gb); 7 else if (reordering_of_pic_nums_idc > 3) { 8 av_log(logctx, AV_LOG_ERROR, 9 "illegal reordering_of_pic_nums_idc %d\n", 10 reordering_of_pic_nums_idc); 11 return AVERROR_INVALIDDATA; 12 } else 13 break; 14 15 if (index >= ref_count[list]) { 16 av_log(logctx, AV_LOG_ERROR, 17 "reference count %d overflow\n", index); 18 return AVERROR_INVALIDDATA; 19 } 20 }
在視頻編碼中,ref_pic_list_modification_flag_l和reordering_of_pic_nums_idc都是H.264/AVC和H.265/HEVC標準中的一部分,用于描述參考圖像列表的修改。
-
ref_pic_list_modification_flag_l:這是一個標志位,用于指示是否要修改參考圖像列表。在H.264/AVC和H.265/HEVC中,參考圖像列表是用于預測當前圖像的運動矢量和殘差的圖像。如果該標志位被設(shè)置為1,那么參考圖像列表將會被修改,否則將保持不變。
-
reordering_of_pic_nums_idc:這是一個指示如何修改參考圖像列表的指令碼。它用于指示參考圖像列表中圖像的重新排序方式。具體來說,它指示了要刪除、移動或添加哪些圖像到參考圖像列表中。
因此,reordering_of_pic_nums_idc字段的作用是基于ref_pic_list_modification_flag_l字段的取值。只有當ref_pic_list_modification_flag_l被設(shè)置為true時,reordering_of_pic_nums_idc字段才會生效,因為只有在需要修改參考圖像列表時,才需要使用reordering_of_pic_nums_idc來指示如何修改列表。
視頻會議中,因為沒有B幀,參考幀列表一般只有1幀,不需要重新排序。
在H.264編碼標準中,modification_of_pic_nums_idc是用于指示參考圖像列表的修改方式的語法元素之一,它在slice頭部的ref_pic_list_modification()語法中使用。modification_of_pic_nums_idc的個數(shù)是根據(jù)slice頭部中的num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1來確定的。
具體規(guī)定如下:
- 如果num_ref_idx_l0_active_minus1大于0,則modification_of_pic_nums_idc的個數(shù)為num_ref_idx_l0_active_minus1 + 1。
- 如果num_ref_idx_l1_active_minus1大于0,則再額外加上num_ref_idx_l1_active_minus1 + 1。
簡單來說,modification_of_pic_nums_idc的個數(shù)等于活動參考圖像列表中的索引數(shù)加一。
在 H.264 標準中,引用圖像列表的重新排序確實有兩個概念:ref_pic_list_reordering 和 ref_pic_list_modification。這可能會導致一些混淆,但它們實際上是指同一種操作。
-
ref_pic_list_reordering:這是在 H.264 標準早期版本中使用的術(shù)語,用于描述對參考圖像列表的重新排序。在參考圖像列表中,編碼器可以通過交換圖像的順序來優(yōu)化編碼效率。這一過程在標準的早期版本中被稱為 ref_pic_list_reordering。
-
ref_pic_list_modification:隨著 H.264 標準的演進,術(shù)語被修改為 ref_pic_list_modification。這是一個更準確的術(shù)語,因為它描述了在解碼器端修改參考圖像列表的操作。在解碼器端,對參考圖像列表的重新排序操作實際上是一種修改操作,因為解碼器必須根據(jù)編碼器發(fā)送的重新排序指令來重新排列圖像。
因此,雖然術(shù)語發(fā)生了變化,但其基本功能和目的保持不變。StreamEye v3.3 中使用的 ref_pic_list_modification 與早期版本中的 ref_pic_list_reordering 是相同的操作,只是術(shù)語上的不同。
StreamEye v3.3 是由公司稱為 "電信科學技術(shù)研究院"(Telecommunication Science and Technology Research Institute)開發(fā)的。它主要用于視頻流分析和處理。根據(jù)我了解到的信息,StreamEye v3.3 于2013年發(fā)布。
關(guān)于 H.264 標準中 ref_pic_list_reordering 到 ref_pic_list_modification 的術(shù)語變更,這發(fā)生在標準的修訂過程中,大約在 2007 年至 2009 年之間的 H.264 標準修訂版本中進行了更改。由于標準的更新通常需要一段時間才能在實際應用中被廣泛采用,因此術(shù)語的變更可能會在實際工具和軟件中出現(xiàn)滯后。

浙公網(wǎng)安備 33010602011771號