美文网首页
MediaCodec解码流程

MediaCodec解码流程

作者: Weller0 | 来源:发表于2019-10-25 11:31 被阅读0次

    先来张MediaCodec上下文涉及的源码图


    MediaCodec流程图

    1、Buffer类型

    Buffer主要包含两个列表,可用Buffer,和所有Buffer,每个列表都包含两个队列
    [0]InputBuffer队列
    [1]OutputBuffer队列

        List<size_t> mAvailPortBuffers[2];// 当前可用的Buffer索引
        Vector<BufferInfo> mPortBuffers[2];// 所有Buffer
    

    mAvailPortBuffers包含一下三个操作
    ①消费(dequeuePortBuffer)
    ②生产(updateBuffers)
    ③清除(returnBuffersToCodecOnPort)

    1.1、dequeuePortBuffer

    ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
        CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
        List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
        if (availBuffers->empty()) {
            return -EAGAIN;//如果没有可用的buffer,返回-EAGAIN(-11)
        }
    
        size_t index = *availBuffers->begin();
        availBuffers->erase(availBuffers->begin());
    ...
        return index;
    }
    

    1.2、updateBuffers

    1. ACodec收到omx_message::FILL_BUFFER_DONE消息,调用onOMXFillBufferDone
    2. 函数onOMXFillBufferDone中构建CodecBase::kWhatDrainThisBuffer消息,发送给MediaCodec
    3. kWhatDrainThisBuffer调用updateBuffers
    4. updateBuffers遍历mPortBuffers找到bufferId相同的buffer所在index,放入mAvailPortBuffers队尾
    

    会在updateBuffers中待会ACodec中消息

    msg->findMessage("reply", &info->mNotify)
    

    在ACodec中会构成解码前/后数据的消息

    kWhatInputBufferFilled// 解码前数据消息,主动通知ACodec接收解码前数据
    kWhatOutputBufferDrained// 解码后数据
    

    1.3、returnBuffersToCodecOnPort

    清理主要发生在以下时机:

    flush
    stop
    release
    state进入UNINITIALIZED
    

    returnBuffersToCodecOnPort主要是clear了mAvailPortBuffers

    2、dequeueInputBuffer

    申请可用的InputBuffer,该方法为同步方法,输入参数input是返回当前是否有可用buffer

    status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
        sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
        msg->setInt64("timeoutUs", timeoutUs);
    
        sp<AMessage> response;
        status_t err;
        if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
            return err;
        }
    
        CHECK(response->findSize("index", index));
    
        return OK;
    }
    

    进入kWhatDequeueInputBuffer消息中,主要在handleDequeueInputBuffer中处理

    bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
        ssize_t index = dequeuePortBuffer(kPortIndexInput);
        return true;
    }
    

    在dequeuePortBuffer方法中查找当前是否有可用的InputBuffer,并返回他在队列中的索引

    3、queueInputBuffer

    往InputBuffer中填充解码前的数据
    进入kWhatQueueInputBuffer消息中,主要在onQueueInputBuffer中处理

    status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
        ...
        sp<AMessage> reply = info->mNotify;// message=kWhatInputBufferFilled
        ...
        reply->setBuffer("buffer", info->mData);
        reply->post();
    
        info->mNotify = NULL;
    
        return OK;
    }
    

    然后进入ACodec的onInputBufferFilled
    这里涉及到ACodec的三种端口模式

    KEEP_BUFFERS 不会把当前持有的buffer送到OMX解码
            ①onInputBufferFilled填充数据时,未找到有效buffer
            ②ACodec处于BaseState状态
    RESUBMIT_BUFFERS 把当前持有的buffer送到OMX解码
            ①ACodec处于ExecutingState状态
            ②ACodec处于OutputPortSettingsChangedState状态,并且是InputBuffer
    FREE_BUFFERS 释放当前持有的buffer
            ①ACodec处于OutputPortSettingsChangedState状态,并且是OutputBuffer
    

    只有RESUBMIT_BUFFERS才会触发OMX解码

    void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
        ...
        if (err2 == OK) {
            err2 = mCodec->mOMX->emptyBuffer(
                mCodec->mNode,
                bufferID,
                0,
                info->mCodecData->size(),
                flags,
                timeUs,
                info->mFenceFd);
          }
        ...
    }
    

    然后等待OMX解码结束会触发omx_message::FILL_BUFFER_DONE事件,进入到onOMXFillBufferDone

    bool ACodec::BaseState::onOMXFillBufferDone(...) {
        ...
        sp<AMessage> reply =
                    new AMessage(kWhatOutputBufferDrained, mCodec);
        ...
        sp<AMessage> notify = mCodec->mNotify->dup();
        notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
        notify->setMessage("reply", reply);// 增加回调kWhatOutputBufferDrained
        notify->post();// 触发kWhatDrainThisBuffer,回到MediaCodec中updateBuffers
    }
    
    size_t MediaCodec::updateBuffers(int32_t portIndex, const sp<AMessage> &msg) {
                info->mFormat =
                    (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat;
                mAvailPortBuffers[portIndex].push_back(i);// 把解码后的数据加入到可用buffer中
        return 0;
    }
    

    4、dequeueOutputBuffer

    读取已经解码后的数据
    进入kWhatDequeueOutputBuffer消息中,主要在handleDequeueOutputBuffer中处理

    bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
            ...
            ssize_t index = dequeuePortBuffer(kPortIndexOutput);// 获取已经解码后的buffer
    
            if (index < 0) {
                CHECK_EQ(index, -EAGAIN);// 获取失败
                return false;
            }
    
            const sp<ABuffer> &buffer =
                mPortBuffers[kPortIndexOutput].itemAt(index).mData;
            ...
        }
    
        return true;
    }
    

    参考文档:
    https://zhuanlan.zhihu.com/p/47129044
    https://blog.csdn.net/dfhuang09/article/details/60132620
    https://unordered.org/timelines/5a22667e4ac00000

    相关文章

      网友评论

          本文标题:MediaCodec解码流程

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