MediaCodec解码流程

先来张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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容