【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【04】

承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【03】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

【此章节小节编号就接着上一章节排列】
8.3、mCodec->changeState(mCodec->mLoadedToIdleState)实现分析:
扭转状态机状态为IDLE状态机实现者,根据最开始状态机实现分析可知,将会执行该状态实现的进入方法
备注:该流程和前面的8.2流程将会在8.2流程进入到具体组件后同步执行,因为8.2中命令到达组件后组件内部是异步线程回调通知的。

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

    status_t err;
    // 分配(输入输出)缓冲区buffer
    // 见下面分析
    if ((err = allocateBuffers()) != OK) {
    	// 分配失败时
        ALOGE("Failed to allocate buffers after transitioning to IDLE state "
             "(error 0x%08x)",
             err);

        // 通知MediaCodec回调监听callback该错误发生通知
        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));

        // 然后必须将底层状态扭转为上一个组件已加载状态,让底层组件做关于本次失败的清除工作
        mCodec->mOMXNode->sendCommand(
                OMX_CommandStateSet, OMX_StateLoaded);
        // 该方法名非常明确的告诉我们了它的功能:
        // 检查你(底层组件)的所有缓冲区buffer是否属于我们(上层ACodec),
        // 为true则表示属于上层分配的,此时应该由上层发起【freeBuffersOnPort】释放buffer端口的缓冲区buffer
        // 备注:关于它们的实现处理将会在后续有时间分析的reset和flush流程中分析到,目前只提一下实现原理 TODO
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexInput)) {
            mCodec->freeBuffersOnPort(kPortIndexInput);
        }
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexOutput)) {
            mCodec->freeBuffersOnPort(kPortIndexOutput);
        }
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexInputExtradata)) {
            mCodec->freeBuffersOnPort(kPortIndexInputExtradata);
        }
        if (mCodec->allYourBuffersAreBelongToUs(kPortIndexOutputExtradata)) {
            mCodec->freeBuffersOnPort(kPortIndexOutputExtradata);
        }
		
		// 此处提醒一下,LoadedState这个状态时表示底层组件已加载状态,
		// 但不是编解码器已工作状态,而这里是在分配缓冲区buffer失败的回退状态处理,也就是返回上一个状态
        mCodec->changeState(mCodec->mLoadedState);
    }
}

allocateBuffers()实现分析:
分配(所有可能的buffer端口)缓冲区buffer

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::LoadedToIdleState::allocateBuffers() {
	// 分配(输入端口)缓冲区buffer
	// 见下面的分析
    status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
    if (err != OK) {
        return err;
    }

    // 分配(输出端口)缓冲区buffer
    err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
    if (err != OK) {
        return err;
    }
    // 根据上面的分析可知,该方法在处理这两个输入输出额外数据端口索引buffer时,实际上为空实现
    err = mCodec->allocateBuffersOnPort(kPortIndexInputExtradata);
    err = mCodec->allocateBuffersOnPort(kPortIndexOutputExtradata);
    if (err != OK) {
        err = OK; // Ignore Extradata buffer allocation failure
    }

    // 执行MediaCodec的回调监听类的该方法,通知ACodec及其底层组件启动完成事件
    // 见上面流程之后的8.3.7小节分析
    mCodec->mCallback->onStartCompleted();

    return OK;
}

mCodec->allocateBuffersOnPort(kPortIndexInput)实现分析:
分配(输入输出端口)缓冲区buffer

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
    // 对于这两种额外数据buffer端口索引直接return
    if (portIndex == kPortIndexInputExtradata ||
            portIndex == kPortIndexOutputExtradata) {
        return OK;
    }

    // 只处理分配这两种输入输出端口buffer
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);

    // 需要被分配缓冲区buffer的端口索引对于的这两个buffer缓冲区必须此次前为空
    CHECK(mAllocator[portIndex] == NULL);
    CHECK(mBuffers[portIndex].isEmpty());

    status_t err;
    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
    	// Surface不为空并且端口索引为输出索引时
    	// 判断是否应该将元数据存储在解码的缓冲区中
    	// 见8.3.1小节分析
        if (storingMetadataInDecodedBuffers()) {
        	// 分配输出元数据缓冲区buffer,将元数据存储在该(解码的)缓冲区中
        	// 见8.3.2小节分析
            err = allocateOutputMetadataBuffers();
        } else {
        	// 否的话
        	// 从Surface中分配输出缓冲区Buffer
        	// 见8.3.3小节分析
            err = allocateOutputBuffersFromNativeWindow();
        }
    } else {
    	// Surface为空或非输出端口时,如输入端口时
    	// 创建初始化OMX该参数结构对象
        OMX_PARAM_PORTDEFINITIONTYPE def;
        InitOMXParams(&def);
        def.nPortIndex = portIndex;

        // 获取底层组件该索引参数对象配置信息
        err = mOMXNode->getParameter(
                OMX_IndexParamPortDefinition, &def, sizeof(def));

        if (err == OK) {
        	// 获取成功时
        	// 对应端口索引的端口Buffer模式类型
            const IOMX::PortMode &mode = mPortMode[portIndex];
            // 获取底层每个buffer大小(单位字节),但下面可能会被重新赋值
            size_t bufSize = def.nBufferSize;
            // 若是有ANWBuffer则总是分配VideoNativeMetadata元数据Buffer
            // 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.
            // 根据不同端口模式,计算元数据Buffer结构占用内存大小
            if (mode == IOMX::kPortModeDynamicANWBuffer) {
                bufSize = sizeof(VideoNativeMetadata);
            } else if (mode == IOMX::kPortModeDynamicNativeHandle) {
                bufSize = sizeof(VideoNativeHandleMetadata);
            }

            // Buffer转换大小
            size_t conversionBufferSize = 0;

            // 数据转换器此前有分析阐述过,不再分析,理想状态是不需要数据转换器(也就是数据存储单元位bit数相同时)
            sp<DataConverter> converter = mConverter[portIndex];
            if (converter != NULL) {
                // here we assume sane conversions of max 4:1, so result fits in int32
                // 这里我们假设最大4:1的正常转换,因此结果适合于int32
                // 根据不同端口类型计算Buffer转换需要的大小
                // 备注:其实它这个数据转换器实现也比较有意思的,可自行查看
                if (portIndex == kPortIndexInput) {
                    conversionBufferSize = converter->sourceSize(bufSize);
                } else {
                    conversionBufferSize = converter->targetSize(bufSize);
                }
            }

            // 字节对齐为32倍数
            size_t alignment = 32; // This is the value currently returned by
                                   // MemoryDealer::getAllocationAlignment().
                                   // TODO: Fix this when Treble has
                                   // MemoryHeap/MemoryDealer.

            // 打印将要分配的缓冲区实际buffer个数、每个buffer大小
            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");

            // 验证buffer大小避免为0或溢出,不能大于编解码器最大允许Buffer大小
            // kMaxCodecBufferSize = 8192 * 4096 * 4, // 8K RGBA
            // 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
            // 不要修改bufSize,因为OMX可能不会期望它在协商后增加
            // 字节对齐为32倍数,得到最终对齐字节数
            // align方法实现分析,见8.3.4小节分析
            size_t alignedSize = align(bufSize, alignment);
            size_t alignedConvSize = align(conversionBufferSize, alignment);
            // 插件缓冲区所有Buffer字节总和不能大于最大SIZE_MAX 
            if (def.nBufferCountActual > SIZE_MAX / (alignedSize + alignedConvSize)) {
                ALOGE("b/22885421");
                return NO_MEMORY;
            }

            if (mode != IOMX::kPortModePresetSecureBuffer) {
            	// 非加密Buffer时
            	// 获取系统共享内存分配器“ashmem”服务
            	// 备注:关于它的实现暂不分析,只需要知道是分配共享内存即可。
            	// 匿名共享内存(ashmem),为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。
                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).
            }

            // 获取对应端口Buffer格式
            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;

                // 创建Buffer信息结构对象
                BufferInfo info;
                // 标记Buffer所有权
                info.mStatus = BufferInfo::OWNED_BY_US;
                info.mFenceFd = -1;
                info.mRenderInfo = NULL;
                info.mGraphicBuffer = NULL;
                info.mNewGraphicBuffer = false;

                if (mode == IOMX::kPortModePresetSecureBuffer) {
                	// 加密Buffer时,此流程暂不分析,通常非该类型
                    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 {
                	// 非加密Buffer时
                	// 是否分配内存成功
                    bool success;
                    // 分配该Buffer端口指定大小bufSize的匿名共享内存块hidl_memory
                    // 该方法传入的是匿名回调方法,早前相关流程有分析过,该方法是HIDL实现的
                    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;
                    }
                    // OMXNode使用该分配的Buffer,见前面分析
                    err = mOMXNode->useBuffer(
                            portIndex, hidlMemToken, &info.mBufferID);

                    if (mode == IOMX::kPortModeDynamicANWBuffer) {
                    	// kPortModeDynamicANWBuffer端口模式时
                    	// 直接强转其OMX分配的数据类型
                        VideoNativeMetadata* metaData = (VideoNativeMetadata*)(
                                (void*)hidlMem->getPointer());
                        metaData->nFenceFd = -1;
                    }

                    // 创建共享内存Buffer
                    // SharedMemoryBuffer是MediaCodecBuffer的子类,
                    // 其实际它只是将IMemory和TMemory这两种Buffer数据访问指针给了父类,
                    // 最终还是使用ABuffer类管理内存数据
                    info.mCodecData = new SharedMemoryBuffer(
                            format, hidlMem);
                    // 可以看出此字段缓存该共享内存对象引用
                    info.mCodecRef = hidlMem;

                    // if we require conversion, allocate conversion buffer for client use;
                    // otherwise, reuse codec buffer
                    // 根据注释和前面对于数据转换器的分析可知:
                    // 若不需要转换器时则Client端Buffer直接使用Codec端共享内存Buffer,
                    // 需要则将会重新分配新的匿名共享内存给Client端缓冲区使用
                    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;
                    }
                }

                // 添加Buffer到输入端口缓冲区队列中
                mBuffers[portIndex].push(info);
            }
        }
    }

    if (err != OK) {
        return err;
    }

    // 创建对应Buffer端口【BufferAndId】列表数据,用于缓存Buffer和Buffer ID的映射
    // ACodecBufferChannel::BufferAndId就定义了两个字段:sp<MediaCodecBuffer> mBuffer; 和 IOMX::buffer_id mBufferId;
    std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
    for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
    	// 初始化该列表,使用匿名创建item结构对象
        array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
    }
    // 根据端口类型,执行此前初始化流程中创建的编解码器缓冲区Buffer通道mBufferChannel
    // 注意:ACodecBufferChannel该类非常重要,它就是通知MediaCodec填充输入数据或消耗输出数据事件
    if (portIndex == kPortIndexInput) {
    	// 设置输入Buffer列表
    	// 见8.3.5小节分析
        mBufferChannel->setInputBufferArray(array);
    } else if (portIndex == kPortIndexOutput) {
    	// 设置输出Buffer列表
    	// 见8.3.6小节分析
        mBufferChannel->setOutputBufferArray(array);
    } else {
        TRESPASS();
    }

    return OK;
}

8.3.1、storingMetadataInDecodedBuffers()实现分析:
Surface不为空并且端口索引为输出索引时,判断是否应该将元数据存储在解码的缓冲区中

// [frameworks/av/media/libstagefright/ACodec.cpp]
    inline bool storingMetadataInDecodedBuffers() {
    	// 该方法为ACodec的内联函数实现
    	// 通过判断输出buffer端口模式是否为动态AndroidNativeWindow数据类型,并且为解码器时,则为true,否则为false
    	// 备注:由前面分析举例SoftAVCDec组件时,获取到的该输出端口模式为【IOMX::kPortModePresetByteBuffer】,因此此时返回false。
    	// 另外硬解码器时该返回值通常为true,也就是说硬解码器使用的缓冲区数据类型支持为【kPortModeDynamicANWBuffer】。
        return (mPortMode[kPortIndexOutput] == IOMX::kPortModeDynamicANWBuffer) && !mIsEncoder;
    }

8.3.2、allocateOutputMetadataBuffers()实现分析:
分配输出元数据缓冲区buffer,将元数据存储在该(解码的)缓冲区中

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::allocateOutputMetadataBuffers() {
    CHECK(storingMetadataInDecodedBuffers());

    OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
    // 从Surface中获取配置的输出缓冲区buffer个数、(每个)buffer大小、最小未出队列缓冲区buffer个数
    // 见8.3.2.1小节分析
    status_t err = configureOutputBuffersFromNativeWindow(
            &bufferCount, &bufferSize, &minUndequeuedBuffers,
            mFlags & kFlagIsSecure /* preregister */);
    if (err != OK)
        return err;
    // 全局缓存新的Surface的最小未出队缓冲区个数    
    mNumUndequeuedBuffers = minUndequeuedBuffers;

    ALOGV("[%s] Allocating %u meta buffers on output port",
         mComponentName.c_str(), bufferCount);

    // 循环创建缓冲区buffer
    for (OMX_U32 i = 0; i < bufferCount; i++) {
    	// buffer信息结构对象,并初始化,该结构非常重要
    	// 见8.3.2.2小节分析
        BufferInfo info;
        // 标记当前buffer数据状态为Surface拥有权
        info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
        info.mFenceFd = -1;
        info.mRenderInfo = NULL;
        info.mGraphicBuffer = NULL;
        info.mNewGraphicBuffer = false;
        // 出队列buffer计数值
        info.mDequeuedAt = mDequeueCounter;

        // 创建一个bufferSize大小的Client客户端编解码buffer存储对象,它将内部缓存输出格式信息,并代理ABuffer的功能
        info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));

        // base()方法最终指向ABuffer的该方法,其指向的是携带真实数据对象,将其转换为VideoNativeMetadata,
        // 也就是客户端使用的buffer缓存数据为VideoNativeMetadata数据类型即视频native元数据结构。
        // 该结构声明定义见8.3.2.3小节分析
        // Initialize fence fd to -1 to avoid warning in freeBuffer().
        ((VideoNativeMetadata *)info.mData->base())->nFenceFd = -1;

        // 创建时,将Codecs端的数据直接使用Client端的buffer数据,也就是先不考虑需要数据转换器
        info.mCodecData = info.mData;

        // 请求底层组件输出端口使用该Buffer,将会创建OMXNodeInstance支持的Buffer数据类型,然后创建新的Buffer ID并关联它。
        // 备注:由此可以直到,该Buffer ID将ACodec中的Buffer数据和OMX底层的Buffer数据类型进行了映射绑定,
        // 后续可以通过该ID来进行编解码数据传递工作。
        // 见8.3.2.4小节分析
        err = mOMXNode->useBuffer(kPortIndexOutput, OMXBuffer::sPreset, &info.mBufferID);
        // 添加Buffer到输出端口缓冲区队列中
        mBuffers[kPortIndexOutput].push(info);

        ALOGV("[%s] allocated meta buffer with ID %u",
                mComponentName.c_str(), info.mBufferID);
    }
    // 创建完所有需要的Buffer

    // 计算当前需要提交的媒体元数据Buffer的个数
    mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
    return err;
}

8.3.2.1、configureOutputBuffersFromNativeWindow(&bufferCount, &bufferSize, &minUndequeuedBuffers,mFlags & kFlagIsSecure /* preregister */)实现分:
从Surface中获取配置的输出缓冲区buffer个数、(每个)buffer大小、最小未出队列缓冲区buffer个数。
preregister参数通常为非安全编解码器时为false

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::configureOutputBuffersFromNativeWindow(
        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
        OMX_U32 *minUndequeuedBuffers, bool preregister) {

    // 创建OMX该参数结构对象并初始化
    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;

    // 最终将会获取底层组件该输出端口索引对应的端口配置信息
    status_t err = mOMXNode->getParameter(
            OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err == OK) {
    	// 成功时,初始化Surface的窗口大小格式和Bit位使用类型
    	// 见下面分析
        err = setupNativeWindowSizeFormatAndUsage(
                mNativeWindow.get(), &mNativeWindowUsageBits,
                preregister && !mTunneled /* reconnect */);
    }
    if (err != OK) {
    	// 失败
        mNativeWindowUsageBits = 0;
        return err;
    }

    // (重置)设置Surface的(图形buffer生产者)缓冲区buffer出队超时时间为-1
    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.
    // 通常该值为false
    if (mTunneled) {
    	// 隧道模式播放时,跳过native window的缓冲区分配。也就将底层组件配置buffer个数信息和参数返回全为0
        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;
    }

    // 从Surface中查询最小未出队缓冲区个数
    *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;
    }
	
	// 下面的注释也比较重要,此处就不翻译了,自行阅读即可,主要说明一些问题存在
	// 使用保守(合适大小)分配,同时尝试减少饥饿(数据不足)情况发生。
	// 下面也就是处理实际应该(最少)分配的缓冲区个数计算和处理,计算最少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
    // 额外buffer个数的 2 + 1 的说明见最后第2点英文说明
    for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) {
        // 计算得到合适大小缓冲区新的buffer个数:底层组件要求的最小个数 + Surface的最小未出队缓冲区个数 + 额外数据buffer个数
        OMX_U32 newBufferCount =
            def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers;
        // 赋值给OMX组件该缓冲区buffer实际个数字段    
        def.nBufferCountActual = newBufferCount;
        // 然后设置该更新参数结构对象给底层具体组件去更新它
        // 举例SoftAVCDec组件实现结果:
        // 该结构对象其它参数未变化,因此只会更新该实际buffer个数值
        err = mOMXNode->setParameter(
                OMX_IndexParamPortDefinition, &def, sizeof(def));

        if (err == OK) {
        	// 成功时,将Surface的最小未出队缓冲区个数 加上 额外数据buffer个数
            *minUndequeuedBuffers += extraBuffers;
            break;
        }

		// 失败时
        ALOGW("[%s] setting nBufferCountActual to %u failed: %d",
                mComponentName.c_str(), newBufferCount, err);
        /* exit condition */
        if (extraBuffers == 0) {
            return err;
        }
    }

    // Surface设置新的缓冲区实际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;
    }

    // 成功时,返回缓冲区需要的实际个数及其每个buffer大小(单位字节)
    *bufferCount = def.nBufferCountActual;
    *bufferSize =  def.nBufferSize;
    return err;
}

setupNativeWindowSizeFormatAndUsage(mNativeWindow.get(), &mNativeWindowUsageBits,preregister && !mTunneled /* reconnect */)实现分析:
初始化Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)
备注:Gralloc:表示(buffer)图形分配的意思

// [frameworks/av/media/libstagefright/ACodec.cpp]
status_t ACodec::setupNativeWindowSizeFormatAndUsage(
        ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */,
        bool reconnect) {
    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;
    
	// 最终将会获取底层组件该输出端口索引对应的端口配置信息
    status_t err = mOMXNode->getParameter(
            OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err != OK) {
        return err;
    }

    OMX_INDEXTYPE index;
    // 获取支持该扩展属性对应索引
    // 举例SoftAVCDec组件实现结果:将不支持该扩展类型buffer
    err = mOMXNode->getExtensionIndex(
            "OMX.google.android.index.AndroidNativeBufferConsumerUsage",
            &index);

    if (err != OK) {
    	// 允许底层组件失败
        // allow failure
        err = OK;
    } else {
    	// 支持的话,则查询Surface中关于bit位数类型使用情况并获取,然后重新设置给底层组件来适配更新
    	// 备注:通常软编解码器很少支持的
        int usageBits = 0;
        if (nativeWindow->query(
                nativeWindow,
                NATIVE_WINDOW_CONSUMER_USAGE_BITS,
                &usageBits) == OK) {
            OMX_PARAM_U32TYPE params;
            InitOMXParams(&params);
            params.nPortIndex = kPortIndexOutput;
            params.nU32 = (OMX_U32)usageBits;

            err = mOMXNode->setParameter(index, &params, sizeof(params));

            if (err != OK) {
                ALOGE("Fail to set AndroidNativeBufferConsumerUsage: %d", err);
                return err;
            }
        }
    }

    OMX_U32 usage = 0;
    // 从OMX IL接口层组件查询使用标志位
    // 见下面分析
    err = mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage);
    if (err != 0) {
    	// 从OMX IL接口层组件查询使用位标志失败
        ALOGW("querying usage flags from OMX IL component failed: %d", err);
        // XXX: Currently this error is logged, but not fatal.
        usage = 0;
    }
    int omxUsage = usage;
	
	// 判断端口buffer分配是否使用保护
    if (mFlags & kFlagIsGrallocUsageProtected) {
        usage |= GRALLOC_USAGE_PROTECTED;
    }

    // 添加视频buffer分配使用类型:硬件纹理、硬件合成、外部显示
    // kVideoGrallocUsage = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_EXTERNAL_DISP)
    usage |= kVideoGrallocUsage;
    *finalUsage = usage;

    // 设置最后的Surface裁剪尺寸(左上右下四个int参数)结构对象的内存均为0
    memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));
    // 色彩空间模式存储类型
    mLastNativeWindowDataSpace = HAL_DATASPACE_UNKNOWN;

    ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
    // 设置Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)
    // 见下面分析
    return setNativeWindowSizeFormatAndUsage(
            nativeWindow,
            def.format.video.nFrameWidth,
            def.format.video.nFrameHeight,
            def.format.video.eColorFormat,
            mRotationDegrees,
            usage,
            reconnect);
}

mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage)实现分析:
从OMX IL接口层组件查询输出buffer端口的使用标志位

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::getGraphicBufferUsage(
        OMX_U32 portIndex, OMX_U32* usage) {
    Mutex::Autolock autoLock(mLock);
    if (mHandle == NULL) {
        return DEAD_OBJECT;
    }

    OMX_INDEXTYPE index;
    OMX_STRING name = const_cast<OMX_STRING>(
            "OMX.google.android.index.getAndroidNativeBufferUsage");
    // 获取底层组件对该参数属性支持的索引
    // 举例SoftAVCDec组件实现结果:不支持
    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

    if (err != OMX_ErrorNone) {
        CLOG_ERROR(getExtensionIndex, err, "%s", name);
        return StatusFromOMXError(err);
    }
	// 支持的话(通常硬件编解码器支持)

    GetAndroidNativeBufferUsageParams params;
    InitOMXParams(&params);
    params.nPortIndex = portIndex;

    // 获取底层关于该端口索引支持的数据配置信息
    err = OMX_GetParameter(mHandle, index, &params);
    if (err != OMX_ErrorNone) {
        CLOG_ERROR(getParameter, err, "%s(%#x): %s:%u", name, index,
                portString(portIndex), portIndex);
        return StatusFromOMXError(err);
    }

    // 并返回该配置信息中的使用位bit数
    *usage = params.nUsage;

    return OK;
}

setNativeWindowSizeFormatAndUsage(nativeWindow, def.format.video.nFrameWidth, def.format.video.nFrameHeight, def.format.video.eColorFormat, mRotationDegrees, usage, reconnect)实现分析:
设置Surface的窗口大小格式和Bit位使用类型(可以称之为 使用位【数】)

// [frameworks/av/media/libstagefright/SurfaceUtils.cpp]
status_t setNativeWindowSizeFormatAndUsage(
        ANativeWindow *nativeWindow /* nonnull */,
        int width, int height, int format, int rotation, int usage, bool reconnect) {
    status_t err = NO_ERROR;
	
	// 由英文注释reconnect才参数的作用:某些情况下需要重连Surface才能出队所有的输出buffer(已编码或已解码数据),通常为false
    // In some cases we need to reconnect so that we can dequeue all buffers
    if (reconnect) {
    	// 这两个断开和连接Surface处理见早前章节中已有分析,最终也会调用Surface自身的perform方法执行对应功能
    	// 备注:关于Surface相关的内容将会在后续Surface系列章节分析
        err = nativeWindowDisconnect(nativeWindow, "setNativeWindowSizeFormatAndUsage");
        if (err != NO_ERROR) {
            ALOGE("nativeWindowDisconnect failed: %s (%d)", strerror(-err), -err);
            return err;
        }

        err = nativeWindowConnect(nativeWindow, "setNativeWindowSizeFormatAndUsage");
        if (err != NO_ERROR) {
            ALOGE("nativeWindowConnect failed: %s (%d)", strerror(-err), -err);
            return err;
        }
    }

    // Surface设置缓冲区buffer大小(尺寸),最终也会调用Surface自身的perform方法执行对应功能
    err = native_window_set_buffers_dimensions(nativeWindow, width, height);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    // format 该参数为视频色彩像素编码格式比如YUV420、RGB等
    // Surface设置缓冲区buffer格式
    // 最终也会调用Surface自身的perform方法执行对应功能
    err = native_window_set_buffers_format(nativeWindow, format);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    // 此处判断设置的原始视频旋转角度并转换为HAL支持的转换角度类型枚举
    int transform = 0;
    if ((rotation % 90) == 0) {
    	// 取余,判断角度是否为90的倍数,是的话进入
        switch ((rotation / 90) & 3) {
        	// 计算是90的几倍
            case 1:  transform = HAL_TRANSFORM_ROT_90;  break;
            case 2:  transform = HAL_TRANSFORM_ROT_180; break;
            case 3:  transform = HAL_TRANSFORM_ROT_270; break;
            // 默认0
            default: transform = 0;                     break;
        }
    }

    // Surface设置缓冲区buffer转换角度类型
    // 最终也会调用Surface自身的perform方法执行对应功能
    err = native_window_set_buffers_transform(nativeWindow, transform);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    // 查询Surface的数据消费使用位,失败将忽略
    int consumerUsage = 0;
    err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
    if (err != NO_ERROR) {
        ALOGW("failed to get consumer usage bits. ignoring");
        err = NO_ERROR;
    }

    // 注译:确保检查Stagefright或视频解码器是否要求了受保护的缓冲区buffer
    // 通常为非保护buffer
    // Make sure to check whether either Stagefright or the video decoder
    // requested protected buffers.
    if (usage & GRALLOC_USAGE_PROTECTED) {
        // Check if the ANativeWindow sends images directly to SurfaceFlinger.
        // 注译:检查是否Surface直接发送图像给SurfaceFlinger
        int queuesToNativeWindow = 0;
        err = nativeWindow->query(
                nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow);
        if (err != NO_ERROR) {
            ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err);
            return err;
        }

        // 检查ANativeWindow的消费者端是否能够处理受保护的内容
        // Check if the consumer end of the ANativeWindow can handle protected content.
        int isConsumerProtected = 0;
        err = nativeWindow->query(
                nativeWindow, NATIVE_WINDOW_CONSUMER_IS_PROTECTED, &isConsumerProtected);
        if (err != NO_ERROR) {
            ALOGE("error query native window: %s (%d)", strerror(-err), -err);
            return err;
        }

        // 如果两个条件都不满足,则拒绝排队进入Surface,返回无权限
        // Deny queuing into native window if neither condition is satisfied.
        if (queuesToNativeWindow != 1 && isConsumerProtected != 1) {
            ALOGE("native window cannot handle protected buffers: the consumer should either be "
                  "a hardware composer or support hardware protection");
            return PERMISSION_DENIED;
        }
    }

    // buffer分配使用位总和
    int finalUsage = usage | consumerUsage;
    ALOGV("gralloc usage: %#x(producer) + %#x(consumer) = %#x", usage, consumerUsage, finalUsage);
    // Surface设置该使用位总和
    // 最终也会调用Surface自身的perform方法执行对应功能
    err = native_window_set_usage(nativeWindow, finalUsage);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    // Surface设置窗口缩放模式[NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW]
    // 即缓冲区在两个维度(即宽高)上缩放以匹配窗口大小
    err = native_window_set_scaling_mode(
            nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x",
            nativeWindow, width, height, format, rotation, finalUsage);
    return NO_ERROR;
}

8.3.2.2、BufferInfo结构定义:
缓冲区buffer信息结构定义,该结构非常重要,它将是编解码器工作时传递数据的载体。
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
TODO 【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【05】

上一篇:Docker 总览


下一篇:Spiking Neural Networks and online learning: An overview and perspectives