Android Media Framework(十)OMXNodeInstance - Ⅲ

这一节,我们将重点剖析OMXNodeInstance的useBuffer和allocateSecureBuffer方法。

1、OMXBuffer

ACodec通过调用OMXNodeInstance的useBuffer方法,指定OMX组件端口使用客户端分配的buffer。哪些类型的buffer属于客户端分配的buffer呢?

在解码流程中,PresetByteBuffer、PresetANWBuffer和DynamicANWBuffer这些类型的buffer都是由客户端分配的,而PresetSecureBuffer则需要由OMX组件来分配。而在编码流程中,所有的输入和输出buffer都应由客户端来分配。

先来看IOmxNode提供的useBuffer接口,调用useBuffer需要传入一个CodecBuffer对象,结束调用后会回传一个uint32_t类型的数据:

useBuffer(
        uint32_t portIndex,
        CodecBuffer omxBuffer
    ) generates (
        Status status,
        BufferId buffer
    );

再看OMXNodeInstance的useBuffer方法,会发现传入参数是OMXBuffer类型的对象,CodecBuffer和OMXBuffer之间有什么关系呢?为什么要使用OMXBuffer呢?

status_t OMXNodeInstance::useBuffer(
         OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer);

客户端可能会分配出多种类型的buffer,它们的格式不同,携带的信息也不同,为了使用一个接口传递这些buffer,需要将它们以统一的格式封装,这就是CodecBuffer的作用。

struct CodecBuffer {
    enum Type : int32_t {
        INVALID = 0,
        PRESET,
        SHARED_MEM,
        ANW_BUFFER,
        NATIVE_HANDLE,
    };

    struct PresetAttributes {
        uint32_t rangeOffset;
        uint32_t rangeLength;
    };

    union Attributes {
        PresetAttributes preset;
        AnwBufferAttributes anwBuffer;
    };

    Type type;

    Attributes attr;

    handle nativeHandle;

    memory sharedMemory;
};

CodecBuffer的定义如上,使用Type记录传递buffer的类型,用Attributes记录buffer相关的信息,如果传递的是nativeHandle则用handle记录,如果传递的是hidlmemory则用memory来记录。接下来的问题是,如何将这些buffer封装为CodecBuffer呢?这个问题肯定难不倒大家,最简单的方式就是实现多个重载函数,根据传入的不同类型的buffer实现不同的封装方式。Android的实现方式多走了一点弯路,选择将不同buffer封装为OMXBuffer,再实现一个方法将OMXBuffer转换为CodecBuffer。选择将不同buffer封装为OMXBuffer的方法也是使用重载的方法,只不过是重载的构造函数。接下来就来看看OMXBuffer到底是怎么实现的:

OMXBuffer OMXBuffer::sPreset(static_cast<sp<MediaCodecBuffer> >(NULL));

OMXBuffer::OMXBuffer(const sp<MediaCodecBuffer>& codecBuffer)
    : mBufferType(kBufferTypePreset),
      mRangeOffset(codecBuffer != NULL ? codecBuffer->offset() : 0),
      mRangeLength(codecBuffer != NULL ? codecBuffer->size() : 0) {
}

/* not use
OMXBuffer::OMXBuffer(const sp<IMemory> &mem)
    : mBufferType(kBufferTypeSharedMem),
      mMem(mem) {
}
*/

OMXBuffer::OMXBuffer(const sp<GraphicBuffer> &gbuf)
    : mBufferType(kBufferTypeANWBuffer),
      mGraphicBuffer(gbuf) {
}

OMXBuffer::OMXBuffer(const sp<NativeHandle> &handle)
    : mBufferType(kBufferTypeNativeHandle),
      mNativeHandle(handle) {
}

OMXBuffer::OMXBuffer(const hidl_memory &hidlMemory)
    : mBufferType(kBufferTypeHidlMemory),
      mHidlMemory(hidlMemory) {
}

OMXBuffer内部定义了一个枚举BufferType,用于表示buffer类型。

enum BufferType {
    kBufferTypeInvalid = 0,
    kBufferTypePreset,
    /* kBufferTypeSharedMem, not use */
    kBufferTypeANWBuffer,
    kBufferTypeNativeHandle,
    kBufferTypeHidlMemory
};

可能有读者会疑惑,这里的BufferType和我们之前学的PortMode所指示的buffer类型有什么区别呢?

PortMode是在configure期间由ACodec确定的,比较具体的buffer信息,它指示了编解码所需用的buffer类型和分配方式。

OMXBuffer中的BufferType是一个比较宽泛的类型,比如说kPortModePresetANWBuffer和kPortModeDynamicANWBuffer都是属于OMXBuffer::kBufferTypeANWBuffer。调用useBuffer时会根据此BufferType来决定如何与组件建立buffer连接。其实笔者认为PortMode和BufferType有点重复了,只用PortMode即可。

不过既然写了,我们就来看看吧。

值得注意的是OMXBuffer::kBufferTypePreset是一个特殊的枚举值,它并不直接指明buffer的具体类型。在之前的讨论中,我们知道preset buffer的分配是OMX组件在OMX_StateLoadToIdle状态下完成的,而dynamic buffer则是在组件在OMX_StateExecuting状态时动态分配的。这意味着在OMX_StateLoadToIdle状态下,dynamic buffer实际上是不存在的。

为了保持客户端与组件之间调用的一致性,对使用dynamic buffer的端口调用useBuffer时传入一个空的buffer,先建立起客户端与组件之间buffer的连接,当真正的dynamic buffer分配完成后,再更新BufferHeader指向的内容即可。

为了让数据能够跨进程传输,需要使用HidlMemory,对应的BufferType是kBufferTypeHidlMemory。

OMXBuffer和CodecBuffer之间是如何互相转换的,这里就不再做具体解释,有兴趣的同学可以阅读LWOmxNode::useBuffer和TWOmxNode::useBuffer。

2、useBuffer

直入主题,上代码!

status_t OMXNodeInstance::useBuffer(
        OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {
    // ...
    switch (omxBuffer.mBufferType) {
        case OMXBuffer::kBufferTypePreset: {
            if (mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
                    && mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
                break;
            }
            return useBuffer_l(portIndex, NULL, NULL, buffer);
        }
        // ...
        case OMXBuffer::kBufferTypeANWBuffer: {
            if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
                    && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
                break;
            }
            return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
        }

        case OMXBuffer::kBufferTypeHidlMemory: {
                if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
                        && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
                        && mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
                    break;
                }
                sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
                if (hidlMemory == nullptr) {
                    return NO_MEMORY;
                }
                return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
        }
        default:
            return BAD_VALUE;
            break;
    }
    return INVALID_OPERATION;
}

根据不同的BufferType调用了不同方法来与OMX组件构建buffer连接,分别是useBuffer_l和useGraphicBuffer_l,接下来我们一一解析。

useBuffer_l

useBuffer_l的代码比较长且逻辑稍微复杂,是因为Android将对kBufferTypePreset和kBufferTypeHidlMemory的处理放在一起了。

为了看得更清楚,我们将这部分代码分成两半,先来看对kBufferTypeHidlMemory的处理:

status_t OMXNodeInstance::useBuffer_l(
        OMX_U32 portIndex, const sp<IMemory> &params,
        const sp<IHidlMemory> &hParams, IOMX::buffer_id *buffer) {
    BufferMeta *buffer_meta;
    OMX_BUFFERHEADERTYPE *header;
    OMX_ERRORTYPE err = OMX_ErrorNone;
    bool isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
    // isMetadata = false,mMetadataType[in/out] = kMetadataBufferTypeInvalid
    if (!isMetadata && mGraphicBufferEnabled[portIndex]) {
        return INVALID_OPERATION;
    }

    size_t paramsSize;
    void* paramsPointer;
    if (hParams != NULL) {
        paramsPointer = hParams->getPointer();
        paramsSize = hParams->getSize();
    }

    bool isOutputGraphicMetadata = (portIndex == kPortIndexOutput) &&
            (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource ||
                    mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer);
    // isOutputGraphicMetadata = false
    uint32_t requiresAllocateBufferBit =
        (portIndex == kPortIndexInput)
            ? kRequiresAllocateBufferOnInputPorts
            : kRequiresAllocateBufferOnOutputPorts;
    // 检查Quirks
    if (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit)) {
        buffer_meta = new BufferMeta(
                    params, hParams, portIndex, !isMetadata /* copy */, NULL /* data */);

        err = OMX_AllocateBuffer(
                mHandle, &header, portIndex, buffer_meta, allottedSize);
    } else {
        OMX_U8 *data = NULL;
        data = static_cast<OMX_U8 *>(paramsPointer);
        buffer_meta = new BufferMeta(
                    params, hParams, portIndex, false /* copy */, NULL);
        err = OMX_UseBuffer(
                mHandle, &header, portIndex, buffer_meta,
                allottedSize, data);
    }

    CHECK_EQ(header->pAppPrivate, buffer_meta);
    *buffer = makeBufferID(header);
    addActiveBuffer(portIndex, *buffer); 
}

3、allocateSecureBuffer

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

请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青山渺渺

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

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

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

打赏作者

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

抵扣说明:

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

余额充值