MediaCodec解碼流程

先來(lái)張MediaCodec上下文涉及的源碼圖


MediaCodec流程圖

1、Buffer類型

Buffer主要包含兩個(gè)列表,可用Buffer,和所有Buffer,每個(gè)列表都包含兩個(gè)隊(duì)列
[0]InputBuffer隊(duì)列
[1]OutputBuffer隊(duì)列

    List<size_t> mAvailPortBuffers[2];// 當(dāng)前可用的Buffer索引
    Vector<BufferInfo> mPortBuffers[2];// 所有Buffer

mAvailPortBuffers包含一下三個(gè)操作
①消費(fèi)(dequeuePortBuffer)
②生產(chǎn)(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消息,調(diào)用onOMXFillBufferDone
2. 函數(shù)onOMXFillBufferDone中構(gòu)建CodecBase::kWhatDrainThisBuffer消息,發(fā)送給MediaCodec
3. kWhatDrainThisBuffer調(diào)用updateBuffers
4. updateBuffers遍歷mPortBuffers找到bufferId相同的buffer所在index,放入mAvailPortBuffers隊(duì)尾

會(huì)在updateBuffers中待會(huì)ACodec中消息

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

在ACodec中會(huì)構(gòu)成解碼前/后數(shù)據(jù)的消息

kWhatInputBufferFilled// 解碼前數(shù)據(jù)消息,主動(dòng)通知ACodec接收解碼前數(shù)據(jù)
kWhatOutputBufferDrained// 解碼后數(shù)據(jù)

1.3、returnBuffersToCodecOnPort

清理主要發(fā)生在以下時(shí)機(jī):

flush
stop
release
state進(jìn)入U(xiǎn)NINITIALIZED

returnBuffersToCodecOnPort主要是clear了mAvailPortBuffers

2、dequeueInputBuffer

申請(qǐng)可用的InputBuffer,該方法為同步方法,輸入?yún)?shù)input是返回當(dāng)前是否有可用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;
}

進(jìn)入kWhatDequeueInputBuffer消息中,主要在handleDequeueInputBuffer中處理

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

在dequeuePortBuffer方法中查找當(dāng)前是否有可用的InputBuffer,并返回他在隊(duì)列中的索引

3、queueInputBuffer

往InputBuffer中填充解碼前的數(shù)據(jù)
進(jìn)入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;
}

然后進(jìn)入ACodec的onInputBufferFilled
這里涉及到ACodec的三種端口模式

KEEP_BUFFERS 不會(huì)把當(dāng)前持有的buffer送到OMX解碼
        ①onInputBufferFilled填充數(shù)據(jù)時(shí),未找到有效buffer
        ②ACodec處于BaseState狀態(tài)
RESUBMIT_BUFFERS 把當(dāng)前持有的buffer送到OMX解碼
        ①ACodec處于ExecutingState狀態(tài)
        ②ACodec處于OutputPortSettingsChangedState狀態(tài),并且是InputBuffer
FREE_BUFFERS 釋放當(dāng)前持有的buffer
        ①ACodec處于OutputPortSettingsChangedState狀態(tài),并且是OutputBuffer

只有RESUBMIT_BUFFERS才會(huì)觸發(fā)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解碼結(jié)束會(huì)觸發(fā)omx_message::FILL_BUFFER_DONE事件,進(jìn)入到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);// 增加回調(diào)kWhatOutputBufferDrained
    notify->post();// 觸發(fā)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);// 把解碼后的數(shù)據(jù)加入到可用buffer中
    return 0;
}

4、dequeueOutputBuffer

讀取已經(jīng)解碼后的數(shù)據(jù)
進(jìn)入kWhatDequeueOutputBuffer消息中,主要在handleDequeueOutputBuffer中處理

bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
        ...
        ssize_t index = dequeuePortBuffer(kPortIndexOutput);// 獲取已經(jīng)解碼后的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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容