Android 12(S) MultiMedia Learning(十)ACodec & OMX


這一節的學習分為三塊內容,omx hidl service用法、OMX架構、ACodec中的buffer分配。

1、omx hidl service

system可以借助vndbinder來訪問vendor分區的內容,這里以omx hidl service為例子學習下hidl代碼要如何閱讀使用。

相關代碼路徑:

hardware/interfaces/media/omx/1.0/IOmx.hal

frameworks/av/media/libstagefright/omx/1.0/Omx.cpp

frameworks/av/services/mediacodec/main_codecservice.cpp

frameworks/av/media/libstagefright/OMXClient.cpp

IOmx.hal中定義相關的接口,編譯之后會在out/soong/.intermediates下生成相關的.h以及.cpp文件

Omx.cpp中會有接口的實現

main_codecservice.cpp用於啟動服務,registerAsService聲明在hidl生成文件中

OMXClient 獲取IOmx服務,IOmx::getService聲明在hidl生成文件中

 

2、OMX架構

2.1、獲取IOmx服務

// ACodec.cpp
    OMXClient client;
    if (client.connect(owner.c_str()) != OK) {
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    }
    omx = client.interface();

// OMXClient.cpp
status_t OMXClient::connect(const char* name) {
    using namespace ::android::hardware::media::omx::V1_0;
    if (name == nullptr) {
        name = "default";
    }
    sp<IOmx> tOmx = IOmx::getService(name);
    if (tOmx.get() == nullptr) {
        ALOGE("Cannot obtain IOmx service.");
        return NO_INIT;
    }
    if (!tOmx->isRemote()) {
        ALOGE("IOmx service running in passthrough mode.");
        return NO_INIT;
    }
    mOMX = new utils::LWOmx(tOmx);
    ALOGI("IOmx service obtained");
    return OK;
}

這里的代碼還算簡單,利用OMXClient的connect方法來獲取IOmx服務,然后封裝到LWOmx當中。

LWOmx定義在 frameworks/av/media/libmedia/include/media/omx/1.0/WOmx.h,繼承於IOMX,注意這里並不是一個binder對象!為什么要把IOmx封裝到LWOmx中呢?看LWOmx的實現就知道了,LWOmx幫我們隱藏了hidl調用的細節(比如hidl callback),讓調用更簡單。

 

2.2、服務的使用

這里以allocateNode方法為例子來看看OMX的架構。

// ACodec.cpp
sp<CodecObserver> observer = new CodecObserver(notify);
sp<IOMXNode> omxNode;
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);

參數傳遞了一個CodecObserver對象和一個IOMXNode對象。CodecObserver繼承於BnOMXObserver,BnOMXObserver聲明在IOMX.h當中;IOMXMode同樣聲明在IOMX.h當中。

// WOmx.cpp
status_t LWOmx::allocateNode(
        char const* name,
        sp<IOMXObserver> const& observer,
        sp<IOMXNode>* omxNode) {
    status_t fnStatus;
    status_t transStatus = toStatusT(mBase->allocateNode(
            name, new TWOmxObserver(observer),
            [&fnStatus, omxNode](Status status, sp<IOmxNode> const& node) {
                fnStatus = toStatusT(status);
                *omxNode = new LWOmxNode(node);
            }));
    return transStatus == NO_ERROR ? fnStatus : transStatus;
}

LWOmx中的調用做了兩個轉換,IOMXObserver轉TWOmxObserver,IOmxNode轉LWOmxNode,乍一看很復雜!

我們先仔細看一下Omx.h中allocateNode的聲明:

    Return<void> allocateNode(
            const hidl_string& name,
            const sp<IOmxObserver>& observer,
            allocateNode_cb _hidl_cb) override;

傳入參數為IOmxObserver,注意它和我們看到的ACodec和IOMX.h中的IOMXObserver是完全不同的,看他們的名字中的"omx"的大小寫,IOmx這是在hal中聲明的,而IOMX是其他地方聲明的binder對象。

IOmxObserver的接口實現為TWOmxObserver,為了傳遞IOMXObserver對象,TWOmxObserver中封裝了一個IOMXObserver對象

// frameworks/av/media/libmedia/include/media/omx/1.0/WOmxObserver.h
struct TWOmxObserver : public IOmxObserver {
    sp<IOMXObserver> mBase;
    TWOmxObserver(sp<IOMXObserver> const& base);
    Return<void> onMessages(const hidl_vec<Message>& tMessages) override;
};

這里注意在stagefright目錄下也有一個WOmxObserver.h,這兩個看起來內容很像但也是完全不同的,從命名空間來看stagefright目錄下的都是hidl接口的實現,media目錄下的作為工具類使用。

同樣的IOmxNode 和 IOMXNode的關系也一樣,callback拿回來的是一個hidl對象,通過LWOmxNode封裝為一個binder對象給上層使用。

接下來看看Omx.cpp中的allocateNode實現具體是怎么做的:

Return<void> Omx::allocateNode(
        const hidl_string& name,
        const sp<IOmxObserver>& observer,
        allocateNode_cb _hidl_cb) {

    using ::android::IOMXNode;
    using ::android::IOMXObserver;

    sp<OMXNodeInstance> instance;
    {
        // 1、檢查實例數量
        Mutex::Autolock autoLock(mLock);
        if (mLiveNodes.size() == kMaxNodeInstances) {
            _hidl_cb(toStatus(NO_MEMORY), nullptr);
            return Void();
        }
        // 2、創建OMXNodeInstance
        instance = new OMXNodeInstance(
                this, new LWOmxObserver(observer), name.c_str());

        OMX_COMPONENTTYPE *handle;
        // 3、創建Component
        OMX_ERRORTYPE err = mStore->makeComponentInstance(
                name.c_str(), &OMXNodeInstance::kCallbacks,
                instance.get(), &handle);
        // ......
        // 4、把component交給OMXNodeInstance管理
        instance->setHandle(handle);

        // 5、從xml查找quirks
        // Find quirks from mParser
        const auto& codec = mParser.getCodecMap().find(name.c_str());
        if (codec == mParser.getCodecMap().cend()) {
        // ......
        } else {
            uint32_t quirks = 0;
            for (const auto& quirk : codec->second.quirkSet) {
                if (quirk == "quirk::requires-allocate-on-input-ports") {
                    quirks |= OMXNodeInstance::
                            kRequiresAllocateBufferOnInputPorts;
                }
                if (quirk == "quirk::requires-allocate-on-output-ports") {
                    quirks |= OMXNodeInstance::
                            kRequiresAllocateBufferOnOutputPorts;
                }
            }
            instance->setQuirks(quirks);
        }
        // 6、添加OMXNodeInstance到IOmx服務管理列表中
        mLiveNodes.add(observer.get(), instance);
        mNode2Observer.add(instance.get(), observer.get());
    }
    observer->linkToDeath(this, 0);
    // callback將OMXNodeInstance返回給上層
    _hidl_cb(toStatus(OK), new TWOmxNode(instance));
    return Void();
}    

第二步創建OMXNodeInstance時會把傳進來的TWOmxObserver轉為LWOmxObserver,這里用到LWOmxObserver聲明在stagefright目錄中。

OMXNodeInstance的創建和OMXStore的makeComponentInstance方法這里不做展開,比較簡單。

IOMXNode調用某個方法的過程:

IOMXNode -> LWOmxNode -> TWOmxNode -> OMXNodeInstance

IOMXObserver的回調過程:

IOMXObserver -> LWOmxObserver -> TWOmxObserver -> CodecObserver

 

3、ACodec中的buffer分配

先看一下mPortMode,分為kPortIndexInput和kPortIndexOutput

mPortMode會在構造函數中被初始化為IOMX::kPortModePresetByteBuffer,configureCodec過程中可能會被修改為其他值,看看都有哪些情況:

encoder:

1、會判斷Message中是否有"android._input-metadata-buffer-type" tag,如果有則置為kPortIndexInput對應值,如果沒有就置kPortIndexInput為IOMX::kPortModePresetByteBuffer

2、如果是video,並且需要secure mode,設定kPortIndexOutput為IOMX::kPortModePresetSecureBuffer,否則保持為IOMX::kPortModePresetByteBuffer

 

decoder:

1、需要secure mode,設定kPortIndexInput為IOMX::kPortModePresetSecureBuffer,否則保持為IOMX::kPortModePresetByteBuffer

2、如果有surface

    tunnel mode,kPortIndexOutput設定為IOMX::kPortModePresetANWBuffer,同時會調用configureTunneledVideoPlayback

    非tunnel mode,kPortIndexOutput設定為IOMX::kPortModeDynamicANWBuffer

     沒有surface,kPortIndexOutput保持為IOMX::kPortModePresetByteBuffer

3、如果是視頻

    組件使用的是OMX.google開頭的軟解組件,kPortIndexOutput保持為IOMX::kPortModePresetByteBuffer

 

到allocateBuffersOnPort時(暫時只討論decoder)

1、kPortIndexOutput

1.1、有surface
1.1.1、非tunnel mode

allocateOutputMetadataBuffers分配buffer

// ACodec.cpp
status_t ACodec::allocateOutputMetadataBuffers() {
    OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
    status_t err = configureOutputBuffersFromNativeWindow(
            &bufferCount, &bufferSize, &minUndequeuedBuffers,
            mFlags & kFlagPreregisterMetadataBuffers /* preregister */);
    if (err != OK)
        return err;
    mNumUndequeuedBuffers = minUndequeuedBuffers;

    for (OMX_U32 i = 0; i < bufferCount; i++) {
        BufferInfo info;
        info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
        info.mFenceFd = -1;
        info.mRenderInfo = NULL;
        info.mGraphicBuffer = NULL;
        info.mNewGraphicBuffer = false;
        info.mDequeuedAt = mDequeueCounter;

        info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));

        ((VideoNativeMetadata *)info.mData->base())->nFenceFd = -1;

        info.mCodecData = info.mData;
        // useBuffer
        err = mOMXNode->useBuffer(kPortIndexOutput, OMXBuffer::sPreset, &info.mBufferID);
        mBuffers[kPortIndexOutput].push(info);

        ALOGV("[%s] allocated meta buffer with ID %u",
                mComponentName.c_str(), info.mBufferID);
    }

    mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
    return err;
}

useBuffer傳入參數為OMXBuffer::sPreset,查看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) {
}

到OMXNodeInstance

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

進入到useBuffer_l 發現有OMX_AllocateBuffer 和 OMX_UseBuffer兩個選擇。先看mMetaDataType,它是在setPortMode時被重新設定值,在這種情況下會被設定為kMetadataBufferTypeANWBuffer

    bool isOutputGraphicMetadata = (portIndex == kPortIndexOutput) &&
            (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource ||
                    mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer);

isOutputGraphicMetaData為true,所以第一個條件不滿足,使用OMX_UseBuffer,isMetaData為true

        if (isMetadata) {
            data = new (std::nothrow) OMX_U8[allottedSize];
            if (data == NULL) {
                return NO_MEMORY;
            }
            memset(data, 0, allottedSize);

            buffer_meta = new BufferMeta(
                    params, hParams, portIndex, false /* copy */, data);
        } 
        err = OMX_UseBuffer(
            mHandle, &header, portIndex, buffer_meta,
            allottedSize, data);

 

到這兒發現,用於創建BufferMeta的IMemory和IHidlMemory都是null,真正用於BufferMeta的是重新分配的一塊buffer,說明在ACodec中創建的buffer 並沒有通過OMX_UseBuffer往下傳遞。回到ACodec中創建BufferInfo的地方看mStatus為OWNED_BY_NATIVE_WINDOW,意思就是真正的output buffer並不是由上層創建。所以在這種情況下播放時,上層通過getBuffer獲取的output buffer中是沒有數據的。

 

 1.1.2、tunnel mode

allocateOutputBuffersFromNativeWindow分配buffer

status_t ACodec::allocateOutputBuffersFromNativeWindow() {

    OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
    status_t err = configureOutputBuffersFromNativeWindow(
            &bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
    if (err != 0)
        return err;
    mNumUndequeuedBuffers = minUndequeuedBuffers;

    static_cast<Surface*>(mNativeWindow.get())
            ->getIGraphicBufferProducer()->allowAllocation(true);

    // Dequeue buffers and send them to OMX
    for (OMX_U32 i = 0; i < bufferCount; i++) {
        ANativeWindowBuffer *buf;
        int fenceFd;
        err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
        if (err != 0) {
            ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
            break;
        }

        sp<GraphicBuffer> graphicBuffer(GraphicBuffer::from(buf));
        BufferInfo info;
        info.mStatus = BufferInfo::OWNED_BY_US;
        info.mFenceFd = fenceFd;
        info.mIsReadFence = false;
        info.mRenderInfo = NULL;
        info.mGraphicBuffer = graphicBuffer;
        info.mNewGraphicBuffer = false;
        info.mDequeuedAt = mDequeueCounter;

        info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
        info.mCodecData = info.mData;

        mBuffers[kPortIndexOutput].push(info);

        IOMX::buffer_id bufferId;
        err = mOMXNode->useBuffer(kPortIndexOutput, graphicBuffer, &bufferId);
        if (err != 0) {
            ALOGE("registering GraphicBuffer %u with OMX IL component failed: "
                 "%d", i, err);
            break;
        }

        mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId;

    }

    OMX_U32 cancelStart;
    OMX_U32 cancelEnd;

    if (err != OK) {
        cancelStart = 0;
        cancelEnd = mBuffers[kPortIndexOutput].size();
    } else {
        cancelStart = bufferCount - minUndequeuedBuffers;
        cancelEnd = bufferCount;
    }

    for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
        BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
        if (info->mStatus == BufferInfo::OWNED_BY_US) {
            status_t error = cancelBufferToNativeWindow(info);
            if (err == 0) {
                err = error;
            }
        }
    }

    static_cast<Surface*>(mNativeWindow.get())
            ->getIGraphicBufferProducer()->allowAllocation(false);

    return err;
}

這里面UseBuffer的參數為GraphicBuffer,參考OMXBuffer代碼:

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

tunnel mode下portmode[out]是IOMX::kPortModePresetANWBuffer,這個portMode的設置比較隱蔽:

else if (!storingMetadataInDecodedBuffers()) {
                err = setPortMode(kPortIndexOutput, IOMX::kPortModePresetANWBuffer);
                if (err != OK) {
                    return err;
                }
            }

進入到UseBuffer中,根據BufferType判斷會走到useGraphicBuffer_l

        case OMXBuffer::kBufferTypeANWBuffer: {
            if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
                    && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
                break;
            }
            return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
        }

mMetadataType在setPortMode時被置為了kMetadataBufferTypeANWBuffer,進入到useGraphicBuffer_l看到

    if (mMetadataType[portIndex] != kMetadataBufferTypeInvalid) {
        return useGraphicBufferWithMetadata_l(
                portIndex, graphicBuffer, buffer);
    }

所以進到useGraphicBufferWithMetadata_l,

status_t OMXNodeInstance::useGraphicBufferWithMetadata_l(
        OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
        IOMX::buffer_id *buffer) {
    if (portIndex != kPortIndexOutput) {
        return BAD_VALUE;
    }

    if (mMetadataType[portIndex] != kMetadataBufferTypeGrallocSource &&
            mMetadataType[portIndex] != kMetadataBufferTypeANWBuffer) {
        return BAD_VALUE;
    }

    status_t err = useBuffer_l(portIndex, NULL, NULL, buffer);
    if (err != OK) {
        return err;
    }

    OMX_BUFFERHEADERTYPE *header = findBufferHeader(*buffer, portIndex);

    return updateGraphicBufferInMeta_l(portIndex, graphicBuffer, *buffer, header);

}

看來還是進入到了useBuffer_l當中,isOutputGraphicMetadata為true,isMetadata為true,所以使用的是OMX_UseBuffer

        if (isMetadata) {
            data = new (std::nothrow) OMX_U8[allottedSize];
            if (data == NULL) {
                return NO_MEMORY;
            }
            memset(data, 0, allottedSize);

            buffer_meta = new BufferMeta(
                    params, hParams, portIndex, false /* copy */, data);
        } 
        err = OMX_UseBuffer(
            mHandle, &header, portIndex, buffer_meta,
            allottedSize, data);

好家伙,看到這里發現和之前非tunnel mode是一樣的,但是出了useBuffer_l,再回到useGraphicBufferWithMetadata_l,看到還有一個函數updateGraphicBufferInMeta_l:

    BufferMeta *bufferMeta = (BufferMeta *)(header->pAppPrivate);
    sp<ABuffer> data = bufferMeta->getBuffer(header, false /* limit */);
    bufferMeta->setGraphicBuffer(graphicBuffer);

    else if (metaType == kMetadataBufferTypeANWBuffer
            && data->capacity() >= sizeof(VideoNativeMetadata)) {
        VideoNativeMetadata &metadata = *(VideoNativeMetadata *)(data->data());
        metadata.eType = kMetadataBufferTypeANWBuffer;
        metadata.pBuffer = graphicBuffer == NULL ? NULL : graphicBuffer->getNativeBuffer();
        metadata.nFenceFd = -1;
    } 

這里看到把OMX_BUFFERHEADERTYPE中的BufferMeta和上層傳來的graphicBuffer做了關聯,OMX和graphic公用一塊buffer,由於graphicBuffer是在ACodec創建,所以mStatus值為OWNED_BY_US

 

1.2、無surface

  無surface的情況與Input的普通模式相同

 

2、kPortIndexInput

2.1、no secure

使用的是hidl_memory

hidl_memory hidlMemToken;

auto transStatus = mAllocator[portIndex]->allocate(
        bufSize,
        [&success, &hidlMemToken](
                bool s,
                hidl_memory const& m) {
            success = s;
            hidlMemToken = m;
        });

err = mOMXNode->useBuffer(
        portIndex, hidlMemToken, &info.mBufferID);

看看BufferType

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

進入到UseBuffer中,此時portMode為kPortModePresetByteBuffer

        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) {
                    ALOGE("OMXNodeInstance useBuffer() failed to map memory");
                    return NO_MEMORY;
                }
                return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
        }

mMetadataType在setPortMode時被置為kMetadataBufferTypeInvalid,進入到useBuffer_l中:

isMetaData為false,isOutputGraphicMetadata為false,這時候看到if中有關於Quirks的判斷

    uint32_t requiresAllocateBufferBit =
        (portIndex == kPortIndexInput)
            ? kRequiresAllocateBufferOnInputPorts
            : kRequiresAllocateBufferOnOutputPorts;

    // we use useBuffer for output metadata regardless of quirks
    if (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit)) 

Quirks一般定義在media_codecs.xml中,譯為怪癖模式,在其他地方找到可以翻譯為兼容模式,示例如下:

42     <MediaCodec name="OMX.foo.bar" >
43         <Type name="something/interesting" />
44         <Type name="something/else" />
45         ...
46         <Quirk name="requires-allocate-on-input-ports" />
47         <Quirk name="requires-allocate-on-output-ports" />
48         <Quirk name="output-buffers-are-unreadable" />
49     </MediaCodec>

這種情況下,codec xml中如果定義有Quirk則進入到OMX_AllocateBuffer當中,沒有定義Quirk則使用OMX_UseBuffer

a. OMX_AllocateBuffer

    if (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit)) {
        buffer_meta = new BufferMeta(
                    params, hParams, portIndex, !isMetadata /* copy */, NULL /* data */);

        err = OMX_AllocateBuffer(
                mHandle, &header, portIndex, buffer_meta, allottedSize);
}

利用傳下來的IHidlMemory創建BufferMeta,第四個參數copy為true,這里看看BufferMeta的構造函數:

    explicit BufferMeta(
            const sp<IMemory> &mem, const sp<IHidlMemory> &hidlMemory,
            OMX_U32 portIndex, bool copy, OMX_U8 *backup)
        : mMem(mem),
          mHidlMemory(hidlMemory),
          mCopyFromOmx(portIndex == kPortIndexOutput && copy),
          mCopyToOmx(portIndex == kPortIndexInput && copy),
          mPortIndex(portIndex),
          mBackup(backup) {
    }

copy為true會讓mCopyToOMX或者mCopyFromOMX置為true,他們的作用就是使能copy,例如CopyToOMX,就是從上層的buffer中copy數據到OMX OMX_BUFFERHEADERTYPE 

中,這個方法會在emptyBuffer_l中調用到

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

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

另外再看一下buffer的所有者是OWNED_BY_US

b. OMX_UseBuffer

看到創建BufferMeta時,copy都為false,不允許數據拷貝,decoder拿不到數據,上層也拿到解碼后的數據,這明顯是不對的。

2.2、secure mode

這時候portMode為kPortModePresetSecureBuffer,直接調用OMXNodeInstance的allocateSecureBuffer獲取一個NativeHandle,BufferInfo中的data使用的睡覺哦SecureBuffer

                if (mode == IOMX::kPortModePresetSecureBuffer) {
                    void *ptr = NULL;
                    sp<NativeHandle> native_handle;
                    err = mOMXNode->allocateSecureBuffer(
                            portIndex, bufSize, &info.mBufferID,
                            &ptr, &native_handle);

                    info.mData = (native_handle == NULL)
                            ? new SecureBuffer(format, ptr, bufSize)
                            : new SecureBuffer(format, native_handle, bufSize);
                    info.mCodecData = info.mData;
                }

進入到OMXNodeInstance看到allocateSecureBuffer並不復雜,創建一個BufferMeta,其中不帶任何上層的buffer,之后直接調用OMX_AllocateBuffer創建一個OMX_BUFFERHEADERTYPE,返回給上層的是用BufferHeader創建的NativeHandle

    BufferMeta *buffer_meta = new BufferMeta(portIndex);

    OMX_BUFFERHEADERTYPE *header;

    OMX_ERRORTYPE err = OMX_AllocateBuffer(
            mHandle, &header, portIndex, buffer_meta, size);

    if (mSecureBufferType[portIndex] == kSecureBufferTypeNativeHandle) {
        *buffer_data = NULL;
        *native_handle = NativeHandle::create(
                (native_handle_t *)header->pBuffer, false /* ownsHandle */);
    } else {
        *buffer_data = header->pBuffer;
        *native_handle = NULL;
    }

上層ACodec用返回的NativeHandle創建一個SecureBuffer,這里面buffer是怎么連通的,可以閱讀OMXNodeInstance::emptyBuffer的第三個case,最后其實還是調用的emptyBuffer_l。

 

 

到這里Buffer的分配大概就了解結束,做一個總結:

Input

  non secure:上層分配一塊hidl memory,omxnode中創建一個BufferMeta,調用OMX_AllocateBuffer在創建OMX_BUFFERHEADERTYPE(暫不了解BufferMeta在該方法中做什么用),允許BufferMeta與OMX_BUFFERHEADERTYPE中的buffer相互做數據拷貝。

  secure:調用OMX_AllocateBuffer返回一個NativeHandle,用這個handle創建SecureBuffer

Output

  無surface,與input non secure相同

  有surface

    non tunnel

      上層創建的buffer並不與底層相關聯,上層無法獲取到ouput data,omxNode中會重新創建一塊buffer,利用這塊buffer創建BufferMeta,並調用OMX_UseBuffer。既然output data並沒有送給上層,那么渲染肯定是另有途徑

    tunnel mode

      上層從graphic獲取buffer,omxNode同樣也會創建一塊buffer,並調用OMX_UseBuffer,但是之后會把graphic buffer與OMX_BUFFERHEADERTYPE相關聯,output data直接送給graphic


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM