OMX标准接口OMX_EmptyThisBuffer机制详解

一、引言:
上一篇博客已经讲了OMX_FillThisBuffer的机制,这边博客分析OMX_EmptyThisBuffer的机制。很多准备工作由于前面已经讲过了,这里就直接进入正题。

二、OMX通过EmptyBufferDone通知至MediaCodec:
底层vdec或者adec组件消耗完待解码数据之后,需要调用callbacks.EmptyBufferDone通知到MediaCodec:

pMSComponent->callbacks.EmptyBufferDone(pOMXComponent, pMSComponent->callbackData, bufferHeader);

看一下OMXNodeInstance中OnEmptyBufferDone的具体实现:

OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(
        OMX_IN OMX_HANDLETYPE /* hComponent */,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
    
    
    if (pAppData == NULL) {
    
    
        ALOGE("b/25884056");
        return OMX_ErrorBadParameter;
    }
    OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
    if (instance->mDying) {
    
    
        return OMX_ErrorNone;
    }
    int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput);

    omx_message msg;
    msg.type = omx_message::EMPTY_BUFFER_DONE;
    msg.fenceFd = fenceFd;
    msg.u.buffer_data.buffer = instance->findBufferID(pBuffer);
    instance->mDispatcher->post(msg);

    return OMX_ErrorNone;
}

将pBuffer的信息写入msg中,并投递出去:

void OMXNodeInstance::onMessages(std::list<omx_message> &messages) {
    
    
    for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) {
    
    
        if (handleMessage(*it)) {
    
    
            messages.erase(it++);
        } else {
    
    
            ++it;
        }
    }

    if (!messages.empty()) {
    
    
        mObserver->onMessages(messages);
    }
}

由于handleMessage没有做什么太多重要的操作,重点看ACodec中对omx_message::EMPTY_BUFFER_DONE的处理:

case omx_message::EMPTY_BUFFER_DONE:
{
    
    
    IOMX::buffer_id bufferID;
    int32_t fenceFd;

    CHECK(msg->findInt32("buffer", (int32_t*)&bufferID));
    CHECK(msg->findInt32("fence_fd", &fenceFd));

    return onOMXEmptyBufferDone(bufferID, fenceFd);
}

跟进到onOMXEmptyBufferDone

bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd) {
    
    
	...
    PortMode mode = getPortMode(kPortIndexInput);

    switch (mode) {
    
    
        case KEEP_BUFFERS:
            break;

        case RESUBMIT_BUFFERS:
        	/* 跟进如下函数 */
            postFillThisBuffer(info);
            break;

        case FREE_BUFFERS:
        default:
            ALOGE("SHOULD NOT REACH HERE: cannot free empty output buffers");
            return false;
    }

    return true;
}

继续跟进:

void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
    
    
    if (mCodec->mPortEOS[kPortIndexInput]) {
    
    
        return;
    }

    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);

    info->mData->setFormat(mCodec->mInputFormat);
    /* 调用ACodecBufferChannel */
    mCodec->mBufferChannel->fillThisBuffer(info->mBufferID);
    info->mData.clear();
    info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}

这里就会调入到ACodecBufferChannel中,看下fillThisBuffer的函数实现:

void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {
    
    
    ALOGV("fillThisBuffer #%d", bufferId);
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findBufferId(array, bufferId);

    if (it == array->end()) {
    
    
        ALOGE("fillThisBuffer: unrecognized buffer #%d", bufferId);
        return;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
    
    
        it->mClientBuffer->setFormat(it->mCodecBuffer->format());
    }
	/* 回调通知至MediaCodec */
    mCallback->onInputBufferAvailable(
            std::distance(array->begin(), it),
            it->mClientBuffer);
}

回到MediaCodec:

void BufferCallback::onInputBufferAvailable(
        size_t index, const sp<MediaCodecBuffer> &buffer) {
    
    
    sp<AMessage> notify(mNotify->dup());
    notify->setInt32("what", kWhatFillThisBuffer);
    notify->setSize("index", index);
    notify->setObject("buffer", buffer);
    notify->post();
}

kWhatFillThisBuffer的消息实现:

case kWhatFillThisBuffer:
{
    
    
	/* size_t index = */updateBuffers(kPortIndexInput, msg);
	...
}

和EmptyBufferDone时的处理一样,这里会更新输入通道的可用buffer数,应用就可以查询到对应id然后往里面填充新的待解码数据了:

size_t MediaCodec::updateBuffers(
        int32_t portIndex, const sp<AMessage> &msg) {
    
    
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
    size_t index;
    CHECK(msg->findSize("index", &index));
    sp<RefBase> obj;
    CHECK(msg->findObject("buffer", &obj));
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(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;
}

至此,OMX组件通知MediaCodec的过程就分析完了。

三、dequeueInputBuffer分析:
当OMX底层完成了解码并将buffer信息更新到mAvailPortBuffers后,应用就可以往里面继续填充新的es数据了,而首先需要做的,就是调用dequeueInputBuffer获得可用的buffer的index:

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的消息处理:

case kWhatDequeueInputBuffer:
{
    
    
	...
    if (handleDequeueInputBuffer(replyID, true /* new request */)) {
    
    
       break;
    }
    ...
}

继续跟进:

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

    if (index < 0) {
    
    
        CHECK_EQ(index, -EAGAIN);
        return false;
    }

    sp<AMessage> response = new AMessage;
    response->setSize("index", index);
    response->postReply(replyID);

    return true;
}

调用至dequeuePortBuffer

ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
    
    
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
	/* 1.获取buffer队列的临时指针对象  */
    List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];

    if (availBuffers->empty()) {
    
    
        return -EAGAIN;
    }
	/* 2.获得队首index */
    size_t index = *availBuffers->begin();
    /* 3.将此index从队列中删除 */
    availBuffers->erase(availBuffers->begin());
	
	/* 4.通过index拿到对应的buffer信息并填充 */
    BufferInfo *info = &mPortBuffers[portIndex][index];
    CHECK(!info->mOwnedByClient);
    {
    
    
        Mutex::Autolock al(mBufferLock);
        info->mOwnedByClient = true;

        // set image-data
        if (info->mData->format() != NULL) {
    
    
            sp<ABuffer> imageData;
            if (info->mData->format()->findBuffer("image-data", &imageData)) {
    
    
                info->mData->meta()->setBuffer("image-data", imageData);
            }
            int32_t left, top, right, bottom;
            if (info->mData->format()->findRect("crop", &left, &top, &right, &bottom)) {
    
    
                info->mData->meta()->setRect("crop-rect", left, top, right, bottom);
            }
        }
    }

    return index;
}

dequeuePortBuffer函数的目的很简单,就是从mAvailPortBuffers中获取可用buffer的index,如果没有的话,会返回-EAGAIN

四、queueInputBuffer分析:
当数据填充完后,应用会调用queueInputBuffer将buffer入队,并通知OMX组件进行解码:

status_t MediaCodec::queueInputBuffer(
        size_t index,
        size_t offset,
        size_t size,
        int64_t presentationTimeUs,
        uint32_t flags,
        AString *errorDetailMsg) {
    
    

    if (errorDetailMsg != NULL) {
    
    
        errorDetailMsg->clear();
    }

    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
    msg->setSize("index", index);
    msg->setSize("offset", offset);
    msg->setSize("size", size);
    msg->setInt64("timeUs", presentationTimeUs);
    msg->setInt32("flags", flags);
    msg->setPointer("errorDetailMsg", errorDetailMsg);

    sp<AMessage> response;

    status_t err = PostAndAwaitResponse(msg, &response);

    return err;
}

跟进kWhatQueueInputBuffer消息:

case kWhatQueueInputBuffer:
{
    
    
    sp<AReplyToken> replyID;
    CHECK(msg->senderAwaitsResponse(&replyID));

    if (!isExecuting()) {
    
    
        PostReplyWithError(replyID, INVALID_OPERATION);
        break;
    } else if (mFlags & kFlagStickyError) {
    
    
        PostReplyWithError(replyID, getStickyError());
        break;
    }

    status_t err = onQueueInputBuffer(msg);

    PostReplyWithError(replyID, err);
    break;
}

继续看onQueueInputBuffer的重点部分:

status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
    
    
	...
	if (...)
	{
    
    
		err = mBufferChannel->queueSecureInputBuffer(...):
    } else {
    
    
        err = mBufferChannel->queueInputBuffer(buffer);
    }
	...
}

这里需要到ACodecBufferChannel中去看下queueInputBuffer的实现:

status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
    
    
    if (mDealer != nullptr) {
    
    
        return -ENOSYS;
    }
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
    
    
        return -ENOENT;
    }
    ALOGV("queueInputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;
}

mInputBufferFilled->dup()拿到的是kWhatInputBufferFilled消息,看一下对应的消息实现:

case kWhatInputBufferFilled:
{
    
    
	onInputBufferFilled(msg);
	break;
}

跟进到onInputBufferFilled:

void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
    
    
	...
    PortMode mode = getPortMode(kPortIndexInput);
    ...
    switch (mode) {
    
    	
    	...
    	case RESUBMIT_BUFFERS:
        {
    
    
        	...
			status_t err2 = mCodec->mOMXNode->emptyBuffer(
                        bufferID, OMXBuffer::sPreset, OMX_BUFFERFLAG_EOS, 0, info->mFenceFd);
            ...
        }
    	...
    }
    ...  	
}

看下OMXNodeInstance::emptyBuffer

status_t OMXNodeInstance::emptyBuffer(
        buffer_id buffer, const OMXBuffer &omxBuffer,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    
    
    Mutex::Autolock autoLock(mLock);

    switch (omxBuffer.mBufferType) {
    
    
    case OMXBuffer::kBufferTypePreset:
        return emptyBuffer_l(
                buffer, omxBuffer.mRangeOffset, omxBuffer.mRangeLength,
                flags, timestamp, fenceFd);

    case OMXBuffer::kBufferTypeANWBuffer:
        return emptyGraphicBuffer_l(
                buffer, omxBuffer.mGraphicBuffer, flags, timestamp, fenceFd);

    case OMXBuffer::kBufferTypeNativeHandle:
        return emptyNativeHandleBuffer_l(
                buffer, omxBuffer.mNativeHandle, flags, timestamp, fenceFd);

    default:
        break;
    }

    return BAD_VALUE;
}
status_t OMXNodeInstance::emptyBuffer_l(
        IOMX::buffer_id buffer,
        OMX_U32 rangeOffset, OMX_U32 rangeLength,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    
    
    /* 1.找到BufferHeader */
    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
	...
	/* 2.将es数据拷贝到底层buffer中 */
	buffer_meta->CopyToOMX(header);
	...
	return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd);
}

继续跟进到重载函数中:

status_t OMXNodeInstance::emptyBuffer_l(
        OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp,
        intptr_t debugAddr, int fenceFd) {
    
    
	...
	OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
	...
}

这里就到了调用OMX标准接口的地方,表示让OMX底层去解码当前送下去的buffer。
最后再简单看下OMX IL层芯片平台的大致实现:

OMX_ERRORTYPE MS_OMX_EmptyThisBuffer(
    OMX_IN OMX_HANDLETYPE        hComponent,
    OMX_IN OMX_BUFFERHEADERTYPE *pBuffer)
{
    
    
	...
	/* 1.check传下来的buffer的有效性 */
    for (i = 0; i < pMSPort->portDefinition.nBufferCountActual; i++) {
    
    
        if (pBuffer == pMSPort->bufferHeader[i]) {
    
    
            findBuffer = OMX_TRUE;
            break;
        }
    }
    ...
    /* 2.将buffer信息写入到msg中,供buffer处理线程使用 */
    if (bPushQueue) {
    
    
        MS_OMX_MESSAGE *message = MS_OSAL_Malloc(sizeof(MS_OMX_MESSAGE));
        if (message == NULL) {
    
    
            ret = OMX_ErrorInsufficientResources;
            goto EXIT;
        }
        message->messageType = MS_OMX_CommandEmptyBuffer;
        message->messageParam = (OMX_U32) i;
        message->pCmdData = (OMX_PTR)pBuffer;

        MS_OSAL_Queue(&pMSPort->bufferQ, (void *)message);
        MS_OSAL_SemaphorePost(pMSPort->bufferSemID);
    }    
    
}

OMX_FillThisBuffer一样,L层中只需要将可用buffer的信息写入msg中,并推入到IL层自己实现的消息队列即可,至于buffer出现线程那边,会不停地读取可用msg,然后从msg中读取出可用的buffer进行新一轮的数据解码即可。

五、总结:
一张图剖析OMX_EmptyThisBufferEmptyBufferDone机制:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/achina2011jy/article/details/124828376
今日推荐