摘 要:文章提出了一種視頻碼流轉換的算法,并進行了詳細闡述,同時給出了該算法關鍵設計處的實際解決方案。
關鍵詞:流媒體 碼流轉換 視頻編解碼 互斥
中圖分類號:TP311 文獻標識碼:A文章編號:1673-8454(2007)12-0080-02
筆者在此提出了一種用軟件編解碼進行視頻轉換的算法,該算法可以以較低的開發成本來完成多種流媒體文件間的格式轉換,在實際應用中取得了很好的效果。
一、視頻碼流轉換簡介

視頻碼流轉換主要有硬件編解碼和軟件編解碼兩種方法。對于兩種編解碼方法而言,其基本原理都是一樣的:硬件編解碼方法是通過攝像裝置以一定的、相對較低的速率捕獲視頻,然后通過編解碼芯片將一組視頻幀編成相應的碼流并以一定的速率發送到網絡上去;而軟件編解碼方法則是主要依托軟件編程,首先以一定的速率將待轉換的碼流還原成視頻幀,再以相同的速率將這些視頻幀編碼成目標碼流并發送至網絡上。其步驟簡單,如圖1所示。目前硬件編解碼的方法比較常見,其優點是實時性強且適于批量生產;但缺點也是明顯的,相對軟件編解碼方法而言,除了前期開發成本較高和開發周期較長外,其產品適用范圍也比較單一,往往只是一對一的格式轉換。對于軟件編解碼方法來說,它克服了硬件編解碼開發過程中存在的缺陷,不過它的實時性效果卻無法與硬件編解碼的效果相比,這也是它最大的不足之處,至于最終選取何種方法應視具體情況而定。
二、視頻碼流轉換算法
本文提出的這種視頻碼流轉換的轉換方法可以克服目前常見的軟件編解碼方法的不足,其算法結構如圖2所示,標志位Flag0初始置為1,而標志位Flag1初始置為0。本算法主要是在內存中開辟兩塊內存單元,其大小視截獲的數據流一幀圖像的大小而定。然后分別在兩個內存區域里存儲一定張數的視頻幀,當任一個內存區域存儲結束后就將該內存區里的全部視頻幀編碼成目標碼流。最后,將生成的目標碼流發送進視頻服務器的發送隊列中,并重復以上步驟直至待轉換碼流結束。在以上步驟中,當一個內存區的全部視頻幀被進行編碼轉換后,就等待被下一組視頻幀所覆蓋。可以看出在這個算法中,軟件在兩個內存區域中對所截獲的視頻幀的編碼轉換的協調異常重要,它是整個軟件得以正常運行的重要保障。

三、軟件互斥的實現
程序在執行時的互斥問題是因為涉及對臨界資源的使用而產生的。臨界資源就是一次只能供一個進程使用的資源,在本程序算法中編碼插件或函數以及向服務器隊列發送的插件或函數就屬于臨界資源。[2] 由于一般的編碼插件或函數只能設計成對一組視頻幀進行視頻編碼處理,要保持視頻轉換的連續性,筆者設計了兩個隊列(內存區域)用來輪流存儲解碼所得來的視頻幀。而這兩塊重復存儲的內存區域也是臨界資源,一方面要對內存區域進行寫操作,另一方面在對該內存區域里的視頻幀進行編碼轉換的同時要禁止再次向該內存區進行寫操作。因此,對這兩個隊列的視頻幀輪流進行存儲和編碼轉換工作就成為本程序執行中互斥的關鍵所在。在此,筆者為每個內存區域設計了鎖機制,在進入每個內存區之前一定要先獲得該內存的鎖,只有獲得了相應內存區域的鎖才能對該內存區進行存儲和編碼轉換操作,并在對該內存區域內的視頻幀操作完成后釋放所獲得內存區的鎖,以便下一次操作的進行。而標志位Flag0、Flag1的設置,更進一步明確了程序調用的時序。當程序最初執行時,將Flag0置1,而Flag1置0,這樣一定是內存0區的視頻幀先被編碼轉換;當Flag0取反為0,而Flag1取反為1時,則只有內存1區的視頻幀被編碼轉換,從而保證了輸出碼流的時序的正確性。對于編碼插件或函數以及向服務器隊列發送的插件或函數的互斥處理,同樣采用了鎖機制,從而確保了一次只對一組連續的視頻幀進行編碼轉換。
四、軟件的具體實現
筆者最近研究的課題中需要在Windows平臺下將M-JPEG碼流格式(一種工業常用非標準格式)轉換成MPEG碼流格式。在實際編程過程中,筆者以C/C++語言為基礎來編制軟件,運用了目前軟件工程學中比較成熟的模塊化編程思想,這使得許多已有的優秀軟件成果可以通過模塊形式被調用。例如,在程序的開始階段,將待轉換的視頻碼流還原成視頻幀的過程中,筆者就根據自己課題的要求選用了AXIS公司的Media控件實現了將M-JPEG碼流以5幀/秒的速率還原成視頻幀。
當然,在整個程序的設計過程中,并不是總能找到合適的控件作為模塊借鑒,也不能因循守舊。筆者從實際出發,根據C/C++語言自身的特點因地制宜,在對內存使用的互斥算法的處理中,并沒有采取經典的PV信號量機制,而是采用了鎖機制的方法。[2] 這主要是因為C/C++語言提供了flock()函數,可以使對內存隊列的加鎖和解鎖操作達到原語操作的效果,這對于進程互斥是至關重要的。[3] 而要在Windows環境下對內存進行操作,就需要借助Windows系統提供的API函數。在Windows環境下,其API函數是各種高級語言與Windows系統聯系的紐帶,能很好地與微軟自身開發的VB/VC++軟件進行混合編程。我們首先可以通過調用API函數中的GloalAlloc()函數開兩個內存區域,其大小應比一幀數據量與預計截得的視頻張數的積略大。當然,這兩個內存區域還都應該留有句柄。其實,Windows會給每個使用GlobalAlloc()等函數聲明的內存區域指定一個句柄(其本質上仍是一個指針,但不要直接操作它),在調用API函數時利用這個句柄來說明要操作哪段內存。當你需要對某個內存進行直接操作時,為確保與其它Windows下的進程互斥,還可以使用GlobalLock()鎖住這段內存并獲得指針來進行操作。當所需內存開辟好以后,就要將視頻幀存入到相應的內存中去了。這時筆者再次借助了Windows系統提供的API函數,主要是運用了BitBlt()函數,將在顯存中截得的視頻幀位圖掃描到相應的內存區域當中。[3] 這中間除了要寫一些內存管理函數對內存進行管理外,對幀格式的了解和把握也是至關重要的,這可以讓你在轉存幀的同時通過一些代碼將幀的格式進行轉換,從而為后面的編碼轉換做好準備。
最后,在處理將一組連續的視頻幀編制成目標碼的過程中,筆者選用并簡單修改了NCTVideoStudio的控件并作為模塊調用,從而完成了將一個內存區中的所有視頻幀編制成MPEG碼流段的任務。當一段段的碼流生成好后,我們可以將它們封裝到預先定義好的數據報中,并根據報頭的地址傳到網絡服務器指定的存儲空間去,以備服務器進行處理。
整個過程將周而復始地連續進行,直至待轉換視頻碼流結束。
五、兩種方法的比較
通過上面對這種軟件編解碼的算法簡介,不難看出系統在進程間以及在資源調用時的開銷,必將造成輸出碼流的不連續性,而為了在C/S系統的客戶終端看到連續的視頻播放,就必須在服務器的播放隊列中預留一定的長度,以確保在客戶端最終能看到連續的視頻節目。[5] 所以用軟件進行視頻碼流轉換對硬件的要求會更高一些。而且由此也可看出用軟件進行碼流轉換必然會造成一定的時延,它雖然可以通過提高服務器性能和網絡可靠性來改善,但終究無法避免。因此,對于高實時性的項目,如實時監控設備,就不適宜用軟件進行視頻碼流轉換,而應采用硬件編解碼芯片來完成碼流轉換和網絡傳輸任務。這里,筆者再次強調:最終選取何種方法還是要視具體情況而定。
參考文獻:
[1]STEVE MACK.流媒體寶典[M].北京:電子工業出版社,2003
[2]Andrew S. Tanenbaum.陳向群,馬洪兵譯.現代操作系統 (第2版)[M].北京:機械工業出版社,2005
[3]Arnold Robbins.Linux Programming By Example:The Fundamentals[M].Prentice Hall PTR,2005
[4]冉林倉.Windows API編程[M].北京:清華大學出版社,2005
[5]Ravindra K.Ahuja,Thomas L.Magnanti,James B.Orlin.Network Flows:Theory,Algorithms,and Applications[M].Prentice Hall/Pearson,2005