相信在你的電腦裏,必定存有一些已經下載好的視頻文件,若是你硬說沒有,那我相信你曾經總有吧?曾經也沒有?那我想對你說曾經免費的時候你不下載,直到電影都收費才後悔那些年錯過下載的大片。html
好了,言歸正傳,在平常咱們必定見過不少後綴爲avi, mp4, rmvb, flv等格式的視頻文件。而不多有人真正挖掘這些文件究竟是什麼?其實以上格式都是封裝視頻的封裝格式。ios
什麼是封裝格式 ?git
把音頻數據和視頻數據打包成一個文件的規範。不用封裝格式差距不大, 各有千秋。github
從視頻播放器播放一個互聯網上的視頻文件算法
須要通過如下幾個步驟:解協議,解封裝,解碼視音頻,視音頻同步。若是播放本地文件則不須要解協議,其餘步驟相同。小程序
爲何要對視頻數據進行編碼bash
視頻編碼的主要做用是將視頻像素數據(RGB,YUV等)壓縮成視頻碼流,從而下降視頻的數據量。舉個例子:好比當前手機的屏幕分辨率是1280 * 720(即咱們平時在視頻軟件中可選的720P),假設一秒鐘30幀(即1秒鐘傳輸30張圖片),那麼一秒鐘的數據爲 1280 * 720(位像素)*30(張) / 8(1字節8位)(結果B)
,也就是一秒鐘的數據量爲3.456M數據量,一分鐘就是207.36M,那麼咱們日常看一部電影就是大約18G的流量,試想下若是是這樣對於存儲即網絡傳輸是件多麼恐怖的事情。網絡
正是由於以上緣由,咱們須要對視頻數據進行編碼,以最小程序減少清晰度與最大程序下降數據量,而H264正是目前普遍使用的一種編碼格式,下面咱們將主要介紹下H264的碼流結構。post
刷新圖像概念編碼
在咱們的印象中,一張圖片就是一張圖像,而在H264中圖像是個集合的概念。
幀、頂場、底場均可以稱爲圖像。一幀一般就是一幅完整的圖像。當採集視頻信號時,若是採用逐行掃描,則每次掃描獲得的信號就是一副圖像,也就是一幀。當採集視頻信號時,若是採用隔行掃描(奇、偶數行),則掃描下來的一幀圖像就被分爲了兩個部分,這每一部分就稱爲「場」,根據次序分爲:「頂場」和「底場」。「幀」和「場」的概念又帶來了不一樣的編碼方式:幀編碼、場編碼。逐行掃描適合於運動圖像,因此對於運動圖像採用幀編碼更好;隔行掃描適合於非運動圖像,因此對於非運動圖像採用場編碼更好。
H264原始碼流
StartCode : Start Code 用於標示這是一個NALU 單元的開始,必須是」00 00 00 01」 或」00 00 01」
NALU Header 下表爲 NAL Header Type
例如:
00 00 00 01 06: SEI信息
00 00 00 01 07: SPS
00 00 00 01 08: PPS
00 00 00 01 05: IDR Slice
複製代碼
什麼是切片(slice)?
片的主要做用是用做宏塊(Macroblock)的載體(ps:下面會介紹到宏塊的概念)。片之因此被創造出來,主要目的是爲限制誤碼的擴散和傳輸。
那麼片(slice)的具體結構,咱們用一張圖來直觀說明吧:
上圖結構中,咱們不難看出,每一個分片也包含着頭和數據兩部分:
什麼是宏塊?
宏塊是視頻信息的主要承載者,它包含着每個像素的亮度和色度信息。視頻解碼的主要工做則是提供高效的方式從碼流中得到宏塊中的像素陣列。
宏塊的組成:一個宏塊由一個16*16亮度像素和附加的一個8 * 8Cb和一個8 * 8Cr彩色像素塊組成。每一個圖像中,若干宏塊被排列成片的形式。
下面是宏塊的結構圖:
切片(slice)類型跟宏塊類型的關係
切片(slice)來說,分爲如下幾種類型:
P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.
I片:只包 I宏塊,I 宏塊利用從當前片中已解碼的像素做爲參考進行幀內預測(不能取其它片中的已解碼像素做爲參考進行幀內預測)。
P片:可包 P和I宏塊,P 宏塊利用前面已編碼圖象做爲參考圖象進行幀內預測,一個幀內編碼的宏塊可進一步做宏塊的分割:即 16×1六、16×八、8×16 或 8×8 亮度像素塊(以及附帶的彩色像素);若是選了 8×8 的子宏塊,則可再分紅各類子宏塊的分割,其尺寸爲 8×八、8×四、4×8 或 4×4 亮度像素塊(以及附帶的彩色像素)。
B片:可包 B和I宏塊,B 宏塊則利用雙向的參考圖象(當前和 來的已編碼圖象幀)進行幀內預測。
SP片(切換P):用於不一樣編碼流之間的切換,包含 P 和/或 I 宏塊
SI片:擴展檔次中必須具備的切換,它包含了一種特殊類型的編碼宏塊,叫作 SI 宏塊,SI 也是擴展檔次中的必備功能。
H.264的碼流結構並無那麼複雜,編碼後視頻的每一組圖像(GOP,圖片組)都給予了傳輸的序列(PPS)和自己這個幀的圖像參數(SPS),因此,總體給夠以下
GOP 圖像組主要形容一個I幀到下一個I幀之間間隔了多少幀,增大圖片組能有效的減小編碼後視頻的體積,可是也會下降視頻質量,至於怎麼取捨,得看需求。
enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分區、非 IDR 圖像的片(片的頭信息和數據)
NAL_SLICE_DPA = 2, // 片分區 A
NAL_SLICE_DPB = 3, // 片分區 B
NAL_SLICE_DPC = 4, // 片分區 C
NAL_SLICE_IDR = 5, / ref_idc != 0 / // IDR 圖像中的片
NAL_SEI = 6, / ref_idc == 0 / // 補充加強信息單元
-
參數集是 H.264 標準的一個新概念,是一種經過改進視頻碼流結構加強錯誤恢復能力的方法。
NAL_SPS = 7, // 序列參數集 (包括一個圖像序列的全部信息,即兩個 IDR 圖像間的全部圖像信息,如圖像尺寸、視頻格式等)
NAL_PPS = 8, // 圖像參數集 (包括一個圖像的全部分片的全部相關信息, 包括圖像類型、序列號等,解碼時某些序列號的丟失可用來檢驗信息包的丟失與否)
-
NAL_AUD = 9, // 分界符
NAL_FILLER = 12, // 填充(啞元數據,用於填充字節)
/ ref_idc == 0 for 6,9, 10 (代表下一圖像爲 IDR 圖像),11(代表該流中已沒有圖像),12 /
};
ps: 以上括號()中的爲類型描述
複製代碼
I幀 | P幀 | B幀 |
---|---|---|
幀內編碼幀 | 前向預測編碼幀 | 雙向預測編碼幀 |
I幀一般是每一個GOP的第一幀,通過適度壓縮,做爲隨機訪問的參考點,可當作一個圖片通過壓縮後的產物 | 經過充分低於圖像序列中前面已編碼幀的時間冗餘信息來壓縮傳輸數據編碼圖像,也叫預測幀 | 既考慮與源圖像序列前面已編碼幀,也顧及源圖像序列後面已編碼幀之間的時間冗餘信息來壓縮傳輸數據量的編碼圖像,也叫雙向預測幀 |
GOP是畫面組,一個GOP是一組連續的畫面。 GOP通常有兩個數字,如M=3,N=12.M制定I幀與P幀之間的距離,N指定兩個I幀之間的距離。那麼如今的GOP結構是 I BBP BBP BBP BB I
一個序列的第一個圖像叫作 IDR 圖像(當即刷新圖像),IDR 圖像都是 I 幀圖像。
I和IDR幀都使用幀內預測。I幀不用參考任何幀,可是以後的P幀和B幀是有可能參考這個I幀以前的幀的。IDR就不容許這樣。
咱們能夠經過第 一、二、三、四、5 塊的編碼來推測和計算第 6 塊的編碼,所以就不須要對第 6 塊進行編碼了,從而壓縮了第 6 塊,節省了空間
能夠看到先後兩幀的差別實際上是很小的,這時候用幀間壓縮就頗有意義。 這裏涉及到幾個重要的概念:塊匹配,殘差,運動搜索(運動估計),運動補償. 幀間壓縮最經常使用的方式就是塊匹配(Block Matching)。找找看前面已經編碼的幾幀裏面,和我當前這個塊最相似的一個塊,這樣我不用編碼當前塊的內容了,只須要編碼當前塊和我找到的快的差別(殘差)便可。找最想的塊的過程叫運動搜索(Motion Search),又叫運動估計。用殘差和原來的塊就能推算出當前塊的過程叫運動補償(Motion Compensation).