OMX 標準インターフェースの OMX_EmptyThisBuffer メカニズムの詳細な説明

1.はじめに:
以前のブログですでに言及されているメカニズムOMX_FillThisBufferと、ここのブログで分析されたメカニズムOMX_EmptyThisBuffer前に多くの準備作業について言及したので、ここで直接トピックに行きましょう。

2. OMX は EmptyBufferDone を介して MediaCodec に通知します。
基になる vdec または adec コンポーネントがデコードするデータを消費した後、callbacks.EmptyBufferDoneMediaCodec への通知を呼び出す必要があります。

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 はあまり多くの重要な操作を行わないため、omx_message::EMPTY_BUFFER_DONEACodec での処理に注目してください。

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 と同じように、入力チャネルの利用可能なバッファの数がここで更新され、アプリケーションは対応する 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 に通知するプロセスを分析しました。

3. dequeueInputBuffer の分析:
OMX の最下層でデコードが完了し、バッファー情報が更新された後mAvailPortBuffers、アプリケーションは引き続き新しい es データを入力できます。最初に行うことは、呼び出してdequeueInputBuffer使用可能なバッファーのインデックスを取得することです。

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使用。そうでない場合は、 を返します-EAGAIN

4. queueInputBuffer の分析:
データがいっぱいになると、アプリケーションは呼び出してqueueInputBufferバッファをエンキューし、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);
    }
	...
}

queueInputBufferここでは、実装を確認するために ACodecBufferChannel に移動する必要があります。

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 の最下層にデコードさせることを意味します。
最後に、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層は利用可能なバッファの情報をmsgに書き込んで、IL層が実装するメッセージキューにプッシュするだけでよく、バッファが出現したスレッドについては、利用可能なmsgを連続して読み込み、次に、msg から使用可能なバッファーを読み取り、新しいラウンドのデータ デコードを実行します。

V. まとめ:絵の
分析OMX_EmptyThisBufferとメカニズム:EmptyBufferDone
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/achina2011jy/article/details/124828376
おすすめ