Android MediaCodec使用介紹

編解碼器簡單概念

MediaCodec用來使用底層多媒體編解碼器。

寬泛講編解碼器處理輸入數據生成輸出數據,它的處理是異步的並且使用一系列輸入輸出buffer。簡單層面來說,你請求或者獲取空的輸入buffer,裝滿輸入之後發送到編解碼器處理,在處理結束後編解碼器再將數據轉換到一個輸出的空buffer。最後你請求或者獲取裝滿數據的輸出buffer,消耗完其內容數據之後將該buffer釋放回編解碼器。

1.支持的數據類型

編解碼器支持三種數據:壓縮的數據、原始的音頻數據、原始的視頻數據。所有的三種數據都可以通過使用ByteBuffers來處理,但是對於原始的視頻數據可以使用Surface來提高編解碼器的性能同時可以使用ImageReader獲取視頻的單個幀,即截圖。

對於原始視頻數據,當你使用ByteBuffers來處理時,其buffer的排列是依據他們的顏色格式。視頻編解碼器支持三種顏色格式:

  • native raw video format:通過COLOR_FormatSurface來標記,可以被用作輸入或者輸出Surface。

  • flexible YUV buffers(比如 COLOR_FormatYUV420Flexible):可以用作輸入輸出Surface以及ByteBuffers模式。

  • other, specific formats:這些只能使用ByteBuffers模式處理。

2.編解碼器的狀態

編解碼器的理論狀態是三種:停止、執行、釋放。

停止狀態由包含三個子狀態:錯誤、爲初始化、配置。

執行狀態包含三個子狀態:刷新、運行、結束流。

當使用工廠方法獲得編解碼器對象時,起處於未初始化狀態。首先需要新建MMediaFormat對象,使用configure方法進行配置,此時處於配置狀態。接着調用start方法此時進入執行狀態。在這個狀態可以通過緩衝buffer操作過程數據。

執行狀態包含三個子狀態。當調用start進入刷新子狀態,該狀態持有所有buffer。一旦第一個輸入buffer出隊列編解碼器就進入運行子狀態,這個狀態最耗時。當你入隊列一個輸入buffer帶有ENDOFSTREAM標記時,編解碼器進入結束流子狀態,在這個狀態不在接收輸入buffer,但依然生成輸出buffer。

調用stop方法進入爲初始化狀態,一旦編解碼器使用完成你必須調用release進行釋放。

編解碼器的使用

以下是如何通過代碼使用編解碼器。編解碼器有兩種使用方式:異步使用、同步使用。

1.異步使用

 MediaCodec codec = MediaCodec.createByCodecName(name);

 MediaFormat mOutputFormat; // member variable

 codec.setCallback(new MediaCodec.Callback() {

   @Override

   void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {

     ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);

     // fill inputBuffer with valid data

     …

     codec.queueInputBuffer(inputBufferId, …);

   }

   @Override

   void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {

     ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);

     MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A

     // bufferFormat is equivalent to mOutputFormat

     // outputBuffer is ready to be processed or rendered.

     …

     codec.releaseOutputBuffer(outputBufferId, …);

   }

   @Override

   void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {

     // Subsequent data will conform to new format.

     // Can ignore if using getOutputFormat(outputBufferId)

     mOutputFormat = format; // option B

   }

   @Override

   void onError(…) {

     …

   }

 });

 codec.configure(format, …);

 mOutputFormat = codec.getOutputFormat(); // option B

 codec.start();

 // wait for processing to complete

 codec.stop();

 codec.release();

2.同步使用

     MediaCodec codec = MediaCodec.createByCodecName(name);

     codec.configure(format, …);

     MediaFormat outputFormat = codec.getOutputFormat(); // option B

     codec.start();

     for (;;) {

       int inputBufferId = codec.dequeueInputBuffer(timeoutUs);

       if (inputBufferId >= 0) {

         ByteBuffer inputBuffer = codec.getInputBuffer(…);

         // fill inputBuffer with valid data

         …

         codec.queueInputBuffer(inputBufferId, …);

       }

       int outputBufferId = codec.dequeueOutputBuffer(…);

       if (outputBufferId >= 0) {

         ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);

         MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A

         // bufferFormat is identical to outputFormat

         // outputBuffer is ready to be processed or rendered.

         …

         codec.releaseOutputBuffer(outputBufferId, …);

       } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

         // Subsequent data will conform to new format.

         // Can ignore if using getOutputFormat(outputBufferId)

         outputFormat = codec.getOutputFormat(); // option B

       }

     }

     codec.stop();

     codec.release();

附錄參考

官方文檔

發佈了40 篇原創文章 · 獲贊 30 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章