MediaCodec
用于訪問底層媒體編解碼器框架,編解碼組件。通常與MediaExtractor(解封裝,例如Mp4文件分解成 video和audio)、MediaSync、MediaMuxer(封裝 例如音視頻合成Mp4文件)、MediaCrypto、Image(cameraX 回調的ImageReader對象可以獲取到Image幀圖像,可轉換成YUV420數組,傳遞給編碼器編碼),Surface(camera的預覽回調)、Audio一起使用。
創建MediaCodec
public static MediaCodec createEncoderByType(@NonNull String type) //創建編碼器public static MediaCodec createDecoderByType(@NonNull String type) //創建解碼器
一般type是:
- h264 MediaFormat.MIMETYPE_VIDEO_AVC(video/avc)
- h265 MediaFormat.MIMETYPE_VIDEO_HEVC(video/hevc)
public static MediaCodec createByCodecName(@NonNull String name) //通過名稱創建
一般name是:
- OMX.google.h264.encoder 軟編碼
- OMX.google.h264.decoder 軟解碼
- OMX.MTK.VIDEO.DECODER.AVC 硬解碼
- 等
以下代碼可以檢測是否支持某編解碼
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {if (codecInfo.isEncoder()) {continue; // 只檢查解碼器}String[] types = codecInfo.getSupportedTypes();for (String type : types) {if (type.equalsIgnoreCase("video/hevc")) {Log.d("MediaCodec", "Device supports H265 decoding");return; // 找到支持的解碼器,可以退出}}
}
Log.d("MediaCodec", "Device does not support H265 decoding");
工作方式
以以下代碼為例
// 假設你已經有了ByteBuffer data和BufferInfo info
codec.queueInputBuffer(inputBufferIndex, 0, data.limit(), presentationTimeUs, 0);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
while (outputBufferIndex >= 0) {// 處理輸出幀,例如繪制到Surface或保存到文件等codec.releaseOutputBuffer(outputBufferIndex, true); // true表示將輸出幀渲染到Surface中(如果有的話)outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0); // 再次嘗試獲取下一個輸出幀
}
- queueInputBuffer 處理輸入數據(如:camera/cameraX回調的視頻幀數據)
- dequeueOutputBuffer 輸出數據 編碼后的數據(如:使用H264編碼則 可直接將數據保存為H264文件)
分析: - MediaCodeC使用一組輸入和輸出Buffer隊列。
- 數據填入設定的空輸入緩沖區 ( inputBuffers = mediaCodec.getInputBuffers();),填滿數據后傳遞給MediaCodec進行編解碼
- 編解碼后數據填充到一個輸出Buffer中。
狀態
- MediaCodec 有三種狀態 Stopped Executing Released
– Stopped 包括 三種狀態 Uninitialized Configured Error
– Executing 包括三種狀態 Flushed Running End-of-Stream
狀態
- 通過以上create工廠方法創建一個MediaCodec ,MediaCodec 處于未初始化狀態
- 需要通過MediaCodec對象的configure方法配置,配置后 處于配置狀態
String mimeType = "video/hevc"; // H265 MIME類型
MediaCodec codec = MediaCodec.createDecoderByType(mimeType);
MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // 使用Surface作為輸出類型,如果是預覽或顯示使用
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); // 設置比特率,如果需要的話
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); // 設置幀率,如果需要的話
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 設置I幀間隔,通常設置為1或2codec.configure(format, null, null, 0); // 使用null作為輸出Surface,因為我們不直接處理輸出數據
codec.start();
- 調用start之后 處于 Executing狀態
- 調用start后,MediaCodec 會立刻刷新子狀態(Flushed ) 并擁有所有的Buffer
- 第一個輸入Buffer從隊列中移除 MediaCodec 會花較長時間轉換為 正在運行的子狀態 (Running )
- 當隊列輸入Buffer攜帶End-of-Stream標記 則轉換為 End-of-Stream子狀態 這種狀態MediaCodec不再接收輸入Buffer,但是仍然會產生輸出Buffer
- Executing狀態下,調用flush函數可以回到 Flushed子狀態
- 調用stop,MediaCodec 處于Uninitialized 狀態 等待再次配置和使用
- 再次創建MediaCodec 時,上個MediaCodec 對象必須調用release函數
- 極少數情況會出現Error狀態,此時需要調用reset函數讓MediaCodec 再次可用
MediaCodec 從創建到start的過程
- 需要經歷JNI
- 與MediaPlayer有很多相似的地方
- 我幫大家看了 想看的花去點這里
就是MediaCodec 會對應到 c++層的JMediaCodec方法
release -> native_release
reset -> native_reset
setup -> native_setup
finalize -> native_finalize
config -> native_configure
- setup -> 調用 JMediaCodec 構造方法創建JMediaCodec
- JMediaCodec的構造方法 -> createByType/CreateByComponentName 創建JMediaCodec
- 接著java 調用config -> native_configure
- native_configure -> 獲取前邊創建的JMediaCodec 對象 調用其configure方法 構建編解碼器
- java 層調用 start -> 最終使用的還是JMediaCodec 對象 的 start方法 會直行ACodec.cpp 的start 函數
MediaCodec 到OMX過程
OpenMAX Integration Layer(OMX IL,集成層)是由Khronos Group開發的一套低層級標準接口,旨在為編解碼器提供一定程度的抽象,使得嵌入式或移動設備能夠統一調用音頻、視頻和圖像編解碼器,從而實現編解碼器實現代碼和調用代碼的跨平臺性。
OMX IL API由兩大主要部分組成,分別是Core API和Component API。
OMX IL Component:在OMX IL中組件表示獨立的功能模塊,組件可能是source(源)、sinks(接收器)、codecs(編解碼器)、filters(過濾器)或任何其他數據處理模塊,組件需要依據Component API來實現。與組件之間的數據通信是通過稱為端口的接口進行的,用戶可以通過輸入端口向組件發送數據,也可以通過輸出端口接收數據。
OMX IL Core:Core API主要用于動態加載卸載組件,調用組件方法;
將OMX IL API封裝并向上層提供高層級接口的部分被稱為IL Client(客戶端),IL Client使用OMX Core來加載組件,卸載組件,調用組件的方法。
- MediaCodec::init 函數
– 創建Acodec Acodec繼承AHander(消息機制有了)
– 初始化ALooper AMessage
– 發送kWhatInit消息
– ACodec收到消息 調用initiateAllocateComponent(format)函數
– 發送kWhatAllocateComponent消息 - 消息中心收到消息 調用onAllocateComponent回調函數
– 通過ACodec::AllocateComponent函數判斷OMXClient和Server是否正常建立連接
– 通過IOMX進行IPC通信
– 調用omx->allocateNode分配Node節點 - onConfigureComponent 函數
– 調用ACodec的configCodec函數 構建編解碼器