生活随笔
收集整理的這篇文章主要介紹了
H.264基础知识及视频码流解析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
H.264基礎知識及視頻碼流解析
目錄
H.264概述 H264相關概念 H264壓縮方式 H264分層結構 H264碼流結構 H264的NAL單元 H.264視頻碼流解析及代碼實現
1. H.264概述
編碼是為了將數據進行壓縮,這樣在傳輸的過程中就不會使資源被浪費。 用一個簡單的例子來說明編碼的必要性:當你此刻顯示器正在播放一個視頻,分辨率是1280*720,幀率是25,那么一秒所產生正常的數據大小為:1280*720(位像素)*25(張) / 8(1字節8位)(結果:B) / 1024(結果:KB) / 1024 (結果:MB) = 2.75MB。顯然一秒這么大的數據你是無法接受的,需要將數據進行壓縮。 H264在視頻采集到輸出中屬于編解碼層次的數據,如下圖所示,是在采集數據后做編碼壓縮時通過編碼標準編碼后所呈現的數據。 對于視頻?件來說,視頻由單張圖?幀所組成,?如每秒25幀,但是圖?幀的像素塊之間存在相似性,因此視頻幀圖像可以進?圖像壓縮;H264采?了16*16的分塊??對,視頻幀圖像進?相似?較和壓縮編碼。如下圖所示:
2. H264相關概念
1. 序列
H264編碼標準中所遵循的理論依據個人理解成:參照一段時間內相鄰的圖像中,像素、亮度與色溫的差別很小。所以當面對一段時間內圖像我們沒必要去對每一幅圖像進行完整一幀的編碼,而是可以選取這段時間的第一幀圖像作為完整編碼,而下一幅圖像可以記錄與第一幀完整編碼圖像像素、亮度與色溫等的差別即可,以此類推循環下去。 什么叫序列呢?上述的這段時間內圖像變化不大的圖像集我們就可以稱之為一個序列。序列可以理解為有相同特點的一段數據。但是如果某個圖像與之前的圖像變換很大,很難參考之前的幀來生成新的幀,那么就結束刪一個序列,開始下一段序列。重復上一序列的做法,生成新的一段序列。
2. 幀類型
H264結構中,一個視頻圖像編碼后的數據叫做一幀,一幀由一個片(slice)或多個片組成,一個片由一個或多個宏塊(MB)組成,一個宏塊由16x16的yuv數據組成。宏塊作為H264編碼的基本單位。 H26使?幀內壓縮和幀間壓縮的?式提?編碼壓縮率; H264采?了獨特的I幀、P幀和B幀策略來實現,連續幀之間的壓縮;
1. I幀
I幀:幀內編碼幀 ,I幀表示關鍵幀,你可以理解為這一幀畫面的完整保留;解碼時只需要本幀數據就可以完成(因為包含完整畫面) I幀特點: 它是一個全幀壓縮編碼幀。它將全幀圖像信息進行JPEG壓縮編碼及傳輸; 解碼時僅用I幀的數據就可重構完整圖像; I幀描述了圖像背景和運動主體的詳情; I幀不需要參考其他畫面而生成; I幀是P幀和B幀的參考幀(其質量直接影響到同組中以后各幀的質量); I幀是幀組GOP的基礎幀(第一幀),在一組中只有一個I幀; I幀不需要考慮運動矢量; I幀所占數據的信息量比較大。
2. P幀
P幀:前向預測編碼幀。P幀表示的是這一幀跟之前的一個關鍵幀(或P幀)的差別,解碼時需要用之前緩存的畫面疊加上本幀定義的差別,生成最終畫面。(也就是差別幀,P幀沒有完整畫面數據,只有與前一幀的畫面差別的數據) P幀的預測與重構:P幀是以I幀為參考幀,在I幀中找出P幀“某點”的預測值和運動矢量,取預測差值和運動矢量一起傳送。在接收端根據運動矢量從I幀中找出P幀“某點”的預測值并與差值相加以得到P幀“某點”樣值,從而可得到完整的P幀。 P幀特點: P幀是I幀后面相隔1~2幀的編碼幀; P幀采用運動補償的方法傳送它與前面的I或P幀的差值及運動矢量(預測誤差); 解碼時必須將I幀中的預測值與預測誤差求和后才能重構完整的P幀圖像; P幀屬于前向預測的幀間編碼。它只參考前面最靠近它的I幀或P幀; P幀可以是其后面P幀的參考幀,也可以是其前后的B幀的參考幀; 由于P幀是參考幀,它可能造成解碼錯誤的擴散; 由于是差值傳送,P幀的壓縮比較高。
3. B幀
B幀:雙向預測內插編碼幀。B幀是雙向差別幀,也就是B幀記錄的是本幀與前后幀的差別(具體比較復雜,有4種情況),換言之,要解碼B幀,不僅要取得之前的緩存畫面,還要解碼之后的畫面,通過前后畫面的與本幀數據的疊加取得最終的畫面。B幀壓縮率高,但是解碼時CPU會比較累。 B幀以前面的I或P幀和后面的P幀為參考幀,“找出”B幀“某點”的預測值和兩個運動矢量,并取預測差值和運動矢量傳送。接收端根據運動矢量在兩個參考幀中“找出(算出)”預測值并與差值求和,得到B幀“某點”樣值,從而可得到完整的B幀。 B幀特點 B幀是由前面的I或P幀和后面的P幀來進行預測的; B幀傳送的是它與前面的I或P幀和后面的P幀之間的預測誤差及運動矢量; B幀是雙向預測編碼幀; B幀壓縮比最高,因為它只反映兩個參考幀間運動主體的變化情況,預測比較準確; B幀不是參考幀,不會造成解碼錯誤的擴散。
3. GOP(畫面組)
GOP即Group of picture(圖像組),指兩個I幀之間的距離(下圖所說的視頻序列就是GOP),Reference(參考周期)指兩個P幀之間的距離,可以理解為跟序列差不多意思,就是一段時間內變化不大的圖像集,比較說GOP為120,如果是720 p60 的話,那就是2s一次I幀。一個I幀所占用的字節數大于一個P幀,一個P幀所占用的字節數大于一個B幀。所以在碼率不變的前提下,GOP值越大,P、B幀的數量會越多,平均每個I、P、B幀所占用的字節數就越多,也就更容易獲取較好的圖像質量;Reference越大,B幀的數量越多,同理也更容易獲得較好的圖像質量。 GOP結構一般有兩個數字,如M=3,N=12。M指定I幀和P幀之間的距離,N指定兩個I幀之間的距離。上面的M=3,N=12,GOP結構為:IBBPBBPBBPBBI。在一個GOP內I frame解碼不依賴任何的其它幀,p frame解碼則依賴前面的I frame或P frame,B frame解碼依賴前最近的一個I frame或P frame 及其后最近的一個P frame。
4. IDR幀(關鍵幀)
IDR(Instantaneous Decoding Refresh)即時解碼刷新。 在編碼解碼中為了方便,將GOP中首個I幀要和其他I幀區別開,把第一個I幀叫IDR,這樣方便控制編碼和解碼流程,所以IDR幀一定是I幀,但I幀不一定是IDR幀;IDR幀的作用是立刻刷新,使錯誤不致傳播,從IDR幀開始算新的序列開始編碼。I幀有被跨幀參考的可能,IDR不會。 I幀不用參考任何幀,但是之后的P幀和B幀是有可能參考這個I幀之前的幀的。IDR就不允許這樣,例如: 其核?作?是,是為了解碼的重同步,當解碼器解碼到 IDR 圖像時,?即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數集,開始?個新的序列。這樣,如果前?個序列出現重?錯誤,在這?可以獲得重新同步的機會。IDR圖像之后的圖像永遠不會使?IDR之前的圖像的數據來解碼。
3. H264壓縮方式
H264采用的核心算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。 幀內(Intraframe)壓縮也稱為空間壓縮(Spatialcompression) 。當壓縮一幀圖像時,僅考慮本幀的數據而不考慮相鄰幀之間的冗余信息,這實際上與靜態圖像壓縮類似。幀內一般采用有損壓縮算法,由于幀內壓縮是編碼一個完整的圖像,所以可以獨立的解碼、顯示。幀內壓縮一般達不到很高的壓縮,跟編碼jpeg差不多。幀間(Interframe)壓縮 的原理是:相鄰幾幀的數據有很大的相關性,或者說前后兩幀信息變化很小的特點。也即連續的視頻其相鄰幀之間具有冗余信息,根據這一特性,壓縮相鄰幀之間的冗余量就可以進一步提高壓縮量,減小壓縮比。幀間壓縮也稱為時間壓縮(Temporalcompression),它通過比較時間軸上不同幀之間的數據進行壓縮。幀間壓縮一般是無損的。幀差值(Framedifferencing)算法是一種典型的時間壓縮法,它通過比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少數據量。
1. 壓縮方式說明
分組,也就是將一系列變換不大的圖像歸為一個組,也就是一個序列,也可以叫GOP(畫面組); 定義幀,將每組的圖像幀歸分為I幀、P幀和B幀三種類型; 預測幀, 以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀; 數據傳輸, 最后將I幀數據與預測的差值信息進行存儲和傳輸。
4. H264分層結構
H264的主要目標是為了有高的視頻壓縮比和良好的網絡親和性,為了達成這兩個目標,H264的解決方案是將系統框架分為兩個層面,分別是視頻編碼層面(VCL:Video Coding Layer)和網絡抽象層面(NAL:Network Coding Layer),H.264原始碼流(裸流)是由?個接?個NALU組成,如下圖: VLC層是對核心算法引擎、塊、宏塊及片的語法級別的定義,負責有效表示視頻數據的內容,最終輸出編碼完的數據SODB; NAL層定義了片級以上的語法級別(如序列參數集和圖像參數集,針對網絡傳輸,后面會描述到),負責以網絡所要求的恰當方式去格式化數據并提供頭信息,以保證數據適合各種信道和存儲介質上的傳輸 。NAL層將SODB打包成RBSP然后加上NAL頭組成一個NALU單元,具體NAL單元的組成也會在后面詳細描述。 在VCL進?數據傳輸或存儲之前,這些編碼的VCL數據,被映射或封裝進NAL單元。(NALU) ?個NALU = ?組對應于視頻編碼的NALU頭部信息 + ?個原始字節序列負荷(RBSP,Raw Byte Sequence Payload). NALU結構單元的主體結構如下所示;?個原始的H.264 NALU單元通常由[StartCode] [NALU Header] [NALU Payload]三部分組成,其中 Start Code ?于標示這是?個NALU 單元的開始,必須是"00 00 00 01" 或"00 00 01",除此之外基本相當于?個NAL header + RBSP; SODB與RBSP的關聯,具體結構如圖3所示: 1. SODB(String Of Data Bits): 數據比特串,是編碼后的原始數據; 2. RBSP(Raw Byte Sequence Payload): 原始字節序列載荷,即在SODB的后面添加了trailing bits,即一個bit 1和若干個bit 0,以便字節對齊; RBSP的形成過程 如果SODB的內容是空的,那么RBSP的內容也是空的。否則,RBSP的第一個字節取自SODB的第1到第8個比特,RBSP字節內部按照從左到右從高到低的順序排列。以此類推,RBSP中的每個字節都直接取自SODP的相應比特。RBSP的最后一個字節包含SODB的最后幾個比特,以及trailing bits。其中,trailing bits的第一個比特為1,其余的比特為0,保證字節對齊。所以RBSP就等于,SODB在它的最后一個字節的最后一個比特后,緊跟值為1的1個比特,然后增加若干比特的0,以補齊這個字節。
5. H264碼流結構
具體講述NAL單元前,十分有必要先了解一下H264的碼流結構。在經過編碼后的H264的碼流如下圖所示,從圖中我們需要得到一個概念,H264碼流是由一個個的NAL單元組成,其中SPS、PPS、IDR和SLICE是NAL單元某一類型的數據。
6. H264的NAL單元
1. H264的NAL結構
在實際的網絡數據傳輸過程中H264的數據結構是以NALU(NAL單元)進行傳輸的,傳輸數據結構組成為[NALU Header]+[RBSP],如下圖所示: 從之前的分析我們可以知道,VCL層編碼后的視頻幀數據,幀有可能是I/B/P幀,這些幀也可能是屬于不同的序列之中;同一序列也還有相應的序列參數集與圖片參數集;綜上所述,想要完成準確無誤視頻的解碼,除了需要VCL層編碼出來的視頻幀數據,同時還需要傳輸序列參數集和圖像參數集等等,所以RBSP不單純只保存I/B/P幀的數據編碼信息,還有其他信息也可能出現在里面。 上面知道NAL單元是作為實際視頻數據傳輸的基本單元,NALU頭是用來標識后面RBSP是什么類型的數據,同時記錄RBSP數據是否會被其他幀參考以及網絡傳輸是否有錯誤,所以針對NAL頭和RBSP的作用以及結構與所承載的數據需要做個簡單的了解。
2. NAL頭
1. NAL頭的組成
NAL單元的頭部是由forbidden_bit(1bit),nal_reference_bit(2bits)(優先級),nal_unit_type(5bits)(類型)三個部分組成的,組成下圖所示:
F(forbiden):禁止位,占用NAL頭的第一個位,當禁止位值為1時表示語法錯誤; NRI:取00~11,似乎指示這個NALU的重要性,如00的NALU解碼器可以丟棄它?不影響圖像的回放,0~3,取值越?,表示當前NAL越重要,需要優先受到保護。如果當前NAL是屬于參考幀的?,或是序列參數集,或是圖像參數集這些重要的單位時,本句法元素必需?于0。 Type:NAL單元數據類型,也就是標識該NAL單元的數據類型是哪種,占用NAL頭的第四到第八個位;
2. NAL單元數據類型
NAL類型主要就是下面圖中這些類型每個類型都有特殊的作用; 例子:0x00 00 00 01 67 ,67:?進制:0110 0111,00111 = 7(?進制),即為序列參數集
4. 在具體介紹NAL數據類型前,有必要知道NAL分為VCL的NAL單元和非VCL的NAL單元 。 5. nal_unit_type為1, 2, 3, 4, 5及12的NAL單元稱為VCL的NAL單元,其他類型的NAL單元為非VCL的NAL單元。 6. 另外一個需要了解的概念就是參數集(Parameter sets) ,參數集是攜帶解碼參數的NAL單元,參數集對于正確解碼是非常重要的,在一個有損耗的傳輸場景中,傳輸過程中比特列或包可能丟失或損壞,在這種網絡環境下,參數集可以通過高質量的服務來發送,比如向前糾錯機制或優先級機制。Parameter sets與其之外的句法元素之間的關系如下圖所示: 7. 每種類型都有代表一種數據類型,比較重要的以下幾種做個簡單的介紹:
1. 非VCL的NAL數據類型:
SPS(Sequence Parameter Set:序列參數集)包含一些通用的參數,比如Profile和Level,比如視頻幀的尺寸,參考幀的最大數量等,這些參數對整個Video Sequence或者Programme都是通用的。 PPS(Picture Parameter Set:圖像參數集)包含一些通用的參數,比如熵編碼類型,有效的參考圖像的數目和初始化參數等,這些參數可以應用到一個Video Sequence或者一部分編碼幀。 SEI(補充增強信息):這部分參數可作為H264的比特流數據而被傳輸,每一個SEI信息被封裝成一個NAL單元。SEI對于解碼器來說可能是有用的,但是對于基本的解碼過程來說,并不是必須的。 一個Parameter Set在開始的時候是不活躍的,直到被激活。一個PPS被預先傳到解碼器,當在一個Slice Header中涉及到的時候,就會被激活,而且直到一個不同的PPS被激活。對于SPS,當一個PPS涉及到它的時候,就會被激活。對于一個以IDR Access Unit開始的Coded Video Sequence,在整個過程中,一個SPS會一直處于活躍狀態。因此,一個SPS可以有效的被IDR Slice激活。
2. VCL的NAL數據類型
頭信息塊,包括宏塊類型,量化參數,運動矢量。這些信息是最重要的,因為離開他們,被的數據塊種的碼元都無法使用。該數據分塊稱為A類數據分塊。 幀內編碼信息數據塊,稱為B類數據分塊。它包含幀內編碼宏塊類型,幀內編碼系數。對應的slice來說,B類數據分塊的可用性依賴于A類數據分塊。和幀間編碼信息數據塊不同的是,幀內編碼信息能防止進一步的偏差,因此比幀間編碼信息更重要。 幀間編碼信息數據塊,稱為C類數據分塊。它包含幀間編碼宏塊類型,幀間編碼系數。它通常是slice種最大的一部分。幀間編碼信息數據塊是不重要的一部分。它所包含的信息并不提供編解碼器之間的同步。C類數據分塊的可用性也依賴于A類數據分塊,但與B類數據分塊無關。 以上三種數據塊每種分割被單獨的存放在一個NAL單元中,因此可以被單獨傳輸。
3. H264的NAL單元與片,宏之間的聯系
為什么數據NAL單元中有這么多數據類型,這個SLICE又是什么東西,為什么不直接是編碼后出來的原始字節序列載荷,所以我覺得在這里再講述幀所細分的一些片和宏的概念應該是比較合適的,也是能夠參照上下文更能理解這些概念的位置,又能給這些困惑做一個合理一點的解釋,所以在此做一個描述: 1幀(一幅圖像) = 1~N個片(slice) //也可以說1到多個片為一個片組 1個片 = 1~N個宏塊(Marcroblock) 1個宏塊 = 16*16的YUV數據(原始視頻采集數據) 從數據層次角度來說,一幅原始的圖片可以算作廣義上的一幀,幀包含片組和片,片組由片來組成,片由宏塊來組成,每個宏塊可以是44、88、16*16像素規模的大小,它們之間的聯系如下圖所示。每個片都是一個獨立的編碼單位。 從容納數據角度來說,NAL單元除了容納Slice編碼的碼流外,還可以容納其他數據,這也就是為什么有SPS、PPS等這些數據出現的原因,并且這些數據在傳輸H264碼流的過程中起到不可或缺的作用,具體作用上面也是有講到的。 那么也就可以對下面這些概念做一個大小的排序了:序列>圖像>片>宏>像素(當然還有片組、亞宏塊等等這些概念,初步了解就不了解這么深了,后面再慢慢研究) 同時有幾點需要說明一下,這樣能便于理解NAL單元: 如果不采用 FMO(靈活宏塊排序) 機制,則一幅圖像只有一個片組; 如果不使用多個片,則一個片組只有一個片; 如果不采用 DP(數據分割)機制,則一個片就是一個 NALU,一個 NALU 也就是一個片。 否則,一個片的組成需要由 三個 NALU 組成,也就是上面說到的A、B、C類數據塊。 這時候在看下面這幅碼流數據分層圖就比較能理解整體的碼流結構組成了。 如我們所見,每個分片也包含著頭和數據兩部分,分片頭中包含著分片類型、分片中的宏塊類型、分片幀的數量以及對應的幀的設置和參數等信息,而分片數據中則是宏塊,這里就是我們要找的存儲像素數據的地方;宏塊是視頻信息的主要承載者,因為它包含著每一個像素的亮度和色度信息。視頻解碼最主要的工作則是提供高效的方式從碼流中獲得宏塊中的像素陣列。宏塊數據的組成如下圖所示: 從上圖中,可以看到,宏塊中包含了宏塊類型、預測類型、Coded Block Pattern、Quantization Parameter、像素的亮度和色度數據集等等信息。 至此,我們對 H.264 的碼流數據結構應該有了一個大致的了解。
需要注意的幾點:
H.264/AVC標準對送到解碼器的NAL單元順序是有嚴格要求的,如果NAL單元的順序是混亂的,必須將其重新依照規范組織后送入解碼器,否則解碼器不能夠正確解碼。 序列參數集NAL單元必須在傳送所有以此參數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重復的序列參數集NAL單元。所謂重復的詳細解釋為:序列參數集NAL單元都有其專門的標識,如果兩個序列參數集NAL單元的標識相同,就可以認為后一個只不過是前一個的拷貝,而非新的序列參數集。 圖像參數集NAL單元必須在所有以此參數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重復的圖像參數集NAL單元,這一點與上述的序列參數集NAL單元是相同。
7. H.264視頻碼流解析及代碼實現
視音頻數據處理入門:H.264視頻碼流解析 H.264原始碼流(又稱為“裸流”)是由一個一個的NALU組成的。他們的結構如下圖所示。
其中每個NALU之間通過startcode(起始碼)進行分隔,起始碼分成兩種:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU對應的Slice為一幀的開始就用0x00000001,否則就用0x000001。 H.264碼流解析的步驟就是首先從碼流中搜索0x000001和0x00000001,分離出NALU;然后再分析NALU的各個字段。本文的程序即實現了上述的兩個步驟。
1. H264 annexb模式
H264有兩種封裝 ?種是annexb模式,傳統模式,有startcode,SPS和PPS是在ES中 ?種是mp4模式,?般mp4 mkv都是mp4模式,沒有startcode,SPS和PPS以及其它信息被封裝在container中,每?個frame前?4個字節是這個frame的?度很多解碼器只?持annexb這種模式,因此需要將mp4做轉換: 在ffmpeg中?h264_mp4toannexb_filter可以做轉換 實現
const AVBitStreamFilter
* bsfilter
= av_bsf_get_by_name ( "h264_mp4toannexb" ) ; AVBSFContext
* bsf_ctx
= NULL
; av_bsf_alloc ( bsfilter
, & bsf_ctx
) ; avcodec_parameters_copy ( bsf_ctx
- > par_in
, ifmt_ctx
- > streams
[ videoindex
] - > codecpar
) ; av_bsf_init ( bsf_ctx
)
2. 解析代碼實現
#include
< stdio
. h
>
#include
< libavutil
/ log
. h
>
#include
< libavformat
/ avio
. h
>
#include
< libavformat
/ avformat
. h
> static char err_buf
[ 128 ] = { 0 } ; static char
* av_get_err ( int errnum
) { av_strerror ( errnum
, err_buf
, 128 ) ; return err_buf
;
}
int main ( int argc
, char
* * argv
) { AVFormatContext
* ifmt_ctx
= NULL
; int videoindex
= - 1 ; AVPacket
* pkt
= NULL
; int ret
= - 1 ; int file_end
= 0 ; FILE
* outfp
= fopen ( "/Users/lijinwang/Downloads/course/study/believe.h264" , "wb" ) ; ifmt_ctx
= avformat_alloc_context ( ) ; if ( ! ifmt_ctx
) { printf ( "[error] Could not allocate context.\n" ) ; return - 1 ; } ret
= avformat_open_input ( & ifmt_ctx
, "/Users/lijinwang/Downloads/course/study/believe.mp4" , NULL
, NULL
) ; if ( ret
!= 0 ) { printf ( "[error]avformat_open_input: %s\n" , av_get_err ( ret
) ) ; return - 1 ; } ret
= avformat_find_stream_info ( ifmt_ctx
, NULL
) ; if ( ret
< 0 ) { printf ( "[error]avformat_find_stream_info: %s\n" , av_get_err ( ret
) ) ; avformat_close_input ( & ifmt_ctx
) ; return - 1 ; } videoindex
= - 1 ; videoindex
= av_find_best_stream ( ifmt_ctx
, AVMEDIA_TYPE_VIDEO
, - 1 , - 1 , NULL
, 0 ) ; if ( videoindex
== - 1 ) { printf ( "Didn't find a video stream.\n" ) ; avformat_close_input ( & ifmt_ctx
) ; return - 1 ; } pkt
= av_packet_alloc ( ) ; av_init_packet ( pkt
) ; const AVBitStreamFilter
* bsfilter
= av_bsf_get_by_name ( "h264_mp4toannexb" ) ; AVBSFContext
* bsf_ctx
= NULL
; av_bsf_alloc ( bsfilter
, & bsf_ctx
) ; avcodec_parameters_copy ( bsf_ctx
- > par_in
, ifmt_ctx
- > streams
[ videoindex
] - > codecpar
) ; av_bsf_init ( bsf_ctx
) ; file_end
= 0 ; while
( 0 == file_end
) { if ( ( ret
= av_read_frame ( ifmt_ctx
, pkt
) ) < 0 ) { file_end
= 1 ; printf ( "read file end: ret:%d\n" , ret
) ; } if ( ret
== 0 && pkt
- > stream_index
== videoindex
) {
#
if 1 int input_size
= pkt
- > size
; int out_pkt_count
= 0 ; if ( av_bsf_send_packet ( bsf_ctx
, pkt
) != 0 ) { av_packet_unref ( pkt
) ; continue ; } av_packet_unref ( pkt
) ; while
( av_bsf_receive_packet ( bsf_ctx
, pkt
) == 0 ) { out_pkt_count
++ ; size_t size
= fwrite ( pkt
- > data
, 1 , pkt
- > size
, outfp
) ; if ( size
!= pkt
- > size
) { printf ( "fwrite failed-> write:%u, pkt_size:%u\n" , size
, pkt
- > size
) ; } av_packet_unref ( pkt
) ; } if ( out_pkt_count
>= 2 ) { printf ( "cur pkt(size:%d) only get 1 out pkt, it get %d pkts\n" , input_size
, out_pkt_count
) ; }
#
else size_t size
= fwrite ( pkt
- > data
, 1 , pkt
- > size
, outfp
) ; if ( size
!= pkt
- > size
) { printf ( "fwrite failed-> write:%u, pkt_size:%u\n" , size
, pkt
- > size
) ; } av_packet_unref ( pkt
) ;
#endif
} else { if ( ret
== 0 ) av_packet_unref ( pkt
) ; } } if ( outfp
) fclose ( outfp
) ; if ( bsf_ctx
) av_bsf_free ( & bsf_ctx
) ; if ( pkt
) av_packet_free ( & pkt
) ; if ( ifmt_ctx
) avformat_close_input ( & ifmt_ctx
) ; printf ( "finish\n" ) ; return 0 ;
}
參考博客:入門理解H264編碼 視音頻數據處理入門:H.264視頻碼流解析
總結
以上是生活随笔 為你收集整理的H.264基础知识及视频码流解析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。