MediaCodec|文档翻译及用法

  • Post author:
  • Post category:其他


MediaCodec|文档翻译

classoverView

mediacodec类可以用来调用系统底层的编码/解码软件。

@Override

public void output(boolean isEos) {


String tag = TAG + “-output”;

ByteBuffer[] outputBuffers = null;

int count = 0;

int outputIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);

try{


outputBuffers = mMediaCodec.getOutputBuffers();

do{


if(outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER){


Log.i(tag, “output from encoder not available”);

if(!isEos){


count++;

if(count >= 5){


Log.i(tag, “output from encoder not available and break===========”);

break;

}

}

}else if(outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){


outputBuffers = mMediaCodec.getOutputBuffers();

Log.i(tag, “encoder output buffers changed”);

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


//在音视频混合器中加入视频/音频轨道

addTrack();

Log.i(tag, “encoder output format change”);

}else if(outputIndex < 0){


Log.e(tag, “output buffer wrong ” + outputIndex);

}else{


ByteBuffer outputBuffer = outputBuffers[outputIndex];

if(outputBuffer == null){


Log.e(tag, “output buffer null”);

return;

}

if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0){


// The codec config data was pulled out and fed to the muxer when we got

// the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.

mBufferInfo.size = 0;

}

Log.d(tag, “buffer size=” + mBufferInfo.size + ” pts=” + mBufferInfo.presentationTimeUs);

if(mBufferInfo.size != 0){


if(!mMuxer.isVideoTrackAdd()){


addTrack();

}

if(!mMuxer.isStarted() && mMuxer.isPrepared()){


mMuxer.start();

}

if(mMuxer.isStarted()){


outputBuffer.position(mBufferInfo.offset);

outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);

//写入指定轨道的数据

mMuxer.writeSampleData(mTrackIndex, outputBuffer, mBufferInfo);

}

}

mMediaCodec.releaseOutputBuffer(outputIndex, false);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {


// 停止编码器

Log.d(tag, “output: eos coming”);

mIsRecording = false;

release();

break;      // out of while

}

}

outputIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);

}while (outputIndex >= 0);

}catch (Exception e){


}

}

//加入轨道

protected void addTrack() {


mMediaFormat = mMediaCodec.getOutputFormat();

mTrackIndex = mMuxer.addTrack(mMediaFormat, mMediaType);

}

@Override

public void release() {


if(!mIsRecording){


mMuxer.eos(mTrackIndex);

mMediaCodec.release();

}

}

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_FRAME




,




BUFFER_FLAG_CODEC_CONFIG









BUFFER_FLAG_END_OF_STREAM



选一个,或者0~

Throws




MediaCodec.CryptoException


如果在




configure(MediaFormat, Surface, MediaCrypto, int)



被加密则抛出异常

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_FRAME




,




BUFFER_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()