天天看點

Android多媒體--MediaCodec 中文API文檔

*由于工作需要,需要利用MediaCodec實作Playback及Transcode等功能,故在學習過程中翻譯了Google官方的MediaCodec API文檔,由于作者水準限制,文中難免有錯誤和不恰當之處,望批評指正。

*轉載請注明出處:http://www.cnblogs.com/roger-yu/

=====================================================================================================

Android MediaCodec系列文章:

(一)Android多媒體--MediaCodec 中文API文檔

(二)Android MediaCodec 的執行個體化方法

(三)Android MediaCodec 狀态(States)轉換分析

(四)Android MediaCodec的資料處理方式分析

從API 16開始,Android提供了Mediacodec類以便開發者更加靈活的處理音視訊的編解碼,與MediaPlayer/VideoView等high-level APIs相比,MediaCodec是low-level APIs,是以它提供了更加完善、靈活、豐富的接口,開發者可以實作更加靈活的功能。廢話即止,開始學習之旅~~

MediaCodec

public final class MediaCodec extends Object

Java.lang.Object

  → android.media.MediaCodec

  MediaCodec類可用于通路Android底層的多媒體編解碼器,例如,編碼器/解碼器元件。它是Android底層多媒體支援基礎架構的一部分(通常與MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。

Android多媒體--MediaCodec 中文API文檔

  從廣義上講,編解碼器就是處理輸入資料來産生輸出資料。MediaCode采用異步方式處理資料,并且使用了一組輸入輸出緩存(input and output buffers)。簡單來講,你請求或接收到一個空的輸入緩存(input buffer),向其中填充滿資料并将它傳遞給編解碼器處理。編解碼器處理完這些資料并将處理結果輸出至一個空的輸出緩存(output buffer)中。最終,你請求或接收到一個填充了結果資料的輸出緩存(output buffer),使用完其中的資料,并将其釋放給編解碼器再次使用。

 資料類型(Data Types)

  編解碼器可以處理三種類型的資料:壓縮資料(即為經過H254. H265. 等編碼的視訊資料或AAC等編碼的音頻資料)、原始音頻資料、原始視訊資料。三種類型的資料均可以利用ByteBuffers進行處理,但是對于原始視訊資料應提供一個Surface以提高編解碼器的性能。Surface直接使用本地視訊資料緩存(native video buffers),而沒有映射或複制資料到ByteBuffers,是以,這種方式會更加高效。在使用Surface的時候,通常不能直接通路原始視訊資料,但是可以使用ImageReader類來通路非安全的解碼(原始)視訊幀。這仍然比使用ByteBuffers更加高效,因為一些本地緩存(native buffer)可以被映射到 direct ByteBuffers。當使用ByteBuffer模式,你可以利用Image類和getInput/OutputImage(int)方法來通路到原始視訊資料幀。

  壓縮緩存(Compressed Buffers)

  輸入緩存(對于解碼器)和輸出緩存(對編碼器)中包含由多媒體格式類型決定的壓縮資料。對于視訊類型是單個壓縮的視訊幀。對于音頻資料通常是單個可通路單元(一個編碼的音頻片段,通常包含幾毫秒的遵循特定格式類型的音頻資料),但這種要求也不是十分嚴格,一個緩存内可能包含多個可通路的音頻單元。在這兩種情況下,緩存不會在任意的位元組邊界上開始或結束,而是在幀或可通路單元的邊界上開始或結束。

  原始音頻緩存(Raw Audio Buffers)

  原始的音頻資料緩存包含完整的PCM(脈沖編碼調制)音頻資料幀,這是每一個通道按照通道順序的一個樣本。每一個樣本是一個按照本機位元組順序的16位帶符号整數(16-bit signed integer in native byte order)。

1 short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
 2   ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
 3   MediaFormat format = codec.getOutputFormat(bufferId);
 4   ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
 5   int numChannels = formet.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
 6   if (channelIx < 0 || channelIx >= numChannels) {
 7     return null;
 8   }
 9   short[] res = new short[samples.remaining() / numChannels];
10   for (int i = 0; i < res.length; ++i) {
11     res[i] = samples.get(i * numChannels + channelIx);
12   }
13   return res;
14 }      

  原始視訊緩存(Raw Video Buffers)

  在ByteBuffer模式下,視訊緩存(video buffers)根據它們的顔色格式(color format)進行展現。你可以通過調用getCodecInfo().getCapabilitiesForType(…).colorFormats方法獲得編解碼器支援的顔色格式數組。視訊編解碼器可以支援三種類型的顔色格式:

  • 本地原始視訊格式(native raw video format):這種格式通過COLOR_FormatSurface标記,并可以與輸入或輸出Surface一起使用。
  • 靈活的YUV緩存(flexible YUV buffers)(例如:COLOR_FormatYUV420Flexible):利用一個輸入或輸出Surface,或在在ByteBuffer模式下,可以通過調用getInput/OutputImage(int)方法使用這些格式。
  • 其他,特定的格式(other, specific formats):通常隻在ByteBuffer模式下被支援。有些顔色格式是特定供應商指定的。其他的一些被定義在 MediaCodecInfo.CodecCapabilities中。這些顔色格式同 flexible format相似,你仍然可以使用 getInput/OutputImage(int)方法。

  從Android 5.1(LOLLIPOP_MR1)開始,所有的視訊編解碼器都支援靈活的YUV4:2:0緩存(flexible YUV 4:2:0 buffers)。

狀态(States)

   在編解碼器的生命周期内有三種理論狀态:停止态-Stopped、執行态-Executing、釋放态-Released,停止狀态(Stopped)包括了三種子狀态:未初始化(Uninitialized)、配置(Configured)、錯誤(Error)。執行狀态(Executing)在概念上會經曆三種子狀态:重新整理(Flushed)、運作(Running)、流結束(End-of-Stream)。

Android多媒體--MediaCodec 中文API文檔
  • 當你使用任意一種工廠方法(factory methods)建立了一個編解碼器,此時編解碼器處于未初始化狀态(Uninitialized)。首先,你需要使用configure(…)方法對編解碼器進行配置,這将使編解碼器轉為配置狀态(Configured)。然後調用start()方法使其轉入執行狀态(Executing)。在這種狀态下你可以通過上述的緩存隊列操作處理資料。
  • 執行狀态(Executing)包含三個子狀态: 重新整理(Flushed)、運作( Running) 以及流結束(End-of-Stream)。在調用start()方法後編解碼器立即進入重新整理子狀态(Flushed),此時編解碼器會擁有所有的緩存。一旦第一個輸入緩存(input buffer)被移出隊列,編解碼器就轉入運作子狀态(Running),編解碼器的大部分生命周期會在此狀态下度過。當你将一個帶有end-of-stream 标記的輸入緩存入隊列時,編解碼器将轉入流結束子狀态(End-of-Stream)。在這種狀态下,編解碼器不再接收新的輸入緩存,但它仍然産生輸出緩存(output buffers)直到end-of- stream标記到達輸出端。你可以在執行狀态(Executing)下的任何時候通過調用flush()方法使編解碼器重新傳回到重新整理子狀态(Flushed)。
  • 通過調用stop()方法使編解碼器傳回到未初始化狀态(Uninitialized),此時這個編解碼器可以再次重新配置 。當你使用完編解碼器後,你必須調用release()方法釋放其資源。
  • 在極少情況下編解碼器會遇到錯誤并進入錯誤狀态(Error)。這個錯誤可能是在隊列操作時傳回一個錯誤的值或者有時候産生了一個異常導緻的。通過調用 reset()方法使編解碼器再次可用。你可以在任何狀态調用reset()方法使編解碼器傳回到未初始化狀态(Uninitialized)。否則,調用 release()方法進入最終的Released狀态。

建立(Creation)

  根據指定的MediaFormat使用MediaCodecList建立一個MediaCodec執行個體。在解碼檔案或資料流時,你可以通過調用MediaExtractor.getTrackFormat方法獲得所期望的格式(media format)。并調用MediaFormat.setFeatureEnabled方法注入任何你想要添加的特定屬性,然後調用MediaCodecList.findDecoderForFormat方法獲得可以處理指定的媒體格式的編解碼器的名字。最後,通過調用createByCodecName(String)方法建立一個編解碼器。

  注意:在Android 5.0 (LOLLIPOP)上,傳遞給MediaCodecList.findDecoder/EncoderForFormat的格式不能包含幀率-frame rate。通過調用format.setString(MediaFormat.KEY_FRAME_RATE, null)方法清除任何存在于目前格式中的幀率。

  你也可以根據MIME類型利用createDecoder/EncoderByType(String)方法建立一個你期望的編解碼器。然而,這種方式不能夠給編解碼器加入指定特性,而且建立的編解碼器有可能不能處理你所期望的媒體格式。

  建立安全的解碼器(Creating secure decoders)

  在Android 4.4(KITKAT_WATCH)及之前版本,安全的編解碼器(secure codecs)沒有被列在MediaCodecList中,但是仍然可以在系統中使用。安全編解碼器隻能夠通過名字進行執行個體化,其名字是在正常編解碼器的名字後附加.secure辨別(所有安全編解碼器的名字都必須以.secure結尾),調用createByCodecName(String)方法建立安全編解碼器時,如果系統中不存在指定名字的編解碼器就會抛出IOException異常。

  從Android 5.0(LOLLIPOP)及之後版本,你可以在媒體格式中使用FEATURE_SecurePlayback屬性來建立一個安全編解碼器。

初始化(Initialization)

  在建立了編解碼器後,如果你想異步地處理資料,可以通過調用setCallback方法設定一個回調方法。然後,使用指定的媒體格式配置編解碼器。這時你可以為視訊原始資料産生者(例如視訊解碼器)指定輸出Surface。此時你也可以為secure 編解碼器設定解密參數(詳見MediaCrypto) 。最後,因為編解碼器可以工作于多種模式,你必須指定是該編碼器是作為一個解碼器(decoder)還是編碼器(encoder)運作。

  從API LOLLIPOP起,你可以在Configured 狀态下查詢輸入和輸出格式的結果。在開始編解碼前你可以通過這個結果來驗證配置的結果,例如,顔色格式。

  如果你想将原始視訊資料(raw video data)送視訊消費者處理(将原始視訊資料作為輸入的編解碼器,例如視訊編碼器),你可以在配置好視訊消費者編解碼器(encoder)後調用createInputSurface方法建立一個目的surface來存放輸入資料,如此,調用視訊生産者(decoder)的setInputSurface(Surface)方法将前面建立的目的Surface配置給視訊生産者作為輸出緩存位置。

  Codec-specific資料

  有些格式,特别是ACC音頻和MPEG4、H.264和H.265視訊格式要求實際資料以若幹個包含配置資料或編解碼器指定資料的緩存為字首。當處理這種壓縮格式的資料時,這些資料必須在調用start()方法後且在處理任何幀資料之前送出給編解碼器。這些資料必須在調用queueInputBuffer方法時使用BUFFER_FLAG_CODEC_CONFIG進行标記。

  Codec-specific資料也可以被包含在傳遞給configure方法的格式資訊(MediaFormat)中,在ByteBuffer條目中以"csd-0", "csd-1"等key标記。這些keys一直包含在通過MediaExtractor獲得的Audio Track or Video Track的MediaFormat中。一旦調用start()方法,MediaFormat中的Codec-specific資料會自動送出給編解碼器;你不能顯示的送出這些資料。如果MediaFormat中不包含編解碼器指定的資料,你可以根據格式要求,按照正确的順序使用指定數目的緩存來送出codec-specific資料。在H264 AVC編碼格式下,你也可以連接配接所有的codec-specific資料并作為一個單獨的codec-config buffer送出。

  Android 使用下列的codec-specific data buffers。對于适當的MediaMuxer軌道配置,這些也要在軌道格式中進行設定。每一個參數集以及被标記為(*)的codec-specific-data段必須以"\x00\x00\x00\x01"字元開頭。

Android多媒體--MediaCodec 中文API文檔

  注意:當編解碼器被立即重新整理或start之後不久重新整理,并且在任何輸出buffer或輸出格式變化被傳回前需要特别地小心,因為編解碼器的codec specific data可能會在flush過程中丢失。為保證編解碼器的正常運作,你必須在重新整理後使用标記為BUFFER_FLAG_CODEC_CONFIGbuffers的buffers再次送出這些資料。

   編碼器(或者産生壓縮資料的編解碼器)将會在有效的輸出緩存之前産生和傳回編解碼器指定的資料,這些資料會以codec-config flag進行标記。包含codec-specific-data的Buffers沒有有意義的時間戳。 

資料處理(Data Processing)

   每一個編解碼器都包含一組輸入和輸出緩存(input and output buffers),這些緩存在API調用中通過buffer-id進行引用。當成功調用start()方法後用戶端将不會“擁有”輸入或輸出buffers。在同步模式下,通過調用dequeueInput/OutputBuffer(…) 方法從編解碼器獲得(取得所有權)一個輸入或輸出buffer。在異步模式下,你可以通過MediaCodec.Callback.onInput/OutputBufferAvailable(…)的回調方法自動地獲得可用的buffers。

  在獲得一個輸入buffe後,向其中填充資料,并利用queueInputBuffer方法将其送出給編解碼器,若使用解密,則利用queueSecureInputBuffer方法送出。不要送出多個具有相同時間戳的輸入buffers(除非它是也被同樣标記的codec-specific data)。

  在異步模式下通過onOutputBufferAvailable方法的回調或者在同步模式下響應dequeuOutputBuffer的調用,編解碼器傳回一個隻讀的output buffer。在這個output buffer被處理後,調用一個releaseOutputBuffer方法将這個buffer傳回給編解碼器。

  當你不需要立即向編解碼器重新送出或釋放buffers時,保持對輸入或輸出buffers的所有權可使編解碼器停止工作,當然這些行為依賴于裝置情況。特别地,編解碼器可能延遲産生輸出buffers直到輸出的buffers被釋放或重新送出。是以,盡可能短時間地持有可用的buffers。

  根據API版本情況,你有三種處理相關資料的方式:

Android多媒體--MediaCodec 中文API文檔

  使用緩存的異步處理方式(Asynchronous Processing using Buffers)

   從Android 5.0(LOLLIPOP)開始,首選的方法是調用configure之前通過設定回調異步地處理資料。異步模式稍微改變了狀态轉換方式,因為你必須在調用flush()方法後再調用start()方法才能使編解碼器的狀态轉換為Running子狀态并開始接收輸入buffers。同樣,初始調用start方法将編解碼器的狀态直接變化為Running 子狀态并通過回調方法開始傳遞可用的輸入buufers。

Android多媒體--MediaCodec 中文API文檔

  異步模式下,編解碼器典型的使用方法如下:

1  MediaCodec codec = MediaCodec.createByCodecName(name);
 2  MediaFormat mOutputFormat; // member variable
 3  codec.setCallback(new MediaCodec.Callback() {
 4    @Override
 5    void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
 6      ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
 7      // fill inputBuffer with valid data
 8      …
 9      codec.queueInputBuffer(inputBufferId, …);
10    }
11 
12    @Override
13    void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
14      ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
15      MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
16      // bufferFormat is equivalent to mOutputFormat
17      // outputBuffer is ready to be processed or rendered.
18      …
19      codec.releaseOutputBuffer(outputBufferId, …);
20    }
21 
22    @Override
23    void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
24      // Subsequent data will conform to new format.
25      // Can ignore if using getOutputFormat(outputBufferId)
26      mOutputFormat = format; // option B
27    }
28 
29    @Override
30    void onError(…) {
31      …
32    }
33  });
34  codec.configure(format, …);
35  mOutputFormat = codec.getOutputFormat(); // option B
36  codec.start();
37  // wait for processing to complete
38  codec.stop();
39  codec.release();      

  使用緩存的同步處理方式(Synchronous Processing using Buffers)

  從Android5.0(LOLLIPOP)開始,即使在同步模式下使用編解碼器你應該通過getInput/OutputBuffer(int) 和/或 getInput/OutputImage(int) 方法檢索輸入和輸出buffers。這允許通過架構進行某些優化,例如,在處理動态内容過程中。如果你調用getInput/OutputBuffers()方法這種優化是不可用的。

  注意,不要同時混淆使用緩存和緩存數組的方法。特别地,僅僅在調用start()方法後或取出一個值為INFO_OUTPUT_FORMAT_CHANGED的輸出buffer ID後你才可以直接調用getInput/OutputBuffers方法。

  同步模式下MediaCodec的典型應用如下:

1  MediaCodec codec = MediaCodec.createByCodecName(name);
 2  codec.configure(format, …);
 3  MediaFormat outputFormat = codec.getOutputFormat(); // option B
 4  codec.start();
 5  for (;;) {
 6    int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
 7    if (inputBufferId >= 0) {
 8      ByteBuffer inputBuffer = codec.getInputBuffer(…);
 9      // fill inputBuffer with valid data
10      …
11      codec.queueInputBuffer(inputBufferId, …);
12    }
13    int outputBufferId = codec.dequeueOutputBuffer(…);
14    if (outputBufferId >= 0) {
15      ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
16      MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
17      // bufferFormat is identical to outputFormat
18      // outputBuffer is ready to be processed or rendered.
19      …
20      codec.releaseOutputBuffer(outputBufferId, …);
21    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
22      // Subsequent data will conform to new format.
23      // Can ignore if using getOutputFormat(outputBufferId)
24      outputFormat = codec.getOutputFormat(); // option B
25    }
26  }
27  codec.stop();
28  codec.release();      

  使用緩存數組的同步處理方式(Synchronous Processing using Buffer Arrays)-- (deprecated)

  在Android 4.4(KITKAT_WATCH)及之前版本,一組輸入或輸出buffers使用ByteBuffer[]數組表示。在成功調用了start()方法後,通過調用getInput/OutputBuffers()方法檢索buffer數組。在這些數組中使用buffer的ID-s(非負數)作為索引,如下面的示範示例中,注意數組大小和系統使用的輸入和輸出buffers的數量之間并沒有固定的關系,盡管這個數組提供了上限邊界。

1  MediaCodec codec = MediaCodec.createByCodecName(name);
 2  codec.configure(format, …);
 3  codec.start();
 4  ByteBuffer[] inputBuffers = codec.getInputBuffers();
 5  ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 6  for (;;) {
 7    int inputBufferId = codec.dequeueInputBuffer(…);
 8    if (inputBufferId >= 0) {
 9      // fill inputBuffers[inputBufferId] with valid data
10      …
11      codec.queueInputBuffer(inputBufferId, …);
12    }
13    int outputBufferId = codec.dequeueOutputBuffer(…);
14    if (outputBufferId >= 0) {
15      // outputBuffers[outputBufferId] is ready to be processed or rendered.
16      …
17      codec.releaseOutputBuffer(outputBufferId, …);
18    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
19      outputBuffers = codec.getOutputBuffers();
20    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
21      // Subsequent data will conform to new format.
22      MediaFormat format = codec.getOutputFormat();
23    }
24  }
25  codec.stop();
26  codec.release();      

  流結束處理(End-of-stream Handling)

  當到達輸入資料結尾時,你必須在調用queueInputBuffer方法中通過指定BUFFER_FLAG_END_OF_STREAM标記來通知編解碼器。你可以在最後一個有效的輸入buffer上做這些操作,或者送出一個額外的以end-of-stream标記的空的輸入buffer。如果使用一個空的buffer,它的時間戳将被忽略。

  編解碼器将會繼續傳回輸出buffers,直到它發出輸出流結束的信号,這是通過指定dequeueOutputBuffer方法中MediaCodec.BufferInfo的end-of-stream标記來實作的,或者是通過回調方法onOutputBufferAvailable來傳回end-of-stream标記。可以在最後一個有效的輸出buffer中設定或者在最後一個有效的輸出buffer後添加一個空的buffer來設定,這種空的buffer的時間戳應該被忽略。

  當通知輸入流結束後不要再送出額外的輸入buffers,除非編解碼器被重新整理或停止或重新開機。

  使用一個輸出表面(Using an Output Surface)

  使用一個輸出Surface進行資料處理的方法與ByteBuffer模式幾乎是相同的,然而,輸出buffers不再可通路,而且被表示為null值。E.g.方法getOutputBuffer/Image(int)将傳回null,方法getOutputBuffers()将傳回僅包含null值的數組。

  當使用一個輸出Surface時,你能夠選擇是否渲染surface上的每一個輸出buffer,你有三種選擇:

  • 不要渲染這個buffer(Do not render the buffer):通過調用releaseOutputBuffer(bufferId, false)。
  • 使用預設的時間戳渲染這個buffer(Render the buffer with the default timestamp):調用releaseOutputBuffer(bufferId, true)。
  • 使用指定的時間戳渲染這個buffer(Render the buffer with a specific timestamp):調用 releaseOutputBuffer(bufferId, timestamp)。

  從Android6.0(M)開始,預設的時間戳是buffer的presentation timestamp(轉換為納秒)。在此前的版本中這是沒有被定義的。

  而且,從Android6.0(M)開始,你可以通過使用setOutputSurface方法動态地改變輸出Surface。

  使用一個輸入表面(Using an Input Surface)

  當使用輸入Surface時,将沒有可通路的輸入buffers,因為這些buffers将會從輸入surface自動地向編解碼器傳輸。調用dequeueInputBuffer時将抛出一個IllegalStateException異常,調用getInputBuffers()将要傳回一個不能寫入的僞ByteBuffer[]數組。

  調用signalEndOfInputStream()方法發送end-of-stream信号。調用這個方法後,輸入surface将會立即停止向編解碼器送出資料。

查詢&自适應播放支援(Seeking & Adaptive Playback Support)

  視訊解碼器(通常指處理壓縮視訊資料的編解碼器)關于搜尋-seek和格式轉換(不管它們是否支援)表現不同,且被配置為adaptive playback。你可以通過調用CodecCapabilities.isFeatureSupported(String)方法來檢查解碼器是否支援adaptive playback 。支援Adaptive playback的解碼器隻有在編解碼器被配置在Surface上解碼時才被激活。

  流域界與關鍵幀(Stream Boundary and Key Frames)

  在調用start()或flush()方法後,輸入資料在合适的流邊界開始是非常重要的:其第一幀必須是關鍵幀(key-frame)。一個關鍵幀能夠獨立地完全解碼(對于大多數編解碼器它意味着I-frame),關鍵幀之後顯示的幀不會引用關鍵幀之前的幀。

  下面的表格針對不同的視訊格式總結了合适的關鍵幀。

Android多媒體--MediaCodec 中文API文檔

  對于不支援adaptive playback的解碼器(包括解碼到Surface上解碼器)

  為了開始解碼與先前送出的資料(也就是seek後)不相鄰的資料你必須重新整了解碼器。由于所有輸出buffers會在flush的一刻立即撤銷,你可能希望在調用flush方法前等待這些buffers首先被标記為end-of-stream。在調用flush方法後輸入資料在一個合适的流邊界或關鍵幀開始是非常重要的。

  注意:flush後送出的資料的格式不能改變;flush()方法不支援格式的不連續性;為此,一個完整的stop()-configure(...)-start()的過程是必要的。

  同時注意:如果你調用start()方法後過快地重新整理編解碼器,通常,在收到第一個輸出buffer或輸出format變化前,你需要向這個編解碼器再次送出codec-specific-data。具體檢視codec-specific-data部分以獲得更多資訊。

  對于支援及被配置為adaptive playback的幾碼器

  為了開始解碼與先前送出的資料(也就是seek後)不相鄰的資料,你沒有必要重新整了解碼器;然而,在間斷後傳入的資料必須開始于一個合适的流邊界或關鍵幀。

  針對一些視訊格式-也就是H.264、H.265、VP8和VP9,也可以修改圖檔大小或者配置mid-stream。為了做到這些你必須将整個新codec-specific配置資料與關鍵幀一起打包到一個單獨的buffer中(包括所有的開始資料),并将它作為一個正常的輸入資料送出。

  在picture-size被改變後以及任意具有新大小的幀傳回之前,你可以從dequeueOutputBuffer方法或onOutputFormatChanged回調中得到 INFO_OUTPUT_FORMAT_CHANGED的傳回值。

  注意:就像使用codec-specific data時的情況,在你修改圖檔大小後立即調用fush()方法時需要非常小心。如果你沒有接收到圖檔大小改變的确認資訊,你需要重試修改圖檔大小的請求。

錯誤處理(Error handling)

  工廠方法createByCodecName以及createDecoder/EncoderByType會在建立codec失敗時抛出一個IOException,你必須捕獲異常或聲明向上傳遞異常。在編解碼器不允許使用該方法的狀态下調用時,MediaCodec方法将會抛出IllegalStateException異常;這種情況一般是由于API接口的不正确調用引起的。涉及secure buffers的方法可能會抛出一個MediaCodec.CryptoException異常,可以調用getErrorCode()方法獲得更多的異常資訊。

  内部的編解碼器錯誤将導緻MediaCodec.CodecException,這可能是由于media内容錯誤、硬體錯誤、資源枯竭等原因所緻,即使你已經正确的使用了API。當接收到一個CodecException時,可以調用isRecoverable()和isTransient()兩個方法來決定建議的行為。

  • 可恢複錯誤(recoverable errors):如果isRecoverable() 方法傳回true,然後就可以調用stop(),configure(...),以及start()方法進行修複。
  • 短暫錯誤(transient errors):如果isTransient()方法傳回true,資源短時間内不可用,這個方法可能會在一段時間之後重試。
  • 緻命錯誤(fatal errors):如果isRecoverable()和isTransient()方法均傳回fase,CodecException錯誤是緻命的,此時就必須reset這個編解碼器或調用released方法釋放資源。

  isRecoverable()和isTransient()方法不可能同時都傳回true。

合法的API調用和API曆史(Valid API Calls and API History)

  下面的表格總結了MediaCodec中合法的API以及API曆史版本。更多的API版本号詳見Build.VERSION_CODES。

  此部分并無卵用,故省略之

嵌套類(Nested classes)

Nested classes--嵌套類(内部類)
class

MediaCodec.BufferInfo

 每一個緩存區的中繼資料都包含有一個偏移量offset和大小size用于訓示相關編解碼器(輸出)緩存中有效資料的範圍。

MediaCodec.Callback

MediaCodec回調接口。

MediaCodec.CodecException

當發生内部的編解碼器錯誤是抛出。

MediaCodec.CryptoException

在入隊列一個安全的輸入緩存過程中發生加密錯誤時抛出。

MediaCodec.CryptoInfo

描述(至少部分地)加密的輸入樣本的結構的中繼資料。
interface

MediaCodec.OnFrameRenderedListener

當一個輸出幀在輸出surface上呈現時,監聽器被調用。

 常量(Constants)

Constants--常量 
int

BUFFER_FLAG_CODEC_CONFIG

這表示帶有此标記的緩存包含編解碼器初始化或編解碼器特定的資料而不是多媒體資料media data。

常量值:2(0x00000002)

BUFFER_FLAG_END_OF_STREAM

它表示流結束,該标志之後不會再有可用的buffer,除非接下來對Codec執行flush()方法。

常量值:4(0x00000004)

BUFFER_FLAG_KEY_FRAME

這表示帶有此标記的(編碼的)緩存包含關鍵幀資料。

常量值:1(0x00000001)

BUFFER_FLAG_SYNC_FRAME

這個常量在API level 21中棄用,使用BUFFER_FLAG_KEY_FRAME代替。

CONFIGURE_FLAG_ENCODE

如果編解碼器被用作編碼器,傳遞這個标志。

CRYPTO_MODE_AES_CBC
CRYPTO_MODE_AES_CTR

CRYPTO_MODE_UNENCRYPTED

常量值:0(0x00000000)

INFO_OUTPUT_BUFFERS_CHANGED
INFO_OUTPUT_FORMAT_CHANGED
INFO_TRY_AGAIN_LATER
String PARAMETER_KEY_REQUEST_SYNC_FRAME
PARAMETER_KEY_SUSPEND
PARAMETER_KEY_VIDEO_BITRATE
VIDEO_SCALING_MODE_SCALE_TO_FIT
VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING

  

*

公有方法(Public methods)

configure

void configure (MediaFormat format, Surface surface, MediaCrypto crypto, int flags)

配置一個元件。

參數說明
format MediaFormat : 輸入資料的格式(解碼器)或期望的輸出資料的格式(編碼器)。format為null時等價于empty format.
surface Surface : 指定一個surface用于顯示解碼器的輸出。如果codec沒有産生raw video output(不是一個視訊解碼器),或者把codec配置為ByteBuffer輸出模式時,surface值為null。
crypto MediaCryto : 指定一個cryto對象實作媒體資料的安全解密,cryto為null時是non-secure codecs
flags int : 指定為CONFIGURE_FLAG_ENCODE時将該元件配置為編碼器
抛出異常
IllegalArgumentException surface已經釋放(或非法),或format不可接受(e.g. 丢失了強制秘鑰),或flags設定不合适(e.g. encoder時忽略了CONFIGURE_FLAG_ENCODE)
IllegalStateException 不在未初始化狀态
MediaCodec.CryptoException DRM錯誤

MediaCodec.CodecException

Codec錯誤

Start

void start()

成功地配置元件後,調用start方法。

如果codec在異步模式下被配置且處于flushed狀态,為處理要求的的輸入buffer,也需調用start方法。

如果codec不處于configured狀态或異步模式下的codec執行flush()方法後,調用start()方法抛出該異常
MediaCodec.CodecException codec錯誤。注意對于start時的一些codec error可能是由于後續方法調用引起的。

dequeueInputBuffer

int dequeueInputBuffer(long timeoutUs)

傳回一個填充了有效資料的input buffer的索引,如果沒有可用的buffer則傳回-1.當timeoutUs==0時,該方法立即傳回;當timeoutUs<0時,無限期地等待一個可用的input buffer;當timeoutUs>0時,至多等待timeoutUs微妙。

timeoutUs long : 微妙機關,負值代表無限期。
傳回值
如果codec不在Executing狀态,或者codec處于異步模式。
codec錯誤

queueInputBuffer(沒完全了解)

void queueInputbuffer(int index, int offset, int size, long presentationtimeUs, int flags)

給指定索引的input buffer填充資料後,将其送出給codec元件。一旦一個input buffer在codec中排隊,它就不再可用直到通過getInputBuffer(int)重新擷取,getInputBuffer(int)是對dequeueInputbuffer(long)的傳回值或onInputBufferAvailable(MediaCodec, int)回調的響應。

許多解碼器要求實際壓縮資料流以“codec specific data”為先導,也就是用于初始化codec的設定資料,例如AVC視訊情況時的PPS/SPS,或vorbis音頻情況時的code tables。MediaExtractor類提供codec specific data作為傳回的track format的一部分,在命名為csd-0,csd-1的條目中。

通過指定BUFFER_FLAG_CODEC_CONFIG,這些buffers可以在start()或flush()後直接送出。然而,如果你使用包含這些keys的MediaFormat配置codec,他們将在start後自動地送出。是以,不鼓勵使用BUFFER_FLAG_CODEC_CONFIG,僅推薦進階使用者使用。

為了訓示輸入資料的最後一塊(或除非decoder flush否則不再有輸入資料)需指定DUFFER_FLAG_END_OF_STREAM。

  注意:android6.0(M)之前,presentationTimeUs不會傳遞到Surface output buffer 的幀的時間戳,這會導緻幀的時間戳沒有定義。使用releaseOutputBuffer(int, long)方法確定一個指定的時間戳被設定。同樣地,盡管frame timestamp可以被destination surface用于同步渲染,必須注意presentationTimeUs正常化,以便不被誤認為是一個系統時間。

index int : 調用dequeueInputBuffer(long)方法傳回的持有者所有的input buffer的索引。
offset int : input buffer中資料起始位置的位元組偏移量。
size int : 有效輸入資料的位元組數。
presentationTimeUs long : 這個buffer送出呈現的時間戳(微妙),它通常是指這個buffer應該送出或渲染的media time。當使用output surface時,将作為timestamp傳遞給frame(轉換為納秒後)。
int : BUFFER_FLAG_CODEC_CONFIG和BUFFER_FLAG_END_OF_STREAM的位掩碼。當沒有禁止時,大多數codecs不會在input buffer中使用BUFFER_FLAG_KEY_FRAME。
如果沒有在Executing狀态
如果cryto對象已經在configure(MediaFormat, Surface, MediaCryto, int)中指定。

release

void release()

釋放codec執行個體使用的資源。釋放任何開放的元件執行個體時調用該方法,而不是依賴于垃圾回收機制在以後某個時間完成資源的釋放。

reset

void reset()

使codec傳回到初始(未初始化)狀态。如果發生了不可恢複的錯誤,調用該方法使codec複位到建立時的初始狀态。

如果codec發生了不可恢複的錯誤且codec不能reset.
如果codec處于released狀态。

stop

void stop()

完成解碼/編碼任務後,需注意的是codec任然處于活躍狀态且準備重新start。為了確定其他client可以調用release()方法,且不僅僅隻依賴于garbage collection為你完成這些工作。

如果codec處于Released狀态。

flush

void flush()

沖洗元件的輸入和輸出端口。

傳回時,所有之前通過調用dequeueInputBuffer方法和dequeueOutputBuffer方法或通過onInputBufferAvailable和onOutputBufferAvailable回調獲得的索引會失效。所有的buffers都屬于codec。

如果codec被配置為異步模式,flush後調用start()重新開始codec操作。編解碼器不會請求輸入緩沖區,直到這已經發生了。

如果codec配置為同步模式,配置時使用了一個input buffer時codec會自動重新開始,否則,當調用dequeueInputBuffer時重新開始。

如果codec沒有處于Executing狀态

微信掃一掃,關注玖零日記,擷取更多相關資訊及源碼 -- 雖無面朝大海,依舊春暖花開

Android多媒體--MediaCodec 中文API文檔

心有猛虎,細嗅薔薇,生活就該無懼無悔

----------------------------------------------------------------------------

作者:二的次方

出處:https://www.cnblogs.com/roger-yu/p/5635494.html

本文版權歸作者和部落格園共有,轉載必須給出原文連結,并保留此段聲明,否則保留追究法律責任的權利