网站五合一建设,爱建站大全网,机械建设网站制作,自己做的网站图片挡住了导航栏上一节我们了解到 ACodec 执行完 start 流程后#xff0c;会把所有的 input buffer 都提交给 MediaCodec 层#xff0c;MediaCodec 是如何处理传上来的 buffer 呢#xff1f;这一节我们就来了解一下这部分内容。 1、ACodecBufferChannel::fillThisBuffer
ACodec 通过调用 A… 上一节我们了解到 ACodec 执行完 start 流程后会把所有的 input buffer 都提交给 MediaCodec 层MediaCodec 是如何处理传上来的 buffer 呢这一节我们就来了解一下这部分内容。 1、ACodecBufferChannel::fillThisBuffer
ACodec 通过调用 ACodecBufferChannel::fillThisBuffer 把input buffer传递给 MediaCodc传入参数为 buffer id
void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {ALOGV(fillThisBuffer #%d, bufferId);std::shared_ptrconst std::vectorconst BufferInfo array(std::atomic_load(mInputBuffers));// 遍历buffer数组查找对应ACodecBufferChannel::BufferInfoBufferInfoIterator it findBufferId(array, bufferId);if (it array-end()) {ALOGE(fillThisBuffer: unrecognized buffer #%d, bufferId);return;}// 如果存在解密/解扰那么需要设置input formatif (it-mClientBuffer ! it-mCodecBuffer) {it-mClientBuffer-setFormat(it-mCodecBuffer-format());}// 调用callbackmCallback-onInputBufferAvailable(std::distance(array-begin(), it),it-mClientBuffer);
}fillThisBuffer 很简单主要步骤如下
遍历buffer数组根据bufferid查找对应ACodecBufferChannel::BufferInfo从而获得mClientBuffer我们这里再回顾一下在不用解密/解扰的模式下mClientBuffer和mCodecBuffer其实是指向同一个MediaCodecBuffer的解密/解扰的模式那么mClientBuffer和mCodecBuffer指向的则不是同一块MediaCodecBuffer了如果mClientBuffer和mCodecBuffer不是指向同一块MediaCodecBuffer那么需要给 mClientBuffer 设置默认的 input format调用 onInputBufferAvailable 将消息回传给 MediaCodec
这里有一点很容易让人忽略为什么调用onInputBufferAvailable时传递的index要用std::distance来计算呢
std::distance应该计算的是 ACodecBufferChannel::BufferInfo 在数组中的位置也就是数组索引所以传递给 MediaCodec 用的 index 其实是 ACodecBufferChannel 的buffer数组索引它和buffer id是两码事。
2、BufferCallback::onInputBufferAvailable
void BufferCallback::onInputBufferAvailable(size_t index, const spMediaCodecBuffer buffer) {spAMessage notify(mNotify-dup());notify-setInt32(what, kWhatFillThisBuffer);notify-setSize(index, index);notify-setObject(buffer, buffer);notify-post();
}onInputBufferAvailable 会把回传的数组索引 以及 MediaCodecBuffer 重新封装到 AMessage中最后交由 MediaCodec Handler 处理。
2、kWhatFillThisBuffer case kWhatFillThisBuffer:{// 将拿到的 MediaCodec 加入到列表当中/* size_t index */updateBuffers(kPortIndexInput, msg);// 如果正在处理以下事件则直接将所有的buffer返回给Codecif (mState FLUSHING|| mState STOPPING|| mState RELEASING) {returnBuffersToCodecOnPort(kPortIndexInput);break;}// 如果 csd buffer 不为空则先写入csd bufferif (!mCSD.empty()) {ssize_t index dequeuePortBuffer(kPortIndexInput);CHECK_GE(index, 0);// If codec specific data had been specified as// part of the format in the call to configure and// if theres more csd left, we submit it here// clients only get access to input buffers once// this data has been exhausted.status_t err queueCSDInputBuffer(index);if (err ! OK) {ALOGE(queueCSDInputBuffer failed w/ error %d,err);setStickyError(err);postActivityNotificationIfPossible();cancelPendingDequeueOperations();}break;}// CCodec 使用的暂时略过if (!mLeftover.empty()) {ssize_t index dequeuePortBuffer(kPortIndexInput);CHECK_GE(index, 0);status_t err handleLeftover(index);if (err ! OK) {setStickyError(err);postActivityNotificationIfPossible();cancelPendingDequeueOperations();}break;}// 如果使用的是异步模式if (mFlags kFlagIsAsync) {// 并且输入不是surface输入是surface的情况我们暂时不看if (!mHaveInputSurface) {// 状态是 flushed则暂时不处理该input buffer等待重新启动if (mState FLUSHED) {mHavePendingInputBuffers true;} else {// 调用onInputBufferAvailable将input buffer返回给上层onInputBufferAvailable();}}} else if (mFlags kFlagDequeueInputPending) {// 如果是同步模式并且处在阻塞等待的状态收到input buffer发送消息结束阻塞CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));// 增加阻塞等待计数使得kWhatDequeueInputTimedOut无效mDequeueInputTimeoutGeneration;mFlags ~kFlagDequeueInputPending;mDequeueInputReplyID 0;} else {postActivityNotificationIfPossible();}break;}kWhatFillThisBuffer 消息处理流程中的内容稍有一点多我们有选择的对内容进行展开
将拿到的 MediaCodec 加入到列表当中这里的列表有两个一个是用来记录 ACodecBufferChannel 中所有的 buffer分为input / output 两个数组mPortBuffers第二个列表是用来记录可用的input/output buffer的 mAvailPortBuffers同样分为input / output 两个数组这里面记录的是可用的索引。
size_t MediaCodec::updateBuffers(int32_t portIndex, const spAMessage msg) {CHECK(portIndex kPortIndexInput || portIndex kPortIndexOutput);size_t index;CHECK(msg-findSize(index, index));spRefBase obj;CHECK(msg-findObject(buffer, obj));spMediaCodecBuffer buffer static_castMediaCodecBuffer *(obj.get());{Mutex::Autolock al(mBufferLock);if (mPortBuffers[portIndex].size() index) {mPortBuffers[portIndex].resize(align(index 1, kNumBuffersAlign));}mPortBuffers[portIndex][index].mData buffer;}mAvailPortBuffers[portIndex].push_back(index);return index;
}这里有一点非常重要如果没看懂很容易对接下来的内容产生疑惑将传来的MediaCodecBuffer记录到 mPortBuffers 中时这里会有一个隐式转换用 MediaCodecBuffer 创建了一个 MediaCodec::BufferInfo好家伙人手一个bufferinfo是吧。 struct BufferInfo {BufferInfo();spMediaCodecBuffer mData;bool mOwnedByClient;};MediaCodec::BufferInfo::BufferInfo() : mOwnedByClient(false) {}用一张图表示一下 buffer之间的关系
如果正在处理release/stop/release则直接将所有的buffer返回给CodecMediaCodec 不会持有任何 buffer
void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex, bool isReclaim) {CHECK(portIndex kPortIndexInput || portIndex kPortIndexOutput);Mutex::Autolock al(mBufferLock);if (portIndex kPortIndexInput) {mLeftover.clear();}for (size_t i 0; i mPortBuffers[portIndex].size(); i) {BufferInfo *info mPortBuffers[portIndex][i];if (info-mData ! nullptr) {spMediaCodecBuffer buffer info-mData;if (isReclaim info-mOwnedByClient) {ALOGD(port %d buffer %zu still owned by client when codec is reclaimed,portIndex, i);} else {info-mOwnedByClient false;info-mData.clear();}mBufferChannel-discardBuffer(buffer);}}mAvailPortBuffers[portIndex].clear();
}returnBuffersToCodecOnPort 会遍历所有 MediaCodec 记录的 BufferChannel 中的 buffer这里之所以要遍历记录的buffer是因为可能刚开始解码还有buffer没有传给MediaCodec流程就结束了MediaCodecBuffer 的 mOwnedByClient 指的是 buffer 是否被上层 app 所持有