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