H.265(HEVC)編碼格式能夠在得到相同編碼質量視頻的前提下,使用相當於H.264(AVC)一半的存儲容量,雖然H.265的算法復雜度比H.264高一個數量級,但是硬件水平在不斷提高,因此H.265使用場合逐漸多了起來。好多硬件廠商芯片內部實現了H.265的硬解碼。最近調試了vlc-android調用OpenMAX硬解碼H.265的部分,使用的硬件平台是ZX-2000,系統是Android5.1。
OpenMAX的官方網站:https://www.khronos.org/openmax/
官方文檔整理:https://github.com/jiayayao/DataSheet/tree/master/encode-decode/openmax
官方介紹:The OpenMAX Working Group has been formed by the Khronos Group, to define a set of standard, open Application Programming Interfaces (APIs) for multimedia applications. The goal of this open standard is to reduce the cost and complexity of porting multimedia software to new processors and architectures.
可見OpenMAX的核心價值在於抽象了各個硬件平台和框架,減少了多媒體應用軟件移植的復雜度。OpenMAX實際上分成三個層次,自上而下分別是,OpenMAX AL(應用層),OpenMAX IL(集成層)和OpenMAX DL(開發層)。在實際的應用中,OpenMAX的三個層次中使用較多的是集成層OpenMAX IL。三個層次的內容如下所示。
第一層:OpenMAX AL(Appliction Layer,應用層),在應用程序和多媒體中間件之間提供了一個標准化接口,多媒體中間件提供服務以實現被期待的API功能;
第二層:OpenMAX IL(Integration Layer,集成層),作為音頻、視頻和圖像編解碼器能與多媒體編解碼器交互,並以統一的行為支持組件(例如,資源和皮膚)。這些編解碼器或許是軟硬件的混合體,對用戶是透明的底層接口應用於嵌入式、移動設備。它提供了應用程序和媒體框架,透明的。編解碼器供應商必須寫私有的或者封閉的接口,集成進移動設備。IL的主要目的是使用特征集合為編解碼器提供一個系統抽象,為解決多個不同媒體系統之間輕便性的問題;
第三層:OpenMAX DL(Development Layer,開發層),定義了一個API,它是音頻、視頻和圖像功能的集合。供應商能夠在一個新的處理器上實現並優化,然后編解碼供應商使用它來編寫更廣泛的編解碼器功能。它包括音頻信號的處理功能,如FFT和filter,圖像原始處理,如顏色空間轉換、視頻原始處理,以實現例如MPEG-4、H.264、MP3、AAC和JPEG等編解碼器的優化。
OpenMAX對硬解碼的事務進行了抽象,提供了一系列的.h文件,各個廠家分別根據頭文件實現OpenMAX的標准。在vlc-android工程中,通過調用mediacodec.c文件,查詢Android系統的MediaCodec是否支持硬解碼,如果支持,則創建對應的硬解碼器。mediacodec.c文件的作用是封裝Android系統的Mediacodec框架,同時OpenMAX及其實現會作為對應的解碼器注冊到MediaCodec框架中。當mediacodec_ndk.c文件中調用Start函數創建解碼器時:
p_sys->p_codec = syms.AMediaCodec.createCodecByName(api->psz_name);
該語句實際上調用的是MediaCodec框架的AMediaCodec_createCodecByName函數(Android5.1源碼:NdkMediaCodec.cpp). Android系統的MediaCodec與OpenMAX的關系如下圖:
對H265編碼格式的視頻來說,創建的解碼器名稱是:OMX.ZX.video.decoder.hevc,也就是說實際的硬解碼走的是ZX-2000對OpenMAX的實現的H265部分,硬解碼時打印的日志大部分都是以“S3OMX”等開頭也證明了這一點”。從OpenMAX的AL, IL, DL三個層次的理解上來看,mediacodec.c文件及該文件之上的文件封裝了OpenMAX,使得OpenMAX在vlc中得以利用,應該屬於AL層;ZX-2000的BSP開發部門實現了OpenMAX標准(代碼以“S3OMX”開頭),BSP中對OpenMAX的實現應該屬於IL層;DL層應該是芯片內部硬解碼部分的驅動實現。所以對於應用層的開發人員來說,應該了解AL和IL層。
一、OpenMAX IL
OpenMAX IL層標准提供了幾個頭文件:
OMX_Types.h:OpenMAX Il的數據類型定義 OMX_Core.h:OpenMAX IL核心的API OMX_Component.h:OpenMAX IL 組件相關的 API OMX_Audio.h:音頻相關的常量和數據結構 OMX_IVCommon.h:圖像和視頻公共的常量和數據結構 OMX_Image.h:圖像相關的常量和數據結構 OMX_Video.h:視頻相關的常量和數據結構 OMX_Other.h:其他數據結構(包括A/V 同步) OMX_Index.h:OpenMAX IL定義的數據結構索引 OMX_ContentPipe.h:內容的管道定義
各個硬件廠商需要根據這些頭文件實現OpenMAX標准。比如以Android5.1源碼為例,TI的OMAP芯片實現OpenMAX的代碼就在文件android5.1\hardware\ti\omap4xxx\ domx\omx_core\src\OMX_Core.c中,其他廠商如果要實現硬解碼,也需要按照類似的做法,將自身對於OpenMAX的實現添加到Android系統BSP中。
Android5.1源碼的android5.1\hardware\ti\omap4xxx\domx\test\sample_proxy\test_sample_ proxy.c文件是TI的OMAP芯片OpenMAX部分的簡單測試程序。該測試程序僅僅是簡單的初始化Component,處理緩沖區,最后通過改變Component狀態釋放Component。如果需要適配OpenMAX到新的芯片,可以參考OMAP的實現。
二、OpenMAX AL
mediacode.c和avcodec.c作用是類似的,mediacodec.c為OpenMAX在VLC下的使用提供了封裝,avcodec.c為FFmpeg在VLC下的使用提供了封裝。提供封裝時,一般需要提供幾個函數,如OpenDecoder,CloseDecoder,DecodeBlock等,這些函數會被上層調用。
當獲取到的RTSP流是H.265編碼格式后,mediacodec_ndk.c創建解碼器“OMX.ZX.video.decoder.hevc”,創建解碼器時,會將mediacodec_ndk.c實現的一系列函數賦值給mc_api結構體,包括start(), stop(), configure(), clean()等。解碼時解碼線程DecodeThread會調用DecodeBlock函數解碼數據塊,但是與avcodec(軟解)的直接解碼不同,mediacodec的解碼線程是將需要解碼的數據塊(block_t類型)通過API經mediacodec_ndk.c送入硬解碼芯片隊列中,另外開啟一個新的線程OutThread來從硬解碼芯片的圖像隊列中取出圖像的index,賦值一些時間戳信息,再推入圖像fifo中,交給渲染線程處理。
注意,硬解碼時推入圖像fifo的picture_t只是帶有硬件緩沖區的index,最后渲染線程只是根據index將該緩沖區釋放(modules/video_out/android/display.c);而軟解碼時,FFmepg會將帶有解碼后圖像數據的picture_t交給opengl(modules/video_output/opengl/display.c)去渲染。個人的理解是,硬解碼時,解碼后的圖像就直接在GPU里邊,GPU在恰當的時機直接把圖像交給顯示的framebuffer就可以了,無需再取出來再交給opengl渲染;軟解碼時,解碼后的圖像在系統內存中,需要交給opengl去渲染。
解碼線程堆棧如下:
硬解碼模塊自行創建的輸出線程堆棧如下:
注意,在將圖像推入圖像fifo之前,會先調用API從硬解碼芯片的圖像隊列中取出圖像,調用mediacodec_ndk.c文件的DequeOutput函數。
參考資料:
《Android系統級深入開發—移植與調試》中OpenMAX章節