Android 13 - Media框架(22)- ACodec(四)

发布时间:2023年12月18日

前面两节我们了解了 ACodec 的创建及配置流程,配置完成后 ACodec 进入了 LoadedState,这一节开始将会了解 ACodec 的启动过程。

调用 ACodec::initiateStart 方法发出的 kWhatStart 消息将有 LoadedState 状态来处理,这个方法会向 OMX 组件发送命令OMX_CommandStateSet ,将组件的状态设定为 OMX_StateIdle,之后将 ACodec 的状态切换到中间等待状态 LoadedToIdleState

void ACodec::LoadedState::onStart() {
    ALOGV("onStart");

    status_t err = mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
    if (err != OK) {
        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
    } else {
        mCodec->changeState(mCodec->mLoadedToIdleState);
    }
}

切换状态时调用 LoadedToIdleState 的 stateEntered 方法,来为 OMX 组件分配 buffer,这是很关键的一步。

void ACodec::LoadedToIdleState::stateEntered() {
    ALOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());

    status_t err;
    if ((err = allocateBuffers()) != OK) {
        ALOGE("Failed to allocate buffers after transitioning to IDLE state "
             "(error 0x%08x)",
             err);

        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));

        mCodec->mOMXNode->sendCommand(
                OMX_CommandStateSet, OMX_StateLoaded);
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexInput)) {
            mCodec->freeBuffersOnPort(kPortIndexInput);
        }
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexOutput)) {
            mCodec->freeBuffersOnPort(kPortIndexOutput);
        }

        mCodec->changeState(mCodec->mLoadedState);
    }
}

stateEntered 中主要调用了 allocateBuffers 方法,如果返回结果有问题,那么会将状态回滚到 LoadedState。

status_t ACodec::LoadedToIdleState::allocateBuffers() {
    status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
    if (err != OK) {
        return err;
    }

    err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
    if (err != OK) {
        return err;
    }

    mCodec->mCallback->onStartCompleted();

    return OK;
}

allocateBuffers 中将 buffer 分配完成后,就会调用 callback 通知 MediaCodec 完成阻塞调用了。我们上面说到将 OMX 组件状态设置为 OMX_StateIdle,这个状态下,OMX 组件处理这个消息时应该是处于一个阻塞的状态,阻塞是在等待上层 buffer 分配完成,一旦完成后就会向 ACodec 发送一条消息,表示事件处理完成了(buffer准备完成),这时候 ACodec 将会再向 OMX 组件发送状态设置命令,将组件状态设置为 OMX_StateExecuting,组件就正式开始工作了。

bool ACodec::LoadedToIdleState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
    switch (event) {
        case OMX_EventCmdComplete:
        {
            status_t err = OK;
            if (data1 != (OMX_U32)OMX_CommandStateSet
                    || data2 != (OMX_U32)OMX_StateIdle) {
                ALOGE("Unexpected command completion in LoadedToIdleState: %s(%u) %s(%u)",
                        asString((OMX_COMMANDTYPE)data1), data1,
                        asString((OMX_STATETYPE)data2), data2);
                err = FAILED_TRANSACTION;
            }

            if (err == OK) {
                err = mCodec->mOMXNode->sendCommand(
                    OMX_CommandStateSet, OMX_StateExecuting);
            }

            if (err != OK) {
                mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
            } else {
                mCodec->changeState(mCodec->mIdleToExecutingState);
            }

            return true;
        }

        default:
            return BaseState::onOMXEvent(event, data1, data2);
    }
}

上面这段主要是要理解,OMX组件在处理 OMX_StateIdle 这条命令时会处在一个阻塞的状态

接下来我们就要一起看 buffer 是如何分配的,如果已经了解我们上一节看的 Port Mode,那么这部分还是很简单的。

代码比较长,我们把代码分成两部分来看:

  1. 有native window的情况下分配output buffer;

  2. 对 input buffer ,以及没有 native window 时的 output buffer 进行分配;

首先我们来看第一部分:

    // 1、在有native window的情况下分配output buffer
    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
        if (storingMetadataInDecodedBuffers()) {
            err = allocateOutputMetadataBuffers();
        } else {
            err = allocateOutputBuffersFromNativeWindow();
        }
    }

我们在上一篇中了解到有native window 时,output mode会有两种情况,一种是 tunnel mode;另一种是 kPortModeDynamicANWBuffer,也就是所谓的 MetaData mode,这里我们先看这种模式。

1、allocateOutputMetadataBuffers

status_t ACodec::allocateOutputMetadataBuffers() {
    CHECK(storingMetadataInDecodedBuffers());
	// 1、调用方法获取 native window 可用的 output buffer 数量以及大小
    OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
    status_t err = configureOutputBuffersFromNativeWindow(
            &bufferCount, &bufferSize, &minUndequeuedBuffers,
            mFlags & kFlagPreregisterMetadataBuffers /* preregister */);
    if (err != OK)
        return err;
    mNumUndequeuedBuffers = minUndequeuedBuffers;

    ALOGV("[%s] Allocating %u meta buffers on output port",
         mComponentName.c_str(), bufferCount);
	// 2、创建 对应数量的 BufferInfo
    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;
		// 3、创建一个 MediaCodecBuffer
        info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));

        // Initialize fence fd to -1 to avoid warning in freeBuffer().
        ((VideoNativeMetadata *)info.mData->base())->nFenceFd = -1;

        info.mCodecData = info.mData;
		// 4、调用 useBuffer 让 OMX 组件使用一块空的 buffer,并且回传 ID
        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);
    }
	// 5、计算需要提交的 output buffer 数量
    mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
    return err;
}

这个方法大致做了以下5个事情:

  1. 获取 native window 可用的 output buffer 数量以及大小;
  2. 创建对应数量的 BufferInfo;
  3. 为 BufferInfo 中的 mData 字段分配空间;
  4. 调用 useBuffer 让 OMX 组件使用一块空的 buffer,并且回传 ID 并与当前的 BufferInfo 相绑定;
  5. 计算需要提交的 output buffer 数量;

1.1、configureOutputBuffersFromNativeWindow

status_t ACodec::configureOutputBuffersFromNativeWindow(
        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
        OMX_U32 *minUndequeuedBuffers, bool preregister) {

    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;
	// 获取 OMX 组件定义的 output port 的定义,定义中会有 output buffer 的数量
    status_t err = mOMXNode->getParameter(
            OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err == OK) {
        err = setupNativeWindowSizeFormatAndUsage(
                mNativeWindow.get(), &mNativeWindowUsageBits,
                preregister && !mTunneled /* reconnect */);
    }
    if (err != OK) {
        mNativeWindowUsageBits = 0;
        return err;
    }
	// 设置从 nativewindow 中获取buffer这个动作为阻塞的
    static_cast<Surface *>(mNativeWindow.get())->setDequeueTimeout(-1);

    // Exits here for tunneled video playback codecs -- i.e. skips native window
    // buffer allocation step as this is managed by the tunneled OMX omponent
    // itself and explicitly sets def.nBufferCountActual to 0.
    // 如果是 tunnel mode,那么端口的 buffer 数量为0,不需要从上层获取 output buffer
    if (mTunneled) {
        ALOGV("Tunneled Playback: skipping native window buffer allocation.");
        def.nBufferCountActual = 0;
        err = mOMXNode->setParameter(
                OMX_IndexParamPortDefinition, &def, sizeof(def));

        *minUndequeuedBuffers = 0;
        *bufferCount = 0;
        *bufferSize = 0;
        return err;
    }
	// 从 native window 获取最小的还未出队列的 buffer 数量
    *minUndequeuedBuffers = 0;
    err = mNativeWindow->query(
            mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
            (int *)minUndequeuedBuffers);

    if (err != 0) {
        ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
                strerror(-err), -err);
        return err;
    }

	// 重新计算OMX 组件上 output buffer 的数量
    // FIXME: assume that surface is controlled by app (native window
    // returns the number for the case when surface is not controlled by app)
    // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported
    // For now, try to allocate 1 more buffer, but don't fail if unsuccessful

    // Use conservative allocation while also trying to reduce starvation
    //
    // 1. allocate at least nBufferCountMin + minUndequeuedBuffers - that is the
    //    minimum needed for the consumer to be able to work
    // 2. try to allocate two (2) additional buffers to reduce starvation from
    //    the consumer
    //    plus an extra buffer to account for incorrect minUndequeuedBufs
    for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
    	// 尝试将output buffer 的数量设置为端口所需的最小数量 +  nativewindow最小未出队列的buffer数量 + 3
        OMX_U32 newBufferCount =
            def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers;
        def.nBufferCountActual = newBufferCount;
        err = mOMXNode->setParameter(
                OMX_IndexParamPortDefinition, &def, sizeof(def));

        if (err == OK) {
            *minUndequeuedBuffers += extraBuffers;
            break;
        }

        ALOGW("[%s] setting nBufferCountActual to %u failed: %d",
                mComponentName.c_str(), newBufferCount, err);
        /* exit condition */
        if (extraBuffers == 0) {
            return err;
        }
    }
	// 设置 native window 的buffer数量
    err = native_window_set_buffer_count(
            mNativeWindow.get(), def.nBufferCountActual);

    if (err != 0) {
        ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
                -err);
        return err;
    }
	// 设置 buffercount 和 buffersize
    *bufferCount = def.nBufferCountActual;
    *bufferSize =  def.nBufferSize;
    return err;
}

由于不了解 Graphic 相关的内容,所以这部分只能边看边猜,以下是我自己的理解,可能有误

  1. 计算output buffer 数量时首先会从 OMX 组件获取输出端口的配置,配置中定义有最小和最大需要的 buffer 数量;
  2. 将从 native window 中 deque buffer 这个动作设置为阻塞的;
  3. 如果是 tunnel mode,不会从上层获取 output buffer,buffer 的数量设置为0;
  4. 从 native window 获取最小的还未出队列的 buffer 数量;
  5. 重新计算OMX 组件上真实使用的 output buffer 的数量;
  6. 设置 native window 的 buffer数量为真实使用的buffer 的数量。

这里对 nBufferCountActual (真实使用的buffer数量)的计算比较令人疑惑,上面的代码中有一个循环,会尝试将 nBufferCountMin (最小 buffer 数量)+ minUndequeuedBuffers + extra 作为真实值,并且尝试设定给组件,只要这个值没有超过最大值就可以成功设定。

这里的 minUndequeuedBuffers 代表什么意思呢?上文中的 mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers 又是代表什么意思呢?

我的理解是native window 有一套自己的buffer管理机制,并不是一开始解码就把所有的output buffer全部分配出来,会根据需求来管理需要的buffer数量。那么开始解码需要有多个块output buffer呢?

支持OMX运行所需要的最少的output buffer 数量为 nBufferCountMin,开始解码时就先分配这么多个(mMetadataBuffersToSubmit 个),如果需要更多再去剩余还未使用的buffer( minUndequeuedBuffers 个)中获取新的 buffer。

1.2、BufferInfo

接下来我们了解一下 BufferInfo,它是一个容器用于记录分配出的 input/output buffer/Handle/MetaData,并且记录下它们所携带的一些信息

    struct BufferInfo {
        enum Status {
            OWNED_BY_US,
            OWNED_BY_COMPONENT,
            OWNED_BY_UPSTREAM,
            OWNED_BY_DOWNSTREAM,
            OWNED_BY_NATIVE_WINDOW,
            UNRECOGNIZED,            // not a tracked buffer
        };

        static inline Status getSafeStatus(BufferInfo *info) {
            return info == NULL ? UNRECOGNIZED : info->mStatus;
        }

        IOMX::buffer_id mBufferID;
        Status mStatus;
        unsigned mDequeuedAt;

        sp<MediaCodecBuffer> mData;  // the client's buffer; if not using data conversion, this is
                                     // the codec buffer; otherwise, it is allocated separately
        sp<RefBase> mMemRef;         // and a reference to the IMemory, so it does not go away
        sp<MediaCodecBuffer> mCodecData;  // the codec's buffer
        sp<RefBase> mCodecRef;            // and a reference to the IMemory

        sp<GraphicBuffer> mGraphicBuffer;
        bool mNewGraphicBuffer;
        int mFenceFd;
        FrameRenderTracker::Info *mRenderInfo;

        // The following field and 4 methods are used for debugging only
        bool mIsReadFence;
        // Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored.
        void setReadFence(int fenceFd, const char *dbg);
        void setWriteFence(int fenceFd, const char *dbg);
        // Log error, if the current fence is not a read/write fence.
        void checkReadFence(const char *dbg);
        void checkWriteFence(const char *dbg);
    };

我们首先要注意的是ACodec::BufferInfo 和 ACodecBufferChannel::BufferInfo 以及 MediaCodec::BufferInfo是三个不一样的内容,我们这里只了解 ACodec::BufferInfo。

  • Status:表示当前 buffer 的使用者
    • OWNED_BY_US:表示 buffer 使用者是 ACodec;
    • OWNED_BY_COMPONENT:表示把 buffer 交给 OMX 组件;
    • OWNED_BY_UPSTREAM:表示 input buffer 使用完成回传给上层(MediaCodec);
    • OWNED_BY_DOWNSTREAM:表示 output buffer 填充好回传给上层;
    • OWNED_BY_NATIVE_WINDOW:表示 buffer 使用者是 native window

ACodec::BufferInfo 中还记录有 buffer id,ACodec 和 OMXNode 进行 buffer 信息传递都是通过该 id 来完成,id 由 OMXNode 创建。

mGraphicBuffer 指向从 native window 中dequeue 出来的 buffer;mNewGraphicBuffer 表示 dequeue出的buffer 是否被使用;mFenceFd 记录了 graphic buffer fence;

ACodec::BufferInfo 中有两个 MediaCodecBuffer,它记录的是分配出的buffer或者handle

    const sp<AMessage> mMeta;
    sp<AMessage> mFormat;
    const sp<ABuffer> mBuffer;

MediaCodecBuffer 有3个成员,一个是 mMeta用于存储 buffer 的元信息,一个是 mFormat 用于存储解码数据携带的格式(例如crop),还有一个就是 mBuffer 用于存储真正的数据或句柄。

mDequeuedAt 用于记录 output buffer(native window buffer)被使用的次数。

1.3、useBuffer

再回到第一节allocateOutputMetadataBuffers当中,可以看到创建 bufferInfo 时,状态为 OWNED_BY_NATIVE_WINDOW,说明此时 buffer 还是在 native window 当中,并未真正被使用。

这里的 mData 指向一个 MediaCodecBuffer,MediaCodecBuffer 中的 mBuffer 的大小是之前获取到output buffer 的大小,后面的使用中又把这块 Buffer 强转为了 VideoNativeMetadata,所以我们就可以猜测,这里的 bufferSize 就是 VideoNativeMetadata 这个结构体的大小。

创建完 BufferInfo 之后,一般来说需要调用 useBuffer 把获取到的 graphic buffer 传递给 OMX,但是这里并没有真正 dequeue 出来,所以就设了一个空buffer(OMXBuffer::sPreset)。

这种情况下 BufferInfo 并不需要使用 mData 以及 mCodeData,但是为了统一,还是创建了一个 VideoNativeMetaData 对象。

最后分配出的 buffer 会统一存储到数组 mBuffer 当中。

1.4、allocateOutputBuffersFromNativeWindow

这里会简单了解 tunnel mode下有 allocateBuffersOnPort 的流程,这种情况下会调用到 allocateOutputBuffersFromNativeWindow,进入方法后首先还是会调用 configureOutputBuffersFromNativeWindow 计算output buffer 的数量,这里我们在上一节已经看过了,tunnel mode 会直接返回0,所以 allocateOutputBuffersFromNativeWindow 中会什么都不做,因此,这种情况下上层不会接触到 output buffer。

2、input buffer allocate

input buffer 和无 native window下 output buffer的分配流程是一致的,本质上是分配一块可以通过 HIDL 传递的共享内存。

但是如果是 secure mode,input buffer 就需要让 OMX 分配一个 buffer handle,再把handle传回来。

        OMX_PARAM_PORTDEFINITIONTYPE def;
        InitOMXParams(&def);
        def.nPortIndex = portIndex;

        err = mOMXNode->getParameter(
                OMX_IndexParamPortDefinition, &def, sizeof(def));

        if (err == OK) {
            const IOMX::PortMode &mode = mPortMode[portIndex];
            size_t bufSize = def.nBufferSize;
            // Always allocate VideoNativeMetadata if using ANWBuffer.
            // OMX might use gralloc source internally, but we don't share
            // metadata buffer with OMX, OMX has its own headers.
            if (mode == IOMX::kPortModeDynamicANWBuffer) {
                bufSize = sizeof(VideoNativeMetadata);
            } else if (mode == IOMX::kPortModeDynamicNativeHandle) {
                bufSize = sizeof(VideoNativeHandleMetadata);
            }

            size_t conversionBufferSize = 0;

            sp<DataConverter> converter = mConverter[portIndex];
            if (converter != NULL) {
                // here we assume conversions of max 4:1, so result fits in int32
                if (portIndex == kPortIndexInput) {
                    conversionBufferSize = converter->sourceSize(bufSize);
                } else {
                    conversionBufferSize = converter->targetSize(bufSize);
                }
            }

            size_t alignment = 32; // This is the value currently returned by
                                   // MemoryDealer::getAllocationAlignment().
                                   // TODO: Fix this when Treble has
                                   // MemoryHeap/MemoryDealer.

            ALOGV("[%s] Allocating %u buffers of size %zu (from %u using %s) on %s port",
                    mComponentName.c_str(),
                    def.nBufferCountActual, bufSize, def.nBufferSize, asString(mode),
                    portIndex == kPortIndexInput ? "input" : "output");

            // verify buffer sizes to avoid overflow in align()
            if (bufSize == 0 || max(bufSize, conversionBufferSize) > kMaxCodecBufferSize) {
                ALOGE("b/22885421");
                return NO_MEMORY;
            }

            // don't modify bufSize as OMX may not expect it to increase after negotiation
            size_t alignedSize = align(bufSize, alignment);
            size_t alignedConvSize = align(conversionBufferSize, alignment);
            if (def.nBufferCountActual > SIZE_MAX / (alignedSize + alignedConvSize)) {
                ALOGE("b/22885421");
                return NO_MEMORY;
            }

            if (mode != IOMX::kPortModePresetSecureBuffer) {
                mAllocator[portIndex] = TAllocator::getService("ashmem");
                if (mAllocator[portIndex] == nullptr) {
                    ALOGE("hidl allocator on port %d is null",
                            (int)portIndex);
                    return NO_MEMORY;
                }
                // TODO: When Treble has MemoryHeap/MemoryDealer, we should
                // specify the heap size to be
                // def.nBufferCountActual * (alignedSize + alignedConvSize).
            }

            const sp<AMessage> &format =
                    portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
            for (OMX_U32 i = 0; i < def.nBufferCountActual && err == OK; ++i) {
                hidl_memory hidlMemToken;
                sp<TMemory> hidlMem;
                sp<IMemory> mem;

                BufferInfo info;
                info.mStatus = BufferInfo::OWNED_BY_US;
                info.mFenceFd = -1;
                info.mRenderInfo = NULL;
                info.mGraphicBuffer = NULL;
                info.mNewGraphicBuffer = false;

                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;
                } else {
                    bool success;
                    auto transStatus = mAllocator[portIndex]->allocate(
                            bufSize,
                            [&success, &hidlMemToken](
                                    bool s,
                                    hidl_memory const& m) {
                                success = s;
                                hidlMemToken = m;
                            });

                    if (!transStatus.isOk()) {
                        ALOGE("hidl's AshmemAllocator failed at the "
                                "transport: %s",
                                transStatus.description().c_str());
                        return NO_MEMORY;
                    }
                    if (!success) {
                        return NO_MEMORY;
                    }
                    hidlMem = mapMemory(hidlMemToken);
                    if (hidlMem == nullptr) {
                        return NO_MEMORY;
                    }
                    err = mOMXNode->useBuffer(
                            portIndex, hidlMemToken, &info.mBufferID);

                    if (mode == IOMX::kPortModeDynamicANWBuffer) {
                        VideoNativeMetadata* metaData = (VideoNativeMetadata*)(
                                (void*)hidlMem->getPointer());
                        metaData->nFenceFd = -1;
                    }

                    info.mCodecData = new SharedMemoryBuffer(
                            format, hidlMem);
                    info.mCodecRef = hidlMem;

                    // if we require conversion, allocate conversion buffer for client use;
                    // otherwise, reuse codec buffer
                    if (mConverter[portIndex] != NULL) {
                        CHECK_GT(conversionBufferSize, (size_t)0);
                        bool success;
                        mAllocator[portIndex]->allocate(
                                conversionBufferSize,
                                [&success, &hidlMemToken](
                                        bool s,
                                        hidl_memory const& m) {
                                    success = s;
                                    hidlMemToken = m;
                                });
                        if (!success) {
                            return NO_MEMORY;
                        }
                        hidlMem = mapMemory(hidlMemToken);
                        if (hidlMem == nullptr) {
                            return NO_MEMORY;
                        }
                        info.mData = new SharedMemoryBuffer(format, hidlMem);
                        info.mMemRef = hidlMem;
                    } else {
                        info.mData = info.mCodecData;
                        info.mMemRef = info.mCodecRef;
                    }
                }

                mBuffers[portIndex].push(info);

套路还是一样的,首先获取 port def,拿到 buffer 数量,如果是secure mode,那么就调用 allocateSecureBuffer 分配 native_handle,再封装为 SecureBuffer

如果是普通buffer,那么就调用共享内存分配一块hidl_memory ,然后在ACodec层直接映射成为 TMemory,再封装为 SharedMemoryBuffer;除此之外,还要调用 useBuffer,将分配的共享内存传递给 OMX。

    std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
        array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
    }
    if (portIndex == kPortIndexInput) {
        mBufferChannel->setInputBufferArray(array);
    } else if (portIndex == kPortIndexOutput) {
        mBufferChannel->setOutputBufferArray(array);
    } else {
        TRESPASS();
    }

当一个端口的buffer分配完成后,会把所有创建出来的BufferInfo中的mData成员(MediaCodecBuffer)组织成一个 vector,并且交由 ACodecBufferChannel 管理。

到这里,buffer的分配就完成了,buffer 分配完成后,OMX组件收到所有的 buffer,结束 IDLE 状态,把状态设定完成的信息回传给 ACodec,ACodec 接收到之后会再下命令让 OMX 组件进入 OMX_StateExecuting 状态,同时 ACodec 进入 IdleToExecuting 状态。

3、IdleToExecutingState

进入 IdleToExecutingState 状态时并不会有什么动作,当 OMX 组件返回状态设定完成的消息时,ACodec 会先调用 ExecutingState 的 resume 方法,然后正式进入 ExecutingState,这部分我们下节再了解。

bool ACodec::IdleToExecutingState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
    switch (event) {
        case OMX_EventCmdComplete:
        {
            if (data1 != (OMX_U32)OMX_CommandStateSet
                    || data2 != (OMX_U32)OMX_StateExecuting) {
                ALOGE("Unexpected command completion in IdleToExecutingState: %s(%u) %s(%u)",
                        asString((OMX_COMMANDTYPE)data1), data1,
                        asString((OMX_STATETYPE)data2), data2);
                mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
                return true;
            }

            mCodec->mExecutingState->resume();
            mCodec->changeState(mCodec->mExecutingState);

            return true;
        }

        default:
            return BaseState::onOMXEvent(event, data1, data2);
    }
}
文章来源:https://blog.csdn.net/qq_41828351/article/details/135005680
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。