標籤:
隨著無線網路和智能手機的發展,智能手機與人們日常生活聯絡越來越緊密,娛樂、商務應用、金融應用、交通出行各種功能的軟體大批湧現,使得人們的生活豐富多彩、快捷便利,也讓它成為人們生活中不可取代的一部分。其中,多媒體由於其直觀性和即時性,應用範圍越來越廣,視頻的解碼與播放也就成為研究的熱點。H.264標準技術日漸成熟,採用了統一的VLC符號編碼,高精度、多模式的位移估計,基於4×4塊的整數變換、分層的編碼文法等。這些措施使得H.264演算法具有很高的編碼效率,在相同的重建映像品質下,能夠比H.263節約50%左右的碼率。而且H.264的碼流結構網路適應性強,增加了差錯恢複能力。正好適用於頻寬受限,差錯率高的無線網路。本文結合ffmpeg開原始碼中的解碼方法,採用多線程接收資料包,多級緩衝資料,接收和解碼並行雙線程操作等方法,緩解了由於傳輸的資料量大、速度快而導致的資料堵塞、解碼出錯、視頻畫面遲鈍、延遲等問題。使得h.264視頻的傳輸速度快,穩定性好。最終實現了pc端到android手機端的視頻傳輸,以及在android手機端的解碼播放。該技術可以應用於視頻會議、視頻監控等應用中。
一、 H.264視頻傳輸播放系統的總體結構H.264視頻傳輸播放系統分為伺服器端和用戶端2個部分,伺服器端負責讀取H.264的視頻資料,並且以RTP/RTCP格式打包發送給用戶端,並且接受用戶端的反饋,對傳輸速度等作相應的控制。Android手機用戶端主要完成從伺服器端接收即時碼流資料,經過緩衝,進行視頻資料解析,然後送去解碼,最後在手機上顯示播放。伺服器端採用c語言實現,用戶端主要用java語言實現。
二、關鍵技術及其實現1.基於RTP協議的打包及解包(1)單個NAL打包H.264NALU單元常由[start code][NALU header][NALU payload]三部分組成,其中start code 用於標誌一個NALU單元的開始,必須是“00000001”或者是“000001”,打包時去掉開始碼,把其他資料打包到RTP包就可以了。(2)分區打包由於1500個位元組是IP資料報的長度的上限,去除20個位元組的資料報首部,1480位元組是用來存放UDP資料報的。所以當一幀中的位元組數超過這個數值時,我們必須將其分區打包。而且UDP在傳輸的過程中也要由包頭開銷,所以將RTP包的最大位元組數定位1400位元組。需要分區的包格式有所區別,首先說明下分區的格式:FU指示位元組有以下格式:+---------------+|0|1|2|3|4|5|6|7|+-+-+-+-+-+-+-+-+|F|NRI| Type |+---------------+FU指示位元組的類型28,29表示FU-A和FU-B。NRI域的值必鬚根據要分區的NAL單元NRI的值設定。FU頭的格式如下:+---------------+|0|1|2|3|4|5|6|7|+-+-+-+-+-+-+-+-+|S|E|R| Type |+---------------+S:開始位(1bit),當設定為1,開始位指示分區NAL單元的開始。第一個分區包設為1,其他的分區設定為0。E:結束位(1bit),當設定為1,結束位指示分區NAL單元的結束,即,FU荷載是最後分區時設定為1,其他時候設定為0。R:保留位(1bit),必須設定為0。Type:5bit(3)打包和解包的流程分析:打包:
分區時詳細說明:①第一個FU-A包的FU indicator 是這麼設定的:F=NALU頭中的F,NRI=NALU頭中的NRI,Type=28 FU header: S=1,E=0,R=0,Type=NALU頭中的Type;②中間的FU-A包的FU indicator是這麼設定的:F=NALU頭中的F,NRI=NALU頭中的NRI,Type=28 FU header: S=0,E=0,R=0,Type=NALU頭中的Type;③尾FU-A包的FU indicator是這麼設定的:F=NALU頭中的F,NRI=NALU頭中的NRI,Type=28 FU header: S=0,E=1,R=0,Type=NALU頭中的Type。 解包:下面我們針對RTP解包時對待分區進行分類的代碼實現做分析:byte startBit=(byte)(recbuf[13]&0x80); byte endBit=(byte)(recbuf[13]&0x40);①如果,startBit==-128,這包是分區的首包。NalBuf[4]=(byte) ((recbuf[12]&0xE0)+(recbuf[13]&0x1F)); 這句用於重建組合NAL單元類型②如果(startBit==0)&&(endBit==0),這包是分區的中間部分。③如果 endBit==64 ,這包是分區尾部。當分類清楚,就可以對各部分做相應的處理,中分析的那樣。2.碼流管理機制(1) 碼流的接收。在發送端碼流發送很快的情況下,由於接收端不僅要接收碼流,還要進行分析,解碼,這個處理需要一個較長的過程,如果接收端順序執行這個過程的話,會導致無法完整接收發送端的包、出現丟包,由此而帶來的是解碼錯誤、無法正常播放視頻、甚至程式奔潰等嚴重錯誤。針對這個問題我們採取並發的處理機制予以解決。線程並發存在的一個意義就是為了提高運行在單一處理器上的速度。在java中我們採用java.util.concurrent包中的執行器(Executor)來管理線程Thread對象。我們建立20個線程,也就是向SingleThreadExecutor提交了20個任務,這些任務將排好隊,每個任務會在下一個任務開始之前運行結束,每個任務都是按照他們被提交的順序,在下一個任務開始之前完成。這樣不僅實現了快速的接收而且還保證了接收到的包順序是正確的。通過這樣的處理後,接收和分析解碼可以被分成兩個部分,我們可以把接收到的資料暫時存放在緩衝區,然後就可以接著去接收下一包資料,不用等著分析、解碼完成後才去接收下一包資料。這樣做大大提高了接收效率,同時避免了丟包問題。(2) 視頻資料解析和解碼。由於採用了並發的機制,接收到的資料不止一包,所以對接收到的資料應該做怎樣合理的處理,成為我們接下來的痛點。我們需要保證的仍然是資料包的順序,還且每次只能處理一包,這裡涉及到一個線程之間的協作問題。我們採用消費者生產者這種線程協作模式來做處理。我們將從存放資料的緩衝區中按順序取到的包經過分析後放入另外一個緩衝區,通知解碼程式可以進行從此緩衝區中獲得資料解碼,然後分析視頻資料的程式進入等待。解碼完成後,通知分析視頻資料的程式繼續進行視頻資料分析,同時解碼程式又進入等待。兩個程式在執行和等待中交替進行。
(3) 多級緩衝機制。上面我們也提到了幾個緩衝,總結如下。①接收後存放資料的緩衝,由於伺服器端源源不斷的即時碼流,和採用了並發機制後帶來更大量的資料,我們不可能馬上處理完,所以必須設定一個緩衝區。②接收端和處理端之間的緩衝,由於網路不穩定,接收到的資料可能會有時快有時慢,這直接會造成解碼的不穩定和視頻播放的不連續,所以在此設定一個緩衝,起到一個平滑,過渡的作用,這個緩衝區既要存放接收到大量的碼流還要為視頻資料分析提供資料,有個寫讀入和讀出的過程,所以我們使用先入先出的隊列Queue容器來做緩衝區。③解析和解碼過程之間的緩衝,由於在此過程中的資料量相較而言不是很大,而這個擷取資料的速度直接影響瞭解碼的速度,所以我們要用一個高效的緩衝區來擔當此時的緩衝作用,由於stack是由系統自動分配,所以速度比較快,所以我們就在棧上分配一個數組用於儲存即可。④解碼後到播放之間的緩衝,這個緩衝區同樣除了起到使播放視頻連續穩定的作用外,主要就是用來顯示映像,還可以對視頻映像進行一些處理工作,平滑,濾波等。3.解碼和播放的實現H.264解碼是移植了ffmpeg 中的H.264解碼部分到Android,並且了深度刪減最佳化。介面部分,檔案接收處理以及視頻顯示都是用java做的,底層的視頻解碼部分則使用C來做從而滿足速度的要求。H.264碼流分割NAl(接受到視頻資料的複原工作)是在java層做而沒有分裝到c中,是因為每次送的資料會受到限制,如果送的資料量大,底層可能會一次解碼好幾幀視頻,但是到介面層只能顯示一幀,造成丟幀。如果每次送的資料量較少,就會使得多次底層調用但並沒有進行實質解碼的現象發生,所以儘管這樣做耦合度差些,速度慢些,但是綜合考慮還是將資料分析工作放在java層完成。
我們將解碼後的視頻資料用bitmap顯示,draw到surfaceView的方法顯示到手機屏上,由於有些手機不支援rgb24但幾乎所有手機都支援rgb565,所以解碼後返回的是rgb565資料。4.程式流程功能架構
三、結束語 本文完整的設計並實現了從pc端到android手機端的H.264視頻傳輸與解碼播放功能。詳細的分析了實現中的技術要點和痛點,詳細分析了rtp打包,解包的流程,針對發送資料快而處理速度慢的問題,採用多線程並發機制予以解決,面對大量,而且不穩定的資料包,針對各個環節的特點,設定了多級緩衝。使得視頻播放更加流暢、平穩。對於分析和解碼的先後次序問題,採用線程協作的思想,利用消費者,生產者模式,保證了視頻資料的時序性。另外對於解碼部分,則利用現有解碼方法進行平台移植,合理處理c層和java層的分工,並以實現了這個完整的功能。在網路狀況好的情況下,android手機端視頻播放延時短,播放流暢,平穩。本文技術研究可以運用到視頻播放的各個應用中,有著很強的實用價值。 文/南京郵電大學 張永芹 龔建榮
H.264視頻在android手機端的解碼與播放(轉)