`
xiandaoyan
  • 浏览: 21470 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Android MediaCodec解码流程分析

阅读更多

MediaCodec解码过程

1解码流程

解码流程大致分为创建解码器,配置解码器,解码操作,释放解码器。

1.1创建解码器

创建解码器可以直接创建默认的,也可以通过两种方式来创建,分别是:视频类型和解码器名。

指定视频类型的创建接口是:createDecoderByType(type)示例代码:

 

try {

    mCodec = MediaCodec.createDecoderByType("video/avc");

} catch (IOException e) {

    throw new RuntimeException("Failed to create codec", e);

}
 

 

指定解码器创建接口是:createByCodecName(decoderName)示例代码:

 

try {

    mCodec = MediaCodec.createByCodecName("OMX.ffmpeg.h264.decoder");

} catch (IOException e) {

    throw new RuntimeException("Failed to create codec", e);

}
 

 

这一步调用了MediaCodecprivate构造函数创建了MediaCodec对象,并调用native_setup来初始化native_setup<->android_media_MediaCodec_native_setup,这里创建JMediaCodec对象,JMediaCodec实例对应的接口调到真正的MediaCodec.cpp相关借口,真正实现编解码相关流程是在MediaCodec.cpp中实现。

1.2配置解码器

配置解码器需要构造一个MediaFormat对象作为参数,MediaFormat需要指定视频的格式,widthheight,以及sps,这个是编码时产生的第一帧数据,配置帧csd-0。如果需要将解码后的数据直接输出到Surface,则需要传入一个Surface对象。示例代码:

 

MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);

// little tricky here, csd-0 is required in order to configure the codec properly

// it is basically the first sample from encoder with flag: BUFFER_FLAG_CODEC_CONFIG

format.setByteBuffer("csd-0", csd0);

mCodec.configure(format, surface, null, 0);
 

 

这一步会把MediaFormat所携带的各种信息,通过native_configure方法传递到JNI层,对应android_media_MediaCodec.cpp的方法是android_media_MediaCodec_native_configure该接口会调用到MediaCodec.cppconfigure,完成解码器的配置。大致流程如下

configure(format, surface, null, 0)(MediaCodec.java)

    ->native_configure(keys, values, surface, crypto, descramblerBinder, flags)(MediaCodec.java)

          ->android_media_MediaCodec_native_configure(android_media_MediaCodec.cpp)

          ->JMediaCodec.configure(format, bufferProducer, crypto, descrambler, flags)(android_media_MediaCodec.cpp)

                ->configure(format, mSurfaceTextureClient, crypto, descrambler, flags)

(MediaCodec.cpp)

mCodecMediaCodec.cpp实例进入MediaCodecconfigure函数,只能配置一次,如果需要重新配置需要调用stop接口,让解码器进入Uninitialized状态,才可以进行重新配置,不然设置会报错误码-38MediaFormat对象在MediaCodec.cppconfigure函数中,被重新封装成一个AMessage对象。

本步骤结束后,解码器进入Configured状态。

1.3解码操作

在解码器配置号以后,可以调用mCodec.start()接口,Codec会转入 Executing 状态startCodec立即进入 Flushed 子状态,此时的Codec拥有所有的input and output buffersClient无法操作这些buffers Executing状态下可以调用 MediaCodec.flush()方法使MediaCodec进入 Flushed 子状态

解码分为两个平行的过程,1.填充已编码的数据;2.获取已解码的数据。一般会分为两个线程来实现,一个填充线程,一个消费线程,当然也支持一个线程搞定,先填充后解码,一帧一帧的处理(我们在云手机项目上的相机就是使用的该方案)。

1.3.1填充数据

Client通过调用 MediaCodec.dequeueInputBuffer(...)请求得到了一个有效的input buffer indexCodec进入到 Running 子状态这个状态下Codec会进行实际的数据处理获取到bufferindex之后,需要调用MediaCodec.getInputBuffer(index)来直接获取到ByteBuffer,然后把需要解码的数据填充到ByteBuffer里,再调用MediaCodec.queueInputBuffer(...)进行解码。实例代码:

int index = mCodec.dequeueInputBuffer(mTimeoutUs);

if (index >= 0) {

ByteBuffer buffer;

// since API 21 we have new API to use

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {

buffer = mCodec.getInputBuffers()[index];

buffer.clear();

} else {

buffer = mCodec.getInputBuffer(index);

}

if (buffer != null) {

buffer.put(data, offset, size);//填充数据

mCodec.queueInputBuffer(index, 0, size, 0, 0);

}

}

 

每个MediaCodec的操作,最终都会通过JNIandroid_media_MediaCodec.cpp调到native层的MediaCodec.cpp中,至于最终用的哪个解码器,是由系统自动决定的,如果是指定了解码器(常用的OMX.ffmpeg.h264.decoderOMX.google.h264.decoder),则会使用Client端指定的解码器。是硬解码还是软解码,这个也是由系统决定的,如果没有硬件解码则会转为软件解码(x86PC上使用时,没有软解码,FFMPG同时支持软解码和硬解码)

1.3.2消费解码数据

Client通过调用MediaCodec.dequeueOutputBuffer(...)请求得到了一个有效的output buffer index需要传入一个MediaCodec.BufferInfo实例,用以存储解码数据,获取到bufferindex后调用MediaCodec.releaseOutputBuffer(...)来释放缓冲区数据到Surface或者你的BufferInfo实例代码:

if (index >= 0) {

// setting true is telling system to render frame onto Surface

mCodec.releaseOutputBuffer(index, true);

if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) ==

MediaCodec.BUFFER_FLAG_END_OF_STREAM) {

break;

}

}

 

1.4释放解码器

MediaCodec数据处理任务完成时或不再需要MediaCodec时,可使用 MediaCodec.release()方法释放其资源实例代码:

 

if (mConfigured) {
    mCodec.stop();
    mCodec.release();
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics