前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android NDK MediaCodec

Android NDK MediaCodec

作者头像
字节流动
发布2021-04-23 12:56:13
2.8K0
发布2021-04-23 12:56:13
举报
文章被收录于专栏:字节流动字节流动

MediaCodec

Android 从 API 16 开始提供java层的 MediaCodec 视频硬解码接口;从 API 21,也就是Android 5.0 开始提供 native 层的 MediaCodec的接口

详细描述可参见官方文档:

https://developer.android.com/reference/android/media/MediaCodec.html

NDK中附带的例子使用 MediaExtractor 解析视频文件数据,其中隐藏了很多细节。下面以h264编码的mp4文件为例,简单介绍一下在native层使用 MediaCodec 对视频进行硬解码的使用方式。

MediaCodec的接口定义在头文件 media/NdkMediaCodec.h 中,各个接口参数的含义不再赘述,仅列出过程和需要注意的细节。

1.创建解码器

代码语言:javascript
复制
    const char* mine = "video/avc";
    AMediaCodec* mMediaCodec =  AMediaCodec_createDecoderByType(mine);

2.配置解码器

代码语言:javascript
复制
AMediaFormat_setBuffer(videoFormat, "csd-1", pps, ppsSize); // pps

注:我们的视频文件是用ffmpeg解析的。widht 和 height 可以从 ffmpeg 中读取。sps 和pps 在 ffmpeg 对应的视频流 AVStream->codec->extradata 中

3.向解码器设置数据

代码语言:javascript
复制
    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mMediaCodec, 2000);
    if (bufidx >= 0) {
        // 获取buffer的索引
        uint8_t* inputBuf = AMediaCodec_getInputBuffer(mMediaCodec, bufidx, &outsize);
        if (inputBuf != nullptr && bufSize <= outsize) {
            // 将待解码的数据copy到硬件中
            memcpy(inputBuf, bufData, bufSize);
            media_status_t status = AMediaCodec_queueInputBuffer(mMediaCodec, bufidx, 0, bufSize, pts, 0);
    }

从ffmpeg里面读取的解码前的每帧数据的前四个字节表示该帧数据的大小,而MediaCode要求的数据必须以 \x00\x00\x00\x01 开头。将前四个字节直接用 \x00\x00\x00\x01 替换即可。

从ffmpeg里面读取的第一帧数据可能对应多个h264帧,后几个h264帧的头部也要修改,否则前几帧图像显示不正确。

4. 以轮询方式读取解码后的数据,解码后的数据格式在mColorFormat中,本例得到的是NV12格式

代码语言:javascript
复制
    AMediaCodecBufferInfo info;
    ssize_t outbufidx = AMediaCodec_dequeueOutputBuffer(mMediaCodec, &info, 2000);
    if (outbufidx >= 0) {
        size_t outsize;
        uint8_t* outputBuf = AMediaCodec_getOutputBuffer(mMediaCodec, outbufidx, &outsize);
        if (outputBuf != nullptr) {
            pts = info.presentationTimeUs;
            int32_t pts32 = (int32_t) pts;
            uint8_t* dst = buffer;
            memcpy(dst, outputBuf, MIN(mOriFrameSize, mFrameSize));
            
            uint8_t * uvBuf = outputBuf + mFrameSize;
            
            uint32_t uvSize = MIN(mOriFrameSize >> 1, mFrameSize >> 1);
            uint8_t *uBuf = dst  + mOriFrameSize;
            uint8_t *vBuf = uBuf + (mOriFrameSize >> 2);
            memcpy(uBuf, uvBuf, uvSize);
            AMediaCodec_releaseOutputBuffer(mMediaCodec, outbufidx, info.size != 0);
            return GOT_OUT_PUT;
        }
    }
    else {
        switch (outbufidx) {
            case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
            // 解码输出的格式发生变化
            {
                auto format = AMediaCodec_getOutputFormat(mMediaCodec);
                AMediaFormat_getInt32(format, "width", &mWidth);
                AMediaFormat_getInt32(format, "height", &mHeight);
                int32_t localColorFMT;
                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &localColorFMT);
                mColorFormat = getTTFormatFromMC(localColorFMT);
                int32_t stride = 0;
                AMediaFormat_getInt32(format, "stride", &stride);
                if(stride == 0) {
                    stride = mWidth;
                }
                mLineSize[0] = stride;
                mFrameSize    = stride * mHeight;
                mOriFrameSize = stride * mOriHeight;
                return OUTPUT_FORMAT_CHANGED;
            }
            case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
                break;
            default:
                break;
        }
    }

作者:梧桐光影 链接:https://www.jianshu.com/p/1bdae1c6d3b7

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档