MediaCodec文檔翻譯&&一些FAQ和例子


前段時間項目需要,接觸到android 4.0以后新添加的mediacodec類,並用mediacodec類實現了一個無縫播放視頻的播放器,並用這個播放器簡單的實現了dash協議。

接觸到這么多,感覺GOOGLE給的文檔寫的不夠詳細,網絡上也沒有一個標准的例子,為了避免后來人走一些不必要的彎路,我在這里打算先用中文翻譯一下codec的文檔,然后再把自己在實現播放器時候的一些注意事項和問題列出來。

首先這篇文章是給半新手看的,你得有一定的安卓基礎,但是由於作者本人的水平有限,也不是什么高手,所以有的地方會說的不清楚,還請包涵。

給技術牛人推薦一個 mediacodec 例子很多的網站:http://bigflake.com/mediacodec/

感覺像是官方人員編寫的,里面有各種關於mediacodec的例子,還有4.3最新的muxer,都是以CTS的形式寫的,本人沒有接觸過,但是參考代碼還是很好的,畢竟實現方法都差不多。

 

MediaCodec|文檔翻譯

  classoverView

 mediacodec類可以用來調用系統底層的編碼/解碼軟件。

 

  mediacodec一般是這么用的:

MediaCodec codec = MediaCodec.createDecoderByType(type);
 codec.configure(format, ...);
 codec.start();
 ByteBuffer[] inputBuffers = codec.getInputBuffers();
 ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 for (;;) {
   int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
   if (inputBufferIndex >= 0) {
     // fill inputBuffers[inputBufferIndex] with valid data
     ...
     codec.queueInputBuffer(inputBufferIndex, ...);
   }

   int outputBufferIndex = codec.dequeueOutputBuffer(timeoutUs);
   if (outputBufferIndex >= 0) {
     // outputBuffer is ready to be processed or rendered.
     ...
     codec.releaseOutputBuffer(outputBufferIndex, ...);
   } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
     outputBuffers = codec.getOutputBuffers();
   } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
     // Subsequent data will conform to new format.
     MediaFormat format = codec.getOutputFormat();
     ...
   }
 }
 codec.stop();
 codec.release();
 codec = null;

//等會再逐一介紹上述代碼每一句的作用

 

每一個Codec類都擁有input buffer和outputbuffer,這些buffer是通過API提供的函數按索引調用(Index),這些buffer的格式是bytebuffer,實際上inputbuffer和outputbuffer都是bytebuffer的數組,用getInputBuffer和getOutputBuffer可以得到bytebuffer的調用。在成功調用Start()函數后,對象(原文是Client,在這里我理解為MediaCodec的實例化對象)並沒有“擁有”input和output buffer,相應的,可以通過調用dequeueInputBuffer(long) 和dequeueOutputBuffer(MediaCodec.BufferInfo, long)把buffer的所有權從codec編解碼器交換給對象。MediaCodec對象沒有必要立刻把buffer傳遞給解碼器或者釋放到surface,上面的代碼僅僅是一個簡單的例子。

 

當MediaCodec對象有一個可用的input buffer時候,可以調用 queueInputBuffer(int, int, int, long, int)函數把buffer傳遞給解碼器,當然,buffer里需要有數據。

 

解碼器會相應的調用 dequeueOutputBuffer(MediaCodec.BufferInfo, long)返回一個output buffer,如果繼續調用 releaseOutputBuffer(int, boolean) ,這個output buffer將被返回到解碼器中,如果再 configure(MediaFormat, Surface, MediaCrypto, int)的時候傳入了一個videoSurface(一般是SurfaceView).那么這個這個output buffer的內容將會顯示在surface上。

 

 

不管是用於解碼器的input buffer還是編碼器的output buffer ,其中都包含了編碼好的多媒體數據.對於視頻格式的文件來說,buffer里包含的是一個短暫的時間片(比如一幀),對於音頻格式的文件,可能是一個多幀的聲音片段。不管哪種情況,buffer里的數據並不是任意字節邊界的二進制數據,buffer里存儲的不是數據流,而是一個元單元(access units)流。

 

很多媒體格式還需要多媒體文件頭數據(這些數據一般是由一些包含set up data的buffer組成),或者編碼器需要的特定數據。因此,最初傳遞給解碼器的buffer必須是帶有BUFFER_FLAG_CODEC_CONFIG下標的編碼特定數據,一般這個下標是由queueInputBuffer(int, int, int, long, int)生成的。編碼器需要的數據,包括媒體格式信息,都通過調用 configure(MediaFormat, Surface, MediaCrypto, int) (in ByteBuffer entries with keys "csd-0", "csd-1", 括號里這句不知道怎么翻譯),自動傳遞給了codec類,不需要對象自己把格式信息傳給codec(也就是configure會自動傳遞帶有BUFFER_FLAG_CODEC_CONFIG的buffer和媒體格式信息給編碼器)。在輸入數據的末尾,對象會通過queueInputBuffer(int, int, int, long, int)函數發送一個帶有BUFFER_FLAG_END_OF_STREAM 下標的信號。為了解碼與之前數據無關的數據(例如執行一個seek),解碼器需要調用flush()。在調用flush()后,對象里的inputbuffer或者outputbuffer都會清空,也就是說對象不再有任何buffer,但是數據的媒體格式信息不會改變,如果需要改變媒體格式信息,依次調用stop,configure,start.

Summary


Nested Classes
class MediaCodec.BufferInfo .包含每一個buffer的元數據信息,例如偏差,在相關解碼器中有效的數據大小
class MediaCodec.CryptoException Thrown when a crypto error occurs while queueing a secure input buffer. (沒用到,不翻譯
class MediaCodec.CryptoInfo Metadata describing the structure of a (at least partially) encrypted input sample. (同上
Constants
int BUFFER_FLAG_CODEC_CONFIG 這個下標表明該buffer是解碼需要信息,不是多媒體的有效數據
int BUFFER_FLAG_END_OF_STREAM 表明一個文件的末尾
int BUFFER_FLAG_SYNC_FRAME This indicates that the buffer marked as such contains the data for a sync frame.
int CONFIGURE_FLAG_ENCODE 如果codec類被用於編碼器,傳遞這個flag
int CRYPTO_MODE_AES_CTR  
int CRYPTO_MODE_UNENCRYPTED  
int INFO_OUTPUT_BUFFERS_CHANGED output buffer有改動,對象必須重新調用getoutputbuffer()來獲取改動后的buffer引用。
int INFO_OUTPUT_FORMAT_CHANGED output的格式改變,接下來的有效數據輸出將遵循新的格式
int INFO_TRY_AGAIN_LATER 如果調用dequeueOutputBuffer(MediaCodec.BufferInfo, long)返回一個non-negative timeout flag, 標明執行函數超時,將不會繼續等待。
String PARAMETER_KEY_REQUEST_SYNC_FRAME Request that the encoder produce a sync frame "soon".
String PARAMETER_KEY_SUSPEND Temporarily suspend/resume encoding of input data.
String PARAMETER_KEY_VIDEO_BITRATE Change a video encoder's target bitrate on the fly.
int VIDEO_SCALING_MODE_SCALE_TO_FIT The content is scaled to the surface dimensions
int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING The content is scaled, maintaining its aspect ratio, the whole surface area is used, content may be cropped
Public Methods
void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
配置一個組件
static MediaCodec createByCodecName(String name)
如果你知道組件的具體名字,可以用這個函數創建那個組件(具體參照,系統注冊的組件機制)
static MediaCodec createDecoderByType(String type)
通過多媒體格式名創建一個可用的解碼器
static MediaCodec createEncoderByType(String type)
同上,創建的是一個編碼器
final Surface createInputSurface()
Requests a Surface to use as the input to an encoder, in place of input buffers.(請求一個surface用於編碼器的輸入,而不是常用的inputbuffer輸入)
final int dequeueInputBuffer(long timeoutUs)
(返回一個inputbuffer的索引用來填充數據,返回-1表示暫無可用buffer)
final int dequeueOutputBuffer(MediaCodec.BufferInfo info, long timeoutUs)
排一個輸出buffer,如果等待 timeoutUs時間還沒響應則跳過,返回TRY_AGAIN_LATER
final void flush()
flush組件的input和output接口, 之前調用  dequeueInputBuffer(long) 和 dequeueOutputBuffer(MediaCodec.BufferInfo, long) 排好的buffer都變成不可用。
MediaCodecInfo getCodecInfo()
獲得編碼器的信息。
ByteBuffer[] getInputBuffers()
Call this after start() returns.
final String getName()
獲得組件的名字。
ByteBuffer[] getOutputBuffers()
Call this after start() returns and whenever dequeueOutputBuffer signals an output buffer change by returning INFO_OUTPUT_BUFFERS_CHANGED
final MediaFormat getOutputFormat()
在dequeueOutputBuffer 返回  INFO_OUTPUT_FORMAT_CHANGED信息后調用,可以查看當前媒體格式信息。
final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags)
在給指定Index的inputbuffer[]填充數據后,調用這個函數把數據傳給解碼器
final void queueSecureInputBuffer(int index, int offset, MediaCodec.CryptoInfo info, long presentationTimeUs, int flags)
和  queueInputBuffer(int, int, int, long, int) 相信,但是傳入的 buffer 是加密了的
final void release()
在你調用這個函數的時候,確保釋放了所以不需要用的組件,而不是依賴gc為你做這些事
final void releaseOutputBuffer(int index, boolean render)
如果你對outputbuffer的處理完后,調用這個函數把buffer重新返回給codec類。
final void setParameters(Bundle params)
為組件實例設置一些可選參數。
final void setVideoScalingMode(int mode)
如果在  configure(MediaFormat, Surface, MediaCrypto, int)傳入了surface,可以通過這個函數來設置顯示模式。 
final void signalEndOfInputStream()
表明輸入流的結束。
final void start()
如果成功配置組件,調用start來開啟
final void stop()
停止編碼/解碼, 可以通過調用start()來重啟。

 

Public Methods


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

Added in  API level 16

配置一個組件。

Parameters
format 如果為解碼器,此處表示輸入數據的格式;如果為編碼器,此處表示輸出數據的格式。
surface 指定一個surface,可用作decode的輸出渲染。
crypto 如果需要給媒體數據加密,此處指定一個crypto類.
flags 如果正在配置的對象是用作編碼器,此處加上CONFIGURE_FLAG_ENCODE 標簽。

public static MediaCodec createByCodecName (String name)

Added in  API level 16

如果你知道你想實例化的組件確切名字, 用這個方法來初始化它,謹慎使用這個函數,最好配合從 MediaCodecList獲得的信息來使用。

Parameters
name 需要實例化的組件的名字.

public static MediaCodec createDecoderByType (String type)

Added in  API level 16

用給定的媒體格式來創建一個解碼器.下面是部分媒體格式和他們所對應的key的列表:

  • "video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm)
  • "video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm)
  • "video/avc" - H.264/AVC video
  • "video/mp4v-es" - MPEG4 video
  • "video/3gpp" - H.263 video
  • "audio/3gpp" - AMR narrowband audio
  • "audio/amr-wb" - AMR wideband audio
  • "audio/mpeg" - MPEG1/2 audio layer III
  • "audio/mp4a-latm" - AAC audio (note, this is raw AAC packets, not packaged in LATM!)
  • "audio/vorbis" - vorbis audio
  • "audio/g711-alaw" - G.711 alaw audio
  • "audio/g711-mlaw" - G.711 ulaw audio

 

Parameters
type 輸入數據的多媒體格式.

public static MediaCodec createEncoderByType (String type)

Added in  API level 16

按給定的媒體類型創建一個編碼器。

Parameters
type 所需要的輸出媒體格式.

public final Surface createInputSurface ()

Added in  API level 18

請求一個surface作為解碼器的輸入(替代掉inputbuffer?).這個函數需要在  configure(MediaFormat, Surface, MediaCrypto, int) 之后 start()之前被調用。

程序應該自己調用release去釋放這個 surface而不是等gc 去做。

public final int dequeueInputBuffer (long timeoutUs)

Added in  API level 16

返回一個可用來填充有效數據的inputbuffer的索引(Index),如果返回-1表示暫無可用的buffer. 如果 timeoutUs == 0,該方法會立刻返回值, 如果 timeoutUs < 0 則一直等待,如果 timeoutUs > 0則等待對應時間.

Parameters
timeoutUs 單位為微秒, 負數表示無窮大.

public final int dequeueOutputBuffer (MediaCodec.BufferInfo info, long timeoutUs)

Added in  API level 16

排一個output buffer, 等待“timeoutUs"后若無有效值則阻塞,單位為 microseconds. 返回成功解碼的outputbuffer的索引 或者 一個 INFO_* constants 常量.

Parameters
info buffer的一些信息
timeoutUs 單位為微秒, 負數表示無窮大.

public final void flush ()

Added in  API level 16

flush,重洗組件的input和output接口, 之前調用 dequeueInputBuffer(long) 和dequeueOutputBuffer(MediaCodec.BufferInfo, long) 排好的buffer都變成不可用。

public MediaCodecInfo getCodecInfo ()

Added in  API level 18

獲得codec的信息.如果codec是 createDecoderByType 或createEncoderByType創建的, 事先並不知道用的什么組件,那么 調用這個函數不會返回CodecInfo。

public ByteBuffer[] getInputBuffers ()

Added in  API level 16

在start()之后可調用這個函數獲得inputbuffer

public final String getName ()

Added in  API level 18

獲得這個組件的名字. 

public ByteBuffer[] getOutputBuffers ()

Added in  API level 16

Call this after start() returns and whenever dequeueOutputBuffer signals an output buffer change by returning INFO_OUTPUT_BUFFERS_CHANGED

這句話不好翻譯,大意是在start()調用,或者在dequeueOutputBuffer 返回 INFO_OUTPUT_BUFFERS_CHANGED信息后調用。

public final MediaFormat getOutputFormat ()

Added in  API level 16

如果 dequeueOutputBuffer 返回INFO_OUTPUT_FORMAT_CHANGED ,調用這個函數。

public final void queueInputBuffer (int index, int offset, int size, long presentationTimeUs, int flags)

Added in  API level 16

再給指定索引的inputbuffer填充完數據后,把它交給編碼器. 很多解碼器需要媒體文件的文件頭,例如 vorbis audio中的編碼表 ,AVC video中的PPS/SPS,. MediaExtractor 類提供了codec所需要的多媒體格式信息 ... 這些buffer應該加上 BUFFER_FLAG_CODEC_CONFIG標簽. 如果這是最后一個輸入數據 (接下來沒有其他的數據輸入,除非馬上要調用flush()) 則應該加上 BUFFER_FLAG_END_OF_STREAM標簽.

Parameters
index 前面由 調用 dequeueInputBuffer(long)返回的index
offset The byte offset into the input buffer at which the data starts. 可以理解為有效數據開始的偏差,一般為0
size 輸入的有效數據的大小
presentationTimeUs 這個buffer被渲染的時間(一般由extractor.getsampleTime獲得)
flags 根據需要從 BUFFER_FLAG_SYNC_FRAMEBUFFER_FLAG_CODEC_CONFIG , BUFFER_FLAG_END_OF_STREAM選一個,或者0~

public final void queueSecureInputBuffer (int index, int offset, MediaCodec.CryptoInfo info, long presentationTimeUs, int flags)

Added in  API level 16

與 queueInputBuffer(int, int, int, long, int)相似,但是 傳入的是一個被加密的buffer。

Parameters
index The index of a client-owned input buffer previously returned in a call to dequeueInputBuffer(long).
offset The byte offset into the input buffer at which the data starts.
info Metadata required to facilitate decryption, the object can be reused immediately after this call returns.
presentationTimeUs The time at which this buffer should be rendered.
flags A bitmask of flags BUFFER_FLAG_SYNC_FRAMEBUFFER_FLAG_CODEC_CONFIG or BUFFER_FLAG_END_OF_STREAM.
Throws
MediaCodec.CryptoException if an error occurs while attempting to decrypt the buffer. An error code associated with the exception helps identify the reason for the failure.

public final void release ()

Added in  API level 16

在你調用這個函數的時候,確保釋放了所以不需要用的組件,而不是依賴gc為你做這些事

public final void releaseOutputBuffer (int index, boolean render)

Added in  API level 16

如果你處理完這個buffer, 調用這個函數把buffer重新返回給codec. 如果你在之前configure()的時候為組件配置了一個surface,那么codec類會在那個surface上顯示這個buffer

Parameters
index 由codec對象擁有的,由 dequeueOutputBuffer(MediaCodec.BufferInfo, long)返回的索引Index.
render 如果有可用的顯示surface,傳遞為true可表示顯示。

public final void setParameters (Bundle params)

Added in  API level 19

可以給組件加上一些附加參數(具體參數文檔沒寫,估計是仍在測試階段)

public final void setVideoScalingMode (int mode)

Added in  API level 16

如果在 configure(MediaFormat, Surface, MediaCrypto, int)傳入了surface,可以通過這個函數來設置顯示模式。 

public final void signalEndOfInputStream ()

Added in  API level 18

標志輸入流的結束。 作用於傳遞 BUFFER_FLAG_END_OF_STREAM 下標相同. 但是這個函數只用在把createInputSurface()返回的surface作為輸入,且codec是編碼器的情況。

public final void start ()

Added in  API level 16

在成功配置組件后, 調用start()函數。相應的可以通過函數 操作inputbuffer和outputbuffer。

public final void stop ()

Added in  API level 16

停止,但是可以調用start()重開。如果要完全停止,調用release() 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM