MediaCodec解码流程

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


MediaCodec流程图

1、Buffer类型

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

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

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

1.1、dequeuePortBuffer

ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
    List *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 msg = new AMessage(kWhatDequeueInputBuffer, this);
    msg->setInt64("timeoutUs", timeoutUs);

    sp 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 &replyID, bool newRequest) {
    ssize_t index = dequeuePortBuffer(kPortIndexInput);
    return true;
}

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

3、queueInputBuffer

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

status_t MediaCodec::onQueueInputBuffer(const sp &msg) {
    ...
    sp 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 &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 reply =
                new AMessage(kWhatOutputBufferDrained, mCodec);
    ...
    sp 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 &msg) {
            info->mFormat =
                (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat;
            mAvailPortBuffers[portIndex].push_back(i);// 把解码后的数据加入到可用buffer中
    return 0;
}

4、dequeueOutputBuffer

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

bool MediaCodec::handleDequeueOutputBuffer(const sp &replyID, bool newRequest) {
        ...
        ssize_t index = dequeuePortBuffer(kPortIndexOutput);// 获取已经解码后的buffer

        if (index < 0) {
            CHECK_EQ(index, -EAGAIN);// 获取失败
            return false;
        }

        const sp &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解码流程)