您的位置:

探究MediaCodec

一、MediaCodec简介

MediaCodec是安卓平台一种用于音频、视频编解码的API。在原生Android平台下,MediaCodec扮演着非常重要的角色。它可以提供音视频编解码的功能,解码视频数据来源可以是网络传输、本地文件、Camera、MediaExtractor等。MediaCodec还可以将编码后的视频流进行数据传输,以供其他模块进行后续处理。

二、MediaCodec的优缺点

1. 资源消耗较低

MediaCodec的资源消耗相比FFmpeg等其它工具库要低得多。它只需要底层的硬件资源支持和一点点的Java代码来实现音视频软件编解码。充分发挥了原生API的本地化性能,提升了应用的执行效率,缩减了资源消耗。

2. 只支持少量编码格式

虽然影响力出色,但MediaCodec只支持部分低复杂度的编码格式,如H.264、VP8、VP9等。如果要实现高复杂度格式、分辨率较高、帧数据量较大的编码过程还需依赖GPU来辅助处理。

3. 兼容性弱

虽然MediaCodec在API Level 16的安卓系统中就存在,但由于不同厂商硬件GPU架构不同,兼容性表现相差非常大。一些品牌更是存在MediaCodec极其不稳定的情况,因此应谨慎使用MediaCodec。

三、MediaCodec与节电

1. 关闭MediaCodec

在使用MediaCodec过程中,如遇到一段时间的空闲期可以考虑关闭它,以降低耗电量的同时提高手机性能。可以通过release()方法对MediaCodec释放资源,或调用flush()方法来使其传输卡顿,并对缓存区清空。

// 资源释放
mediaCodec.release();
// 清空缓存区
mediaCodec.flush();

2. 调整MediaCodec的Bit rate

在编码过程中,我们可以通过调整Bit rate的大小来降低耗电量。Bit rate越大,编码文件大小、帧速度越快。因此在不影响性能的情况下,尽量采用更小的Bit rate。

MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE_XXX, WIDTH, HEIGHT);
//设置bitrate,单位bps
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000);

3. 优化MediaCodec的配置

在配置MediaCodec参数时,可以通过增加缓存区大小和调整颜色格式等配置项来提高它的性能表现。以降低CPU、GPU的负载,优化电量消耗。

// 增加缓存区大小
mediaCodec.setParameters(parameters);
// 调整颜色格式
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);

四、MediaCodec编码流程

下面我们通过代码来了解MediaCodec的编码流程:

// 创建MediaCodecInfo编解码器
MediaCodecInfo mediaCodecInfo = MediaCodecList.findDecoderByType(MIME_TYPE_XXX);
if (mediaCodecInfo == null) {
    return false;
}
// 获取支持该编解码器的颜色格式
MediaCodecInfo.CodecCapabilities codecCapabilities = mediaCodecInfo.getCapabilitiesForType(MIME_TYPE_XXX);

// 创建MediaFormat
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE_XXX, WIDTH, HEIGHT);
format.setInteger(MediaFormat.KEY_BIT_RATE, BITRATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, codecCapabilities.colorFormats[0]);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
// 初始化MediaCodec
MediaCodec mediaCodec = MediaCodec.createDecoderByType(MIME_TYPE_XXX);
mediaCodec.configure(format, null, null, 0);
mediaCodec.start();

// 获取缓存区
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

// 解码数据流
while (notEnd) {
    int inIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_US);
    if (inIndex >= 0) {
        ByteBuffer buffer = inputBuffers[inIndex];
        buffer.clear();
        int sampleSize = mediaExtractor.readSampleData(buffer, 0);
        if (sampleSize < 0) {
            mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
        } else {
            mediaCodec.queueInputBuffer(inIndex, 0, sampleSize, mediaExtractor.getSampleTime(), 0);
            mediaExtractor.advance();
        }
    }

    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, MEDIACODEC_TIMEOUT);
    while (outIndex >= 0) {
        ByteBuffer buffer = outputBuffers[outIndex];
        // 将解码出来的数据进行后续处理
        // ...
        mediaCodec.releaseOutputBuffer(outIndex, false);
        outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, MEDIACODEC_TIMEOUT);
    }
}
// 关闭MediaCodec
mediaCodec.stop();
mediaCodec.release();
mediaExtractor.release();

五、结语

MediaCodec虽然有其局限性,但在一些简单且易于掌握的音视频处理场景下,MediaCodec已经足以胜任。而且在合理使用的情况下还能达到省电的目的,使得Android平台下的开发更加高效和便捷。