Android Media Framework(十一)OMXNodeInstance - Ⅳ

在这篇文章中,我们将一起学习编解码过程中的两个数据驱动函数:emptyBuffer和fillBuffer,Android Media Framework(十)OMXNodeInstance - Ⅲ 是本文的基础,所以要仔细阅读上一篇文章哦。

1、emptyBuffer

客户端将数据填充到input buffer后会调用OMXNodeInstance的emptyBuffer方法,OMXNodeInstance根据不同的BufferType,对传递下来的不同信息进行封装,再统一调用OMX_EmptyThisBuffer通知到OMX组件消耗/清空input buffer中的数据。

emptyBuffer函数如下,首先我们要注意进入函数后就加了锁,既然有锁就说明有多个线程在使用OMXNodeInstance对象,有哪些线程呢?一个是ACodec中的消息处理线程,这部分内容我们将在ACodec相关章节做讲解;另一个是OMXNodeInstance中的CallbackDispatcherThread,用于处理组件的回调,这部分内容将在下一篇文章做讲解。

status_t OMXNodeInstance::emptyBuffer(
        buffer_id buffer, const OMXBuffer &omxBuffer,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    Mutex::Autolock autoLock(mLock);
    if (mHandle == NULL) {
        return DEAD_OBJECT;
    }

    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;
}

我们在switch语句中看到,Input BufferType可能有三种,分别为kBufferTypePreset、kBufferTypeANWBuffer和kBufferTypeNativeHandle。再回忆下,三种BufferType分别对应哪些具体的Buffer类型:

  • kBufferTypePreset:

    • kPortModePresetByteBuffer
    • kPortModePresetSecureBuffer
  • kBufferTypeANWBuffer:

    • kPortModeDynamicANWBuffer
  • kBufferTypeNativeHandle

    • kPortModeDynamicNativeHandle

要注意的是,在这里kPortModePresetANWBuffer不能算到kBufferTypePreset中,因为它只能用在输出端口。

PresetBuffer表示所有使用的buffer都是预置的,buffer地址在整个数据处理阶段不会发生改变,因此数据传输只需要传递起始偏移量、数据长度、时间戳、flag信息即可。为什么不要传递fenceFd呢?fence只有ANWBuffer要用,有兴趣的同学可以阅读Graphics相关文章。

对于kPortModeDynamicANWBuffer和kPortModeDynamicNativeHandle而言,我们之前已经讨论过,带有Dynamic前缀的buffer需要在数据处理阶段动态地确定其地址或handle。在之前的文章中我们看到,当对使用Metadata的端口调用useBuffer时,BUFFERHEADER的pBuffer成员会指向一个大小为sizeof(VideoNativeMetadata/VideoNativeHandleMetadata)、内容初始化为空的buffer。随后,在上层调用emptyBuffer时,会将相关的buffer信息更新到BufferMeta中,并将这些信息填充到pBuffer所指向的buffer中,这样组件就能获取到真实的buffer地址或handle了。

理解以上两点内容,接下来的函数解析就会很简单了。

2、emptyBuffer_l

status_t OMXNodeInstance::emptyBuffer_l(
        IOMX::buffer_id buffer,
        OMX_U32 rangeOffset, OMX_U32 rangeLength,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);

    BufferMeta *buffer_meta =
        static_cast<BufferMeta *>(header->pAppPrivate);

    header->nFilledLen = rangeLength;
    header->nOffset = rangeOffset;

    buffer_meta->CopyToOMX(header);

    return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd);
}

简化后的代码很简单,首先根据buffer id找到input port中对应的BufferHeader,自然而然拿到了BufferMeta。接下来需要调用CopyToOMX将上层的数据写到OMX中,这里真的要做数据拷贝吗?

答案是不一定。从CopyToOMX的实现中我们可以看出,当mCopyToOmx为false不需要做数据拷贝。问题变成mCopyToOmx什么时候为false呢?这在前面一篇文章中已经做了解释,只有在Quirk下才会将mCopyToOmx置为true。至于Quirk会在何时启用请再仔细阅读上一篇文章。

void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
    if (!mCopyToOmx) {
        return;
    }

    memcpy(header->pBuffer + header->nOffset,
            getPointer() + header->nOffset,
            header->nFilledLen);
}

OMX_U8 *getPointer() {
    return mMem.get() ? static_cast<OMX_U8*>(mMem->unsecurePointer()) :
            mHidlMemory.get() ? static_cast<OMX_U8*>(
            static_cast<void*>(mHidlMemory->getPointer())) : nullptr;
}

mCopyToOmx为true时数据是怎么做拷贝的呢?虽然CopyToOMX函数只传入了BufferHeader,但是BufferMeta中存储了Hidlmemory的地址,用getPointer拿到地址就能够进行数据拷贝了。

3、emptyGraphicBuffer_l

status_t OMXNodeInstance::emptyGraphicBuffer_l(
        IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);

    status_t err = updateGraphicBufferInMeta_l(
            kPortIndexInput, graphicBuffer, buffer, header);

    int64_t codecTimeUs = getCodecTimestamp(timestamp);

    header->nOffset = 0;
    if (graphicBuffer == NULL) {
        header->nFilledLen = 0;
    } else if (mMetadataType[kPortIndexInput] == kMetadataBufferTypeGrallocSource) {
        header->nFilledLen = sizeof(VideoGrallocMetadata);
    } else {
        header->nFilledLen = sizeof(VideoNativeMetadata);
    }
    return emptyBuffer_l(header, flags, codecTimeUs, (intptr_t)header->pBuffer, fenceFd);
}

4、emptyNativeHandleBuffer_l

5、emptyBuffer_l

6、fillBuffer

关注公众号《青山渺渺》阅读全文

  • 41
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山渺渺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值