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

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

相关推荐

  1. Android Media Framework(OMXNodeInstance - Ⅳ

    2024-07-14 23:34:04       25 阅读
  2. Android 13 - Media框架(25)- OMXNodeInstance(二)

    2024-07-14 23:34:04       48 阅读
  3. Android OpenMAX(八)如何学习OMXNodeInstance

    2024-07-14 23:34:04       31 阅读
  4. 算法基础

    2024-07-14 23:34:04       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-14 23:34:04       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-14 23:34:04       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-14 23:34:04       57 阅读
  4. Python语言-面向对象

    2024-07-14 23:34:04       68 阅读

热门阅读

  1. 基于matlab的深度学习案例及基础知识专栏前言

    2024-07-14 23:34:04       20 阅读
  2. C++ 桥接模式 (Bridge Pattern)

    2024-07-14 23:34:04       19 阅读
  3. liunx作业笔记1

    2024-07-14 23:34:04       19 阅读
  4. iOS热门面试题(二)

    2024-07-14 23:34:04       19 阅读
  5. python-程序结构

    2024-07-14 23:34:04       23 阅读
  6. Redis无感升级配置

    2024-07-14 23:34:04       20 阅读
  7. GitHub每周最火火火项目(7.8-7.14)

    2024-07-14 23:34:04       20 阅读