【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【04】
2021/5/14 20:55:30
本文主要是介绍【六】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(¶ms); params.nPortIndex = kPortIndexOutput; params.nU32 = (OMX_U32)usageBits; err = mOMXNode->setParameter(index, ¶ms, 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(¶ms); params.nPortIndex = portIndex; // 获取底层关于该端口索引支持的数据配置信息 err = OMX_GetParameter(mHandle, index, ¶ms); 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】
这篇关于【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【04】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-18android.permission.read_media_video
- 2024-01-18android_getaddrinfo failed eai_nodata
- 2024-01-18androidmo
- 2024-01-15Android下三种离屏渲染技术
- 2024-01-09Android 蓝牙使用
- 2024-01-06Android对接华为AI - 文本识别
- 2023-11-15代码安全之代码混淆及加固(Android)
- 2023-11-10简述Android语音播报TTS
- 2023-11-06Android WiFi工具类
- 2023-07-22Android开发未来的出路