2011年8月18日 09時31分13秒
SODB到RBSP的轉換:
對SODB的最后填充rbsp_trailing_bits就得到RBSP,而這個rbsp_trailing_bits是第一個比特為1,接下來是0,直到字節對齊。比如SODB的最后幾個比特是1001,這時rbsp_trailing_bits即為:1000
SODB 到RBSP到轉換代碼如下:
void SODBtoRBSP(Bitstream*currStream)
{
currStream->byte_buf <<= 1; //左移1bit
currStream->byte_buf |= 1; //在尾部填一個"1"占1bit
currStream->bits_to_go--;
currStream->byte_buf <<= currStream->bits_to_go;
currStream->streamBuffer[currStream->byte_pos++] =currStream->byte_buf;
currStream->bits_to_go = 8;
currStream->byte_buf = 0;
}
前4句就是要得到rbsp_trailing_bits,bits_to_go用來記錄要添0的個數。bits_to_go這個量在進行u_v等函數調用的函數writeUVLC2buffer中是對其不斷進行改變的。
2011年8月17日
19時14分02秒
NALU第一字節就是包括3個語法結構: forbidden_zero_bit(1),nal_ref_idc(2),nal_unit_type(5),加起來正好一個字節, 如下圖所示:
下圖展示的是H.264中的碼流結構:
JM8.6代碼中對H.264的描述:
在編碼函數中,先將NALU填充完,然后再寫nalu的函數WriteAnnexbNALU中,先寫3個字節的起始碼0x000001
即:
putc (0, f);
putc (0, f);
putc (1, f);
BitsWritten += 24;
然后再將NALU寫入到文件中,即:
if (n->len != fwrite (n->buf, 1, n->len, f))
在JM8.6的main函數中,
1. 先調用start_sequence();函數來開始序列:start_sequence()函數中進行了寫序列參數集和圖像參數集的操作。
(1) GenerateSeq_parameter_set_NALU產生序列參數集,WriteNALU函數寫NALU,即:
nalu = GenerateSeq_parameter_set_NALU ();
len += WriteNALU (nalu);
(2) GeneratePic_parameter_set_NALU產生圖像參數集,WriteNALU函數寫NALU,即:
nalu = GeneratePic_parameter_set_NALU ();
len += WriteNALU (nalu);
在函數GenerateSeq_parameter_set_NALU和函數GeneratePic_parameter_set_NALU中都是先將參數集數據寫入到數據結構bitstream->streamBuffer中,然后利用SODBtoRBSP函數對原始數據進行處理得到RBSP,然后再函數RBSPtoNALU函數中將RBSP轉為EBSP 最后調用WriteNALU (nalu)函數將所寫好的nalu寫入到文件中去。
2. 在for循環中調用encode_one_frame函數編碼每一幀,並且包括將編碼完每一幀得到熵編碼得到的碼流 寫入到一個NALU中去,所以說一個NALU就是一幀圖像編碼過程中的編碼結果是保存在currStream->streamBuffer數據結構中的,然后在函數writeUnit中將currStream->streamBuffer中的數據復制到了nalu結構中。其實是在writeUnit函數中先生成了一個nalu數據結構,然后填充nalu的相關數據,最后調用WriteNALU函數將nalu寫入到文件中。
幀圖像:encode_one_frame()
->(1) frame_picture()->code_a_picture()->(while循環)encode_one_slice->encode_one_MB
(2) writeout_picture()->(for循環)writeUnit()->WriteNALU()[准確來說是寫一個slice]
場圖像:encode_one_frame()
->(1)field_picture (top_pic, bottom_pic);->code_a_picture(分別對頂場和底場編碼)
(2)writeout_picture (top_pic);
writeout_picture (bottom_pic);分別寫頂場和底場
通過分析可以知道編碼時是將一個slice作為一個小組(區域單位)進行編碼的,一幀圖像可以包括多個slice。在JM8.6中數據結構的層次是:
Picture{
Slice
{DataPartition
{Bitstream
}
}
}
從這個我們可以看到一幅圖像可以包括多個slice,一個slice可以對應1個Bitstream,而在函數writeUnit中是以slice為單位進行寫的,所以我們可以得到一個NALU中包含一個slice,一般情況下一個slice對應一幅圖像,所以,此時一個NALU也就對應一個slice
3. 最后釋放空間
對於RBSPtoEBSP函數的理解:
這個函數是將RBSP轉為EBSP,需要進行填充防止競爭的0x03,需要對以下幾種進行變化:
0x000000----->0x00000300
0x000001----->0x00000301
0x000002----->0x00000302
0x000003------>0x00000303
在具體的代碼中, 是利用下面的代碼進行實現的:
if(count == ZEROBYTES_SHORTSTARTCODE && !(NAL_Payload_buffer[i] & 0xFC))
{
streamBuffer[j] = 0x03;
j++;
count = 0;
}
注意:在上面的if語句中,count用於統計0x00字節的個數,當count==2時,我們需要檢測接下來的一個字節是否為(00,01,02或03),此處用的方法比較巧妙:0xFC=11111100b,也就是說屏蔽了最后兩位比特,這樣要求NAL_Payload_buffer[i] 必須為0。如果滿足這個說明就是我們要找的那4個。