H264学习_基本数据结构
原文地址:http://blog.csdn.net/yangzhongxuan/article/details/8003494
名詞解釋
場和幀 :??? 視頻的一場或一幀可用來產生一個編碼圖像。在電視中,為減少大面積閃爍現象,把一幀分成兩個隔行的場。
片:???????????? 每個圖象中,若干宏塊被排列成片的形式。片分為I片、B片、P片和其他一些片。
???????????????????? I片只包含I宏塊,P片可包含P和I宏塊,而B片可包含B和I宏塊。
???????????????????? I宏塊利用從當前片中已解碼的像素作為參考進行幀內預測。
???????????????????? P宏塊利用前面已編碼圖象作為參考圖象進行幀內預測。
???????????????????? B宏塊則利用雙向的參考圖象(前一幀和后一幀)進行幀內預測。
???????????????????? 片的目的是為了限制誤碼的擴散和傳輸,使編碼片相互間是獨立的。
???????????????????? 某片的預測不能以其它片中的宏塊為參考圖像,這樣某一片中的預測誤差才不會傳播到其它片中去。
宏塊 :??????? 一個編碼圖像通常劃分成若干宏塊組成,一個宏塊由一個16×16亮度像素和附加的一個8×8 Cb和一個8×8 Cr彩色像素塊組成。
數據之間的關系:
H264結構中,一個視頻圖像編碼后的數據叫做一幀,一幀由一個片(slice)或多個片組成,一個片由一個或多個宏塊(MB)組成,一個宏塊由16x16的yuv數據組成。宏塊作為H264編碼的基本單位。
SODB??????? 數據比特串 ---->最原始的編碼數據,即VCL數據;
RBSP ???? 原始字節序列載荷 ---->在SODB的后面填加了結尾比特(RBSP trailing bits 一個bit“1”)若干比特“0”,以便字節對齊;
EBSP ???? 擴展字節序列載荷 ---- > 在RBSP基礎上填加了仿校驗字節(0X03)它的原因是: 在NALU加到Annexb上時,需要添加每組NALU之前的開始碼StartCodePrefix,如果該NALU對應的slice為一幀的開始則用4位字節表示,ox00000001,否則用3位字節表示ox000001(是一幀的一部分)。另外,為了使NALU主體中不包括與開始碼相沖突的,在編碼時,每遇到兩個字節連續為0,就插入一個字節的0x03。解碼時將0x03去掉。也稱為脫殼操作。
H264/AVC 的分層結構
H.264的主要目標是:
1.高的視頻壓縮比;
2.良好的網絡親和性;
為了完成這些目標H264的解決方案是:
1.VCL?? video coding layer??? 視頻編碼層;
2.NAL?? network abstraction layer?? 網絡提取層;
?
其中,VCL層是對核心算法引擎,塊,宏塊及片的語法級別的定義,他最終輸出編碼完的數據 SODB;
NAL層定義片級以上的語法級別(如序列參數集和圖像參數集,針對網絡傳輸),
同時支持以下功能:獨立片解碼,起始碼唯一保證,SEI以及流格式編碼數據傳送,NAL層將SODB打包成RBSP然后加上NAL頭,組成一個NALU(NAL單元);
H264網絡傳輸的結構
H264在網絡傳輸的是NALU,NALU的結構是:NAL頭+RBSP,實際傳輸中的數據流如圖所示:
?
?
?
???????
NALU頭用來標識后面的RBSP是什么類型的數據,他是否會被其他幀參考以及網絡傳輸是否有錯誤。
NALU頭結構
長度:1byte
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
?
1.forbidden_bit:???????????????????????????? 禁止位,初始為0,當網絡發現NAL單元有比特錯誤時可設置該比特為1,以便接收方糾錯或丟掉該單元。
2.nal_reference_bit:?????????????????? nal重要性指示,標志該NAL單元的重要性,值越大,越重要,解碼器在解碼處理不過來的時候,可以丟掉重要性為0的NALU。
不同類型的NALU的重要性指示如下表所示。?
| nal_unit_type | NAL類型 | nal_reference_bit |
| 0 | 未使用 | ?0 |
| 1 | 非IDR的片 | 此片屬于參考幀,則不等于0, 不屬于參考幀,則等與0 |
| 2 | 片數據A分區 | 同上 |
| 3 | 片數據B分區 | 同上 |
| 4 | 片數據C分區 | 同上 |
| 5 | IDR圖像的片 | 5 |
| 6 | 補充增強信息單元(SEI) | 0 |
| 7 | 序列參數集 | 非0 |
| 8 | 圖像參數集 | 非0 |
| 9 | 分界符 | 0 |
| 10 | 序列結束 | 0 |
| 11 | 碼流結束 | 0 |
| 12 | 填充 | 0 |
| 13..23 | 保留 | ?0 |
| 24..31 | 不保留 | ?0 |
?????? 所謂參考幀,就是在其他幀解碼時需要參照的幀。比如一個I幀可能被一個或多個B幀參考,一個B幀可能被某個P幀參考。
?????? 從這個表我們也可以看出來,DIR的I幀是非常重要的,他一丟,那么這個序列的所有幀都沒辦法解碼了;?????? 序列參數集和圖像參數集也很重要,沒有序列參數集,這個序列的幀就沒法解;
?????? 沒有圖像參數集,那用到這個圖像參數集的幀都沒法解。
3.nal_unit_type:NALU類型取值如下表所示。
| nal_unit_type | NAL類型 | C |
| 0 | 未使用 | ? |
| 1 | 非IDR圖像中不采用數據劃分的片段 | 2,3,4 |
| 2 | 非IDR圖像中A類數據劃分片段 | 2 |
| 3 | 非IDR圖像中B類數據劃分片段 | 3 |
| 4 | 非IDR圖像中C類數據劃分片段 | 4 |
| 5 | IDR圖像的片 | 2,3 |
| 6 | 補充增強信息單元(SEI) | 5 |
| 7 | 序列參數集 | 0 |
| 8 | 圖像參數集 | 1 |
| 9 | 分界符 | 6 |
| 10 | 序列結束 | 7 |
| 11 | 碼流結束 | 8 |
| 12 | 填充 | 9 |
| 13..23 | 保留 | ? |
| 24..31 | 不保留(RTP打包時會用到) | |
RTP 打包時的擴展類型
| 24 | STAP-A | Single-time aggregation packet |
| 25 | STAP-B | Single-time aggregation packet |
| 26 | MTAP16 | Multi-time aggregation packet |
| 27 | MTAP24 | Multi-time aggregation packet |
| 28 | FU-A | Fragmentation unit |
| 29 | FU-B | Fragmentation unit |
| 30-31 | undefined | ? |
RBSP
RBSP數據是下表中的一種
| RBSP類型 | 所寫 | 描述 |
| 參數集 | PS | 序列的全局信息,如圖像尺寸,視頻格式等 |
| 增強信息 | SEI | 視頻序列解碼的增強信息 |
| 圖像界定符 | PD | 視頻圖像的邊界 |
| 編碼片 | SLICE | 編碼片的頭信息和數據 |
| 數據分割 | ? | DP片層的數據,用于錯誤恢復解碼 |
| 序列結束符 | ? | 表明一個序列的結束,下一個圖像為IDR圖像 |
| 流結束符 | ? | 表明該流中已沒有圖像 |
| 填充數據 | ? | 亞元數據,用于填充字節 |
?????? 從前面的分析我們知道,VCL層出來的是編碼完的視頻幀數據,
?????? 這些幀可能是I、B、P幀,而且這些幀可能屬于不同的序列,再者同一個序列還有相對應的一套序列參數集和圖片參數集等等,
?????? 所以要完成視頻的解碼,不僅需要傳輸VCL層編碼出來的視頻幀數據,還需要傳輸序列參數集、圖像參數集等數據。
?????? 參數集:包括序列參數集 SPS? 和圖像參數集 PPS
?????? SPS 包含的是針對一連續編碼視頻序列的參數,如標識符 seq_parameter_set_id、幀數及 POC 的約束、參考幀數目、解碼圖像尺寸和幀場編碼模式選擇標識等等。
?????? PPS對應的是一個序列中某一幅圖像或者某幾幅圖像,
?????? 其參數如標識符 pic_parameter_set_id、可選的 seq_parameter_set_id、熵編碼模式選擇標識、片組數目、初始量化參數和去方塊濾波系數調整標識等等。
?????? 數據分割:組成片的編碼數據存放在 3 個獨立的 DP(數據分割,A、B、C)中,各自包含一個編碼片的子集。
?????? 分割A包含片頭和片中每個宏塊頭數據。
?????? 分割B包含幀內和 SI 片宏塊的編碼殘差數據。
?????? 分割 C包含幀間宏塊的編碼殘差數據。
?????? 每個分割可放在獨立的 NAL 單元并獨立傳輸。
NAL的開始和結束
編碼器將每個NAL各自獨立、完整地放入一個分組,因為分組都有頭部,解碼器可以方便地檢測出NAL的分界,并依次取出NAL進行解碼。
每個NAL前有一個起始碼 0x00 00 01(或者0x00 00 00 01),解碼器檢測每個起始碼,作為一個NAL的起始標識,當檢測到下一個起始碼時,當前NAL結束。
同時H.264規定,當檢測到0x000000時,也可以表征當前NAL的結束。那么NAL中數據出現0x000001或0x000000時怎么辦?H.264引入了防止競爭機制,如果編碼器檢測到NAL數據存在0x000001或0x000000時,編碼器會在最后個字節前插入一個新的字節0x03,這樣:
0x000000->0x000003000x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解碼器檢測到0x000003時,把03拋棄,恢復原始數據(脫殼操作)。解碼器在解碼時,首先逐個字節讀取NAL的數據,統計NAL的長度,然后再開始解碼。
?NALU的順序要求
?H.264/AVC標準對送到解碼器的NAL單元順序是有嚴格要求的,如果NAL單元的順序是混亂的,必須將其重新依照規范組織后送入解碼器,否則解碼器不能夠正確解碼。
???????
1.序列參數集NAL單元???????
必須在傳送所有以此參數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重復的序列參數集NAL單元。
所謂重復的詳細解釋為:序列參數集NAL單元都有其專門的標識,如果兩個序列參數集NAL單元的標識相同,就可以認為后一個只不過是前一個的拷貝,而非新的序列參數集。??????
2.圖像參數集NAL單元??????
必須在所有以此參數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重復的圖像參數集NAL單元,這一點與上述的序列參數集NAL單元是相同的。
???????
3.不同基本編碼圖像中的片段(slice)單元和數據劃分片段(data partition)單元在順序上不可以相互交叉,即不允許屬于某一基本編碼圖像的一系列片段(slice)單元和數據劃分片段(data partition)單元中忽然出現另一個基本編碼圖像的片段(slice)單元片段和數據劃分片段(data partition)單元。
4.參考圖像的影響:如果一幅圖像以另一幅圖像為參考,則屬于前者的所有片段(slice)單元和數據劃分片段(data partition)單元必須在屬于后者的片段和數據劃分片段之后,無論是基本編碼圖像還是冗余編碼圖像都必須遵守這個規則。
5.基本編碼圖像的所有片段(slice)單元和數據劃分片段(data partition)單元必須在屬于相應冗余編碼圖像的片段(slice)單元和數據劃分片段(data partition)單元之前。
???????
6.如果數據流中出現了連續的無參考基本編碼圖像,則圖像序號小的在前面。
????????
7.如果arbitrary_slice_order_allowed_flag置為1,一個基本編碼圖像中的片段(slice)單元和數據劃分片段(data partition)單元的順序是任意的,如果arbitrary_slice_order_allowed_flag置為零,則要按照片段中第一個宏塊的位置來確定片段的順序,若使用數據劃分,則A類數據劃分片段在B類數據劃分片段之前,B類數據劃分片段在C類數據劃分片段之前,而且對應不同片段的數據劃分片段不能相互交叉,也不能與沒有數據劃分的片段相互交叉。
8.如果存在SEI(補充增強信息)單元的話,它必須在它所對應的基本編碼圖像的片段(slice)單元和數據劃分片段(data partition)單元之前,并同時必須緊接在上一個基本編碼圖像的所有片段(slice)單元和數據劃分片段(data partition)單元后邊。假如SEI屬于多個基本編碼圖像,其順序僅以第一個基本編碼圖像為參照。
9.如果存在圖像分割符的話,它必須在所有SEI 單元、基本編碼圖像的所有片段slice)單元和數據劃分片段(data partition)單元之前,并且緊接著上一個基本編碼圖像那些NAL單元。
????????
10.如果存在序列結束符,且序列結束符后還有圖像,則該圖像必須是IDR(即時解碼器刷新)圖像。序列結束符的位置應當在屬于這個IDR圖像的分割符、SEI 單元等數據之前,且緊接著前面那些圖像的NAL單元。如果序列結束符后沒有圖像了,那么它的就在比特流中所有圖像數據之后。
??????
11.流結束符在比特流中的最后。
h264有兩種封裝,
一種是annexb模式,傳統模式,有startcode,SPS和PPS是在ES中
一種是mp4模式,一般mp4 mkv會有,沒有startcode,SPS和PPS以及其它信息被封裝在container中,每一個frame前面是這個frame的長度
很多解碼器只支持annexb這種模式,因此需要將mp4做轉換:
在ffmpeg中用h264_mp4toannexb_filter可以做轉換
實現:
注冊filter
avcbsfc = av_bitstream_filter_init("h264_mp4toannexb");
轉換bitstream
av_bitstream_filter_filter(AVBitStreamFilterContext?*bsfc,
AVCodecContext *avctx, const char *args,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size, int keyframe)
總結
以上是生活随笔為你收集整理的H264学习_基本数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么查询自己违章处理 如何查询已处理过的
- 下一篇: 手机数据线与充电线的区别在哪里