NDK Mediacodec

作者: 梧桐光影 | 来源:发表于2016-12-02 18:56 被阅读2817次

    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.创建解码器

        const char* mine = "video/avc";
        AMediaCodec* mMediaCodec =  AMediaCodec_createDecoderByType(mine);
    

    2.配置解码器

        AMediaFormat* videoFormat = AMediaFormat_new();
        AMediaFormat_setString(videoFormat, "mime", "video/avc");
        AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_WIDTH, width); // 视频宽度
        AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_HEIGHT, height); // 视频高度
        AMediaFormat_setBuffer(videoFormat, "csd-0", sps, spsSize); // sps
        AMediaFormat_setBuffer(videoFormat, "csd-1", pps, ppsSize); // pps
    

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

    3.向解码器设置数据

        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帧的头部也要修改,否则前几帧图像显示不正确。

    1. 以轮询方式读取解码后的数据,解码后的数据格式在mColorFormat中,本例得到的是NV12格式
        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;
            }
        }
    

    相关文章

      网友评论

      • Jaden_xyz:请问,你的AMediaCodec_configure中参数是怎么设置的?我这里总出22的错误
        tzq_ea15:我现在也要去解码后nv12数据, 能分享一下具体的代码么?
        tzq_ea15:你好请问一下你这个 uint8_t* dst = buffer; buffer 定义是怎么样的
        梧桐光影:参见“2.配置解码器”里面的设置方式,sps/pps得指定对。如果用ffmpeg读文件,得自己从extradata中提取sps/pps

      本文标题:NDK Mediacodec

      本文链接:https://www.haomeiwen.com/subject/akeqmttx.html