Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 8】
2021/4/23 1:55:28
本文主要是介绍Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 8】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
承接上一章节分析:Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 7】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析
【此章节小节编号就接着上一章节排列】
3.6、omx->allocateNode(componentName.c_str(), observer, &omxNode)实现分析:
分配编解码器组件节点信息对象
// [frameworks/av/media/libmedia/omx/1.0/WOmx.cpp] status_t LWOmx::allocateNode( char const* name, sp<IOMXObserver> const& observer, sp<IOMXNode>* omxNode) { // 方法执行结果状态 status_t fnStatus; // 方法转换状态 // 备注:其实此处的转换实现都是为了匹配HIDL语言实现的HAL服务接口而做一些对象类型转换。 // toStatusT方法,见3.6.1小节分析 // TWOmxObserver类声明和构造函数,见3.6.2小节分析 // mBase->allocateNode()方法,该方法的传入有三个:&fnStatus, omxNode,以及另外一个匿名函数, // 该匿名函数实现方式其实在此前章节已分析过,也就是一个std::function类型的回调方法类型参数 // 见3.6.3小节分析 // LWOmxNode类声明和构造函数,见3.6.4小节分析 status_t transStatus = toStatusT(mBase->allocateNode( name, new TWOmxObserver(observer), [&fnStatus, omxNode](Status status, sp<IOmxNode> const& node) { // 由上一个流程执行完毕后将会回调该方法,缓存再次进行数据类型对象转换。 // 也就是说:在HIDL实现的Binder服务机制中会有一套专门为了实现该Binder服务重新定义的通信交互数据类型。 // 交互时必须来回转换数据类型才能进行Binder通信。 fnStatus = toStatusT(status); // LWOmxNode数据类型转换,也是代理对象实现,见3.6.5小节分析 *omxNode = new LWOmxNode(node); })); return transStatus == NO_ERROR ? fnStatus : transStatus; }
3.6.1、toStatusT() 状态类型对象转换:
其实和前面章节中分析过的toStatus() 方法刚好相反的转换,就是为了HIDL语言的对象类型和native层的对象类型进行转换。
// [frameworks/av/media/libmedia/include/media/omx/1.0/conversion.h] /** * \brief Convert `Status` to `status_t`. This is for legacy binder calls. * * \param[in] t The source `Status`. * \return the corresponding `status_t`. */ // convert: Status -> status_t inline status_t toStatusT(Status const& t) { switch (t) { case Status::NO_ERROR: case Status::NAME_NOT_FOUND: case Status::WOULD_BLOCK: case Status::NO_MEMORY: case Status::ALREADY_EXISTS: case Status::NO_INIT: case Status::BAD_VALUE: case Status::DEAD_OBJECT: case Status::INVALID_OPERATION: case Status::TIMED_OUT: case Status::ERROR_UNSUPPORTED: case Status::UNKNOWN_ERROR: case Status::RELEASE_ALL_BUFFERS: return static_cast<status_t>(t); case Status::BUFFER_NEEDS_REALLOCATION: return NOT_ENOUGH_DATA; default: ALOGW("Unrecognized status value: %" PRId32, static_cast<int32_t>(t)); return static_cast<status_t>(t); } }
3.6.2、new TWOmxObserver(observer)实现分析:
其实看对象名称也知道是啥实现原理了,也就是又是一个HIDL语言类型实现的CodecObserver对象的对象类型转换和功能代理对象。
根据前面对象类型转换英文注释可知,其实还有相反过程的对象类型转换,也就是肯定还存储一个LW开头的转换类型即 LWOmxObserver。
TWOmxObserver类声明
// [frameworks/av/media/libmedia/include/media/omx/1.0/WOmxObserver.h] // 其实可以从这里看到,父类 IOmxObserver 的命名空间声明,集合我们前面分析过的HIDL语言特性, // 可知该头文件肯定也是HIDL语言实现的hal文件在编译时自动生成的头文件。此处不再分析。 // 此处可以看出 该代理类父类和CodecObserver被代理类的父类不是同一个哦,一般情况下我们实现代理类可能都会是同一个父类接口, // 但这里为啥如何呢,其实很简单,就是为了实现HIDL语音的语法呀,否则咋进行HIDL实现的Binder服务调用和转换。其实你也可以看成一样功能的类即可。 using ::android::hardware::media::omx::V1_0::IOmxObserver; struct TWOmxObserver : public IOmxObserver { sp<IOMXObserver> mBase; TWOmxObserver(sp<IOMXObserver> const& base); Return<void> onMessages(const hidl_vec<Message>& tMessages) override; };
TWOmxObserver类构造函数
缓存被代理对象
// [frameworks/av/media/libmedia/omx/1.0/WOmxObserver.cpp] // TWOmxObserver TWOmxObserver::TWOmxObserver(sp<IOMXObserver> const& base) : mBase(base) { }
它的onMessage代理实现:
看下面实现其实很简单,就是完成了代理功能的实现,参数对象类型也必须一起转换,然后再调用被代理类对应代理方法。此处不再详细分析。此实现是完成从native层到HIDL层的对象类型转换。
// [frameworks/av/media/libmedia/omx/1.0/WOmxObserver.cpp] Return<void> TWOmxObserver::onMessages(const hidl_vec<Message>& tMessages) { std::list<omx_message> lMessages; for (size_t i = 0; i < tMessages.size(); ++i) { lMessages.push_back(omx_message{}); convertTo(&lMessages.back(), tMessages[i]); } mBase->onMessages(lMessages); return Return<void>(); }
3.6.3、mBase->allocateNode()方法实现分析:
该方法的传入有三个:&fnStatus, omxNode,以及另外一个匿名函数,该匿名函数实现方式其实在此前章节已分析过,也就是一个std::function类型的回调方法类型参数。
mBase的调用流程为:IOmx --> BpHwOmx --> BsOmx --> Omx,【注意此处的调用类调用流程中间可能还有多个代理类实现,不过对于这些中间代理类并不影响我们的功能分析,因此不再详细分析其调用执行流程,只需要知道也是Binder机制调用原理即可】最终将会调用到。Omx类的实现:
注意此处的name参数就是插件编解码器组件名。
// [frameworks/av/media/libstagefright/omx/1.0/Omx.cpp] Return<void> Omx::allocateNode( const hidl_string& name, const sp<IOmxObserver>& observer, allocateNode_cb _hidl_cb) { // 引入命名空间 using ::android::IOMXNode; using ::android::IOMXObserver; // OMX组件节点信息实例对象【也就是获取的底层具体实现】 sp<OMXNodeInstance> instance; // 加锁代码块进行访问 { Mutex::Autolock autoLock(mLock); // kMaxNodeInstances该常量定义值为:(1 << 16),即2的16次方,表示最大编解码器节点信息对象实例个数 if (mLiveNodes.size() == kMaxNodeInstances) { // 失败时直接回调传入的回调方法该错误【NO_MEMORY】 _hidl_cb(toStatus(NO_MEMORY), nullptr); return Void(); } // 创建一个OMX组件节点信息实例对象 // 3.6.3.1小节分析 instance = new OMXNodeInstance( this, new LWOmxObserver(observer), name.c_str()); // OMX组件类型接口类实例接口访问句柄,关于该类的声明介绍见前面相关章节分析 // 注意:该对象是底层具体组件实现赋值上来的。 OMX_COMPONENTTYPE *handle; // 创建(插件编解码器组)组件实例 // 3.6.3.2小节分析 OMX_ERRORTYPE err = mMaster->makeComponentInstance( name.c_str(), &OMXNodeInstance::kCallbacks, instance.get(), &handle); if (err != OMX_ErrorNone) { LOG(ERROR) << "Failed to allocate omx component " "'" << name.c_str() << "' " " err=" << asString(err) << "(0x" << std::hex << unsigned(err) << ")"; _hidl_cb(toStatus(StatusFromOMXError(err)), nullptr); return Void(); } // 创建具体编解码器处理组件实例信息对象成功时 // 设置该组件类型信息对象指针给OMXNodeInstance // 3.6.3.3小节分析 instance->setHandle(handle); // 查询编解码器xml配置文件中的该组件(<Quirk>标签特殊)配置信息映射列表 // 备注:关于mParser的实现源码分析见前面相关章节 // Find quirks from mParser const auto& codec = mParser.getCodecMap().find(name.c_str()); if (codec == mParser.getCodecMap().cend()) { // 若是尾部数据项,则表示获取失败了 LOG(WARNING) << "Failed to obtain quirks for omx component " "'" << name.c_str() << "' " "from XML files"; } else { // for循环处理 uint32_t quirks = 0; for (const auto& quirk : codec->second.quirkSet) { // 其实根据配置文件中具体信息可知,以下两个Quirk标签在编解码器中都基本存在 // 他们的作用正如名称一样:表示输入输出buffer需要在输入输出端口上分配buffer。 if (quirk == "quirk::requires-allocate-on-input-ports") { quirks |= OMXNodeInstance:: kRequiresAllocateBufferOnInputPorts; } if (quirk == "quirk::requires-allocate-on-output-ports") { quirks |= OMXNodeInstance:: kRequiresAllocateBufferOnOutputPorts; } } // 设置该组件(<Quirk>标签特殊)配置信息 // 3.6.3.4小节分析 instance->setQuirks(quirks); } // 添加正在使用的观察者监听类对象指针和OMX组件节点信息实例对象的映射关系 // 生存的节点信息列表映射对象声明:KeyedVector<wp<IBase>, sp<OMXNodeInstance> > mLiveNodes; mLiveNodes.add(observer.get(), instance); // 添加 OMX组件节点信息实例对象指针 和 观察者监听类对象指针 的映射关系 // 声明:KeyedVector<OMXNodeInstance*, wp<IBase> > mNode2Observer; mNode2Observer.add(instance.get(), observer.get()); } // 当前Omx服务监听observer观察者端服务的Binder死亡通知事件, // 也就是说当observer服务端在死亡时将会通知Omx服务这里该事件并处理它 observer->linkToDeath(this, 0); // 最后执行调用端传入的回调方法,并传入数据类型转换后的数据 // new TWOmxNode(instance) 实现分析,见3.6.3.5小节分析 _hidl_cb(toStatus(OK), new TWOmxNode(instance)); return Void(); }
3.6.3.1、new OMXNodeInstance(this, new LWOmxObserver(observer), name.c_str())实现分析:
创建一个OMX组件节点信息实例对象
先分析LWOmxObserver类声明和构造函数实现
LWOmxObserver实现原理其实和前面分析的TWOmxObserver类似,同样是个代理实现,这两个类同时组成了native层和HIDL实现的HAL层的对象类型转换,其实也就是相当于HIDL实现的Bn端和Bp端进行数据交互时必须使用的对象类型转换技术。后续再碰到这种转换实现,将不会再详细分析了。
注意:这两个转换类型的父类是不同的,那肯定得不同呀,本来就是要做对象类型转换,一样的话还转换啥。
LWOmxObserver类声明
其实这里可以看到,它的父类其实和最初被代理对象CodecObserver的父类其实同一个,其实也就是完成了通过HIDL通道的数据类型转换后的数据原类型还原代理。
// [frameworks/av/media/libstagefright/omx/include/stagefright/omx/1.0/WOmxObserver.h] struct LWOmxObserver : public BnOMXObserver { sp<IOmxObserver> mBase; LWOmxObserver(sp<IOmxObserver> const& base); void onMessages(std::list<omx_message> const& lMessages) override; };
LWOmxObserver类构造函数实现
// [frameworks/av/media/libstagefright/omx/1.0/WOmxObserver.cpp] // LWOmxObserver LWOmxObserver::LWOmxObserver(sp<IOmxObserver> const& base) : mBase(base) { }
它的onMessage代理实现:
看下面实现其实很简单,就是完成了代理功能的实现,参数对象类型也必须一起转换,然后再调用被代理类对应代理方法。此处不再详细分析。关于此次有一个native_handle_t数据类型的参与,相当于一个访问句柄,也不再分析了。
此实现是完成从HIDL层到native层的数据类型转换。
// [frameworks/av/media/libstagefright/omx/1.0/WOmxObserver.cpp] void LWOmxObserver::onMessages(std::list<omx_message> const& lMessages) { hidl_vec<Message> tMessages; std::vector<native_handle_t*> handles(lMessages.size()); tMessages.resize(lMessages.size()); size_t i = 0; for (auto const& message : lMessages) { wrapAs(&tMessages[i], &handles[i], message); ++i; } auto transResult = mBase->onMessages(tMessages); if (!transResult.isOk()) { LOG(ERROR) << "LWOmxObserver::onMessages - Transaction failed"; } for (auto& handle : handles) { native_handle_close(handle); native_handle_delete(handle); } }
再分析OMXNodeInstance类声明:
// [frameworks/av/media/libstagefright/omx/include/stagefright/omx/OMXNodeInstance.h] struct OMXNodeInstance : public BnOMXNode { OMXNodeInstance( Omx *owner, const sp<IOMXObserver> &observer, const char *name); } // [frameworks/av/media/libmedia/include/media/IOMX.h] class BnOMXNode : public BnInterface<IOMXNode> {} // [frameworks/av/media/libmedia/include/media/IOMX.h] class IOMXNode : public IInterface {}
OMXNodeInstance类构造函数实现:
主要就是初始化工作,由于后续流程还有很多需要分析的,因此后续的章节分析中将对不重要的处理只简单提一下其实现原理,而不再一一分析。
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] OMXNodeInstance::OMXNodeInstance( Omx *owner, const sp<IOMXObserver> &observer, const char *name) : mOwner(owner), mHandle(NULL), mObserver(observer), mDying(false), mSailed(false), mQueriedProhibitedExtensions(false), mQuirks(0), mBufferIDCount(0), mRestorePtsFailed(false), mMaxTimestampGapUs(0LL), mPrevOriginalTimeUs(-1LL), mPrevModifiedTimeUs(-1LL) { // 获取用于debug的组件名和通过系统属性值来指定debug级别,此处不再分析 mName = ADebug::GetDebugName(name); DEBUG = ADebug::GetDebugLevelFromProperty(name, "debug.stagefright.omx-debug"); ALOGV("debug level for %s is %d", name, DEBUG); DEBUG_BUMP = DEBUG; mNumPortBuffers[0] = 0; mNumPortBuffers[1] = 0; mDebugLevelBumpPendingBuffers[0] = 0; mDebugLevelBumpPendingBuffers[1] = 0; mMetadataType[0] = kMetadataBufferTypeInvalid; mMetadataType[1] = kMetadataBufferTypeInvalid; mPortMode[0] = IOMX::kPortModePresetByteBuffer; mPortMode[1] = IOMX::kPortModePresetByteBuffer; mSecureBufferType[0] = kSecureBufferTypeUnknown; mSecureBufferType[1] = kSecureBufferTypeUnknown; mGraphicBufferEnabled[0] = false; mGraphicBufferEnabled[1] = false; // 判断是否为安全编解码器 mIsSecure = AString(name).endsWith(".secure"); // 旧版自适应实验功能是否启用 mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled("legacy-adaptive"); if (!strcmp(mName, "qcom.encoder.tme") || !strcmp(mName, "qti.decoder.vc1sw") || !strcmp(mName, "qcom.decoder.vp8") || !strcmp(mName, "qcom.encoder.vp8")) { // 若debug组件名等于上面这四种,那么将设置该特殊(怪癖)属性类型,我们目前不需要分析这个 mQuirks = kRequiresAllocateBufferOnInputPorts | kRequiresAllocateBufferOnOutputPorts; } }
3.6.3.2、mMaster->makeComponentInstance(name.c_str(), &OMXNodeInstance::kCallbacks,instance.get(), &handle)实现分析:
创建(插件编解码器组)组件实例
由前面相关章节分析过,mMaster是构造函数中初始化的,其对于实现类为OMXMaster。
注意:此处有一个非常重要的callback回调监听类的参数即 &OMXNodeInstance::kCallbacks,它将会用于监听编解码器回调事件。
它对应的声明为:
是OMXNodeInstance内部的一个static声明的静态类实现,类型为OMX_CALLBACKTYPE,即前面提到过的OMX回调类型接口。
// [frameworks/av/media/libstagefright/omx/include/stagefright/omx/OMXNodeInstance.h] struct OMXNodeInstance : public BnOMXNode { static OMX_CALLBACKTYPE kCallbacks; } // [frameworks/native/headers/media_plugin/media/openmax/OMX_Core/h] // 再次给出该类声明吧,可能看到它就是一个接口方法指针声明 typedef struct OMX_CALLBACKTYPE { OMX_ERRORTYPE (*EventHandler)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData); OMX_ERRORTYPE (*EmptyBufferDone)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); OMX_ERRORTYPE (*FillBufferDone)( OMX_OUT OMX_HANDLETYPE hComponent, OMX_OUT OMX_PTR pAppData, OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); } OMX_CALLBACKTYPE;
kCallbacks的具体实现为:
注意:该实现非常重要,就是后续编解码器发起获取输入输出数据及其消息事件的功能回调通知等。
此次将初始化完成了接口方法指针的赋值,将会在底层编解码器功能实现时回调执行这些功能。
这三个方法指针对应的方法实现都是OMXNodeInstance自身的实现,后续流程中将会分析到。
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] // static OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = { &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone };
makeComponentInstance方法实现:
注意此处的appData数据类型实际对应上面的 OMXNodeInstance 实例指针,OMX_PTR其实定义是 void * 无类型指针
// [frameworks/av/media/libstagefright/omx/OMXMaster.cpp] OMX_ERRORTYPE OMXMaster::makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { // 这句一定会打印 ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName); // 加锁访问 Mutex::Autolock autoLock(mLock); // 首先清空OMX组件类型信息访问句柄指针的指针 *component = NULL; // 注意:mPluginByComponentName该KV值映射map对象,在前面OMXMaster构造函数实现相关章节中已分析过, // 它其实就是加载的编解码器具体插件实现库的组件名和其(插件编解码器)组件具体实现类实例的映射。 // 实现库有两个:libstagefrighthw.so 和 libstagefright_softomx_plugin.so。请参考前面章节分析 // 因此此处将会获取到该组件名在该列表key值中的索引 ssize_t index = mPluginByComponentName.indexOfKey(String8(name)); if (index < 0) { // 小于0查询失败,表示指定的组件名无效 return OMX_ErrorInvalidComponentName; } // 根据key值获取该具体实际插件实现的插件实现类实例对象 OMXPluginBase *plugin = mPluginByComponentName.valueAt(index); // 最后调用了具体插件的该方法实现。 // 见下面的分析 OMX_ERRORTYPE err = plugin->makeComponentInstance(name, callbacks, appData, component); if (err != OMX_ErrorNone) { return err; } // 最后将该组件类型信息对象和该组件所属插件实现信息对象映射缓存。 // 备注:此处阐述一下这两个的关系 // 该组件所属插件实现信息对象;其是多种编解码器集合的OMX插件实现so库,就是用来加载管理该插件提供的多种编解码器组件类型信息对象。 mPluginByInstance.add(*component, plugin); return err; }
plugin->makeComponentInstance(name, callbacks, appData, component)实现分析:
调用具体编解码器插件库的插件实现类的该方法实现。
根据前面章节分析,此处将只分析安卓原生软编解码器组件插件的实现,暂不分析高通硬实现,后续有时间再考虑吧。
直接定位到【libstagefrighthw.so】库中的具体实现
// [frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp] OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { ALOGV("makeComponentInstance '%s'", name); // 循环匹配指定name组件名 // 关于 当前插件支持的编解码器组件名列表信息 kComponents 及其 kNumComponents 的分析见前面流程中的相关分析 for (size_t i = 0; i < kNumComponents; ++i) { // 匹配组件名是否相同,strcmp返回0时表示相同,其他均不同 if (strcmp(name, kComponents[i].mName)) { continue; } // 在前面流程相关章节中分析过,该so库名称前缀的都是安卓原生实现的软编解码器实际对应的每种组件具体实现so库。 // 每个组件都是通过单独的so库来实现加载。可参考前面相关章节中的分析。 // 下面的分析我们将举例分析一种解码器组件实现库:即h264编码格式的解码器,位于真实pad中vendor/lib/libstagefright_soft_avcdec.so AString libName = "libstagefright_soft_"; // mLibNameSuffix该值对应于每个lib库名的后缀,比如 "avcdec" libName.append(kComponents[i].mLibNameSuffix); libName.append(".so"); // RTLD_NODELETE means we keep the shared library around forever. // this eliminates thrashing during sequences like loading soundpools. // It also leaves the rest of the logic around the dlopen()/dlclose() // calls in this file unchanged. // // Implications of the change: // -- the codec process (where this happens) will have a slightly larger // long-term memory footprint as it accumulates the loaded shared libraries. // This is expected to be a small amount of memory. // -- plugin codecs can no longer (and never should have) depend on a // free reset of any static data as the library would have crossed // a dlclose/dlopen cycle. // // 打开so库返回库访问句柄指针 void *libHandle = dlopen(libName.c_str(), RTLD_NOW|RTLD_NODELETE); if (libHandle == NULL) { ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror()); return OMX_ErrorComponentNotFound; } // 声明方法指针,指针名为CreateSoftOMXComponentFunc typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( const char *, const OMX_CALLBACKTYPE *, OMX_PTR, OMX_COMPONENTTYPE **); // 加载获取so库中名称为 【_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE】 的 // createSoftOMXComponent方法地址,赋值给方法指针。 // 注意:此处加载的方式是C++方法名编译方式编译到so库的,因此此次直接写入了编译之后的结果标识符来加载的。 // 见下面的分析【目前只举例分析】 CreateSoftOMXComponentFunc createSoftOMXComponent = (CreateSoftOMXComponentFunc)dlsym( libHandle, "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" "PvPP17OMX_COMPONENTTYPE"); if (createSoftOMXComponent == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorComponentNotFound; } // 执行该方法也就是执行创建软编解码器组件实现方法来创建该组件,返回该组件具体实现,如 SoftAVC 对象 // 举例AVC解码器实现,简单描述这一步中的工作执行原理: // 1、SoftAVC创建时构造函数执行,缓存上层传入参数值, // 2、创建一个线程名称为该组件名的ALooper消息循环线程来执行编解码任务, // 3、创建 OMX_COMPONENTTYPE 即OMX组件类型对象, // 并初始化它对象的指针,根据前面相关章节分析过它的类声明,其实就是方法指针赋值和部分变量的赋值, // 最后将该创建的对象指针赋值给传入参数【component】(其为双重指针), // 如此上层将可以通过该指针来访问执行底层具体组件的相关方法实现了。 // 4、底层也可以使用上层传入的【callbacks】回调对象指针来访问上层实现的对应方法。 // 5、初始化一个输入输出端口缓冲buffer区,默认它们的最小buffer数均为1个,默认均为2个,每个输入端口缓冲buffer大小默认为【1024 * 1024】即 1M 大小。 // 6、提供了可开启解码器输入数据DUMP记录在文件中的功能。【通过宏定义开关来实现】 sp<SoftOMXComponent> codec = (*createSoftOMXComponent)(name, callbacks, appData, component); if (codec == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorInsufficientResources; } // 执行该组件实现类 initCheck() 方法 // 举例AVC解码器组件实现中这一步工作原理:其实际该方法为父类空实现,直接返回【OMX_ErrorNone】。 OMX_ERRORTYPE err = codec->initCheck(); if (err != OMX_ErrorNone) { dlclose(libHandle); libHandle = NULL; return err; } // 增加(递增1)该智能指针的引用计数加1,主要为了当该方法返回时,当前sp智能指针类会执行析构函数, // 会执行内部实际对象指针的对象内存被释放,因为若不加1,此时析构函数执行时将判断出引用计数为1,那么必须要释放它指向的内存。 // 备注:其实该功能的作用此前很多章节中都已阐述过了,后面再碰到就不会再阐述。 codec->incStrong(this); // 设置so库访问句柄 // 举例AVC解码器组件实现中这一步工作原理: // 只是缓存该指针而已 codec->setLibHandle(libHandle); // 返回无错码 return OMX_ErrorNone; } return OMX_ErrorInvalidComponentName; }
dlsym(libHandle, “_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE”)实现举例分析:
加载获取so库中名称为 【_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE】 的createSoftOMXComponent方法地址,赋值给方法指针。
备注:这些所有的安卓原生so库实现都是在【frameworks/av/media/libstagefright/codecs/】目录下。
额外提醒:
另外必须注意:安卓原生中还有一个软编解码器实现目录:
【frameworks/av/media/codec2/components/】该目录下也有类似于上面目录下的组件插件实现,但是不同点是:
1、components目录下的组件比codecs目录下的要少,但是components下面可能提供了codecs下没有的组件实现,比如components目录下有hevc的编码器,而codecs中没有该组件。
2、codecs下面为安卓原生实现,而components都是引入【external/】目录下的对应的三方开源库的实现。
备注:components目录下的所有实现的so库在编译期间加载途径为,都在【frameworks/av/services/mediacodec/registrant/Android.bp】编译文件中引入的,而该编译文件会在【frameworks/av/services/mediacodec/Android.bp】这个目录下引入,而这一个编译文件最终会在系统产品编译文件中加入【device/<厂商名>/<产品名>/base.mk】,厂商名如高通 qcom。
举例分析一种解码器组件实现库:即h264编码格式的解码器,位于真实pad中vendor/lib/libstagefright_soft_avcdec.so
该so库编译文件为
frameworks/av/media/libstagefright/codecs/avcdec/Android.bp
so库中 createSoftOMXComponent 方法实现:
// [frameworks/av/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp] android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { return new android::SoftAVC(name, callbacks, appData, component); }
SoftAVC类功能实现分析:
该类的实现目前暂不详细分析,会在后续有时间时分析给出来,因此后面涉及到该实现的功能时会直接使用文字来描述它实现的工作原理即可。
TODO:关于安卓原生H264、H265的编解码器组件编解码功能具体实现会在后续有时间时再详细分析后给出来吧。
3.6.3.3、instance->setHandle(handle)实现分析:
设置该组件类型信息对象指针给OMXNodeInstance
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] void OMXNodeInstance::setHandle(OMX_HANDLETYPE handle) { CLOG_LIFE(allocateNode, "handle=%p", handle); CHECK(mHandle == NULL); // 缓存底层组件信息访问对象 mHandle = handle; if (handle != NULL) { // 不为空时将创建Callback回调监听分发器类 // 见下面的分析 mDispatcher = new CallbackDispatcher(this); } }
CallbackDispatcher类声明:【省略其他】
是OMXNodeInstance类的内部类
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] struct OMXNodeInstance::CallbackDispatcher : public RefBase { private: sp<OMXNodeInstance> const mOwner; }
CallbackDispatcher类构造函数实现:
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] OMXNodeInstance::CallbackDispatcher::CallbackDispatcher(const sp<OMXNodeInstance> &owner) : mOwner(owner), mDone(false) { // 创建callback回调分发器线程来完成回调分发任务【关于线程实现源码分析,见文章头推荐】 // 见下面的分析 mThread = new CallbackDispatcherThread(this); // 启动线程并设置线程的优先级较高 mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND); }
CallbackDispatcherThread类声明和构造函数实现:
是OMXNodeInstance类的内部类
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] // This provides the underlying Thread used by CallbackDispatcher. // Note that deriving CallbackDispatcher from Thread does not work. struct OMXNodeInstance::CallbackDispatcherThread : public Thread { // 构造函数默认实现 explicit CallbackDispatcherThread(CallbackDispatcher *dispatcher) : mDispatcher(dispatcher) { } private: CallbackDispatcher *mDispatcher; bool threadLoop(); CallbackDispatcherThread(const CallbackDispatcherThread &); CallbackDispatcherThread &operator=(const CallbackDispatcherThread &); };
mThread->run() 启动线程之后的流程分析:
有线程实现源码分析章节可知,它将会执行线程类的线程循环方法,如下
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] bool OMXNodeInstance::CallbackDispatcherThread::threadLoop() { return mDispatcher->loop(); }
mDispatcher->loop()实现分析:
此处非常重要,此处就将完成向上层ACodec回调OMX插件中的事件消息回调功能,它使用了线程循环功能来采用【生产者-消费者】设计模式来完成的。
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] bool OMXNodeInstance::CallbackDispatcher::loop() { // 消息线程循环处理回调分发器中的消息队列 for (;;) { // 消息队列来自于OMX组件实现 std::list<omx_message> messages; // 加锁代码块来访问消息队列 { Mutex::Autolock autoLock(mLock); // 若回调消息分发器未结束(即执行析构函数),并且消息队列为空, // 那么当前消息线程将会在【mQueueChanged】条件变量上等待(被消息加入时唤醒)。 // 备注:第一次开始执行时肯定无消息会wait。 while (!mDone && mQueue.empty()) { mQueueChanged.wait(mLock); } // 若消费分发器已停止工作那么结束线程循环 if (mDone) { break; } // 交换消息队列,其实就是将空队列messages和有消息的队列mQueue进行所有元素数据交互。 messages.swap(mQueue); } // 分发消息队列 // TODO 关于此次的分析目前先不忙分析,因为该实现流程很长, // 并且初始化时也没有消息,因此在后续流程添加消息时会继续分析的。 dispatch(messages); } // 根据线程实现原理章节,可知,若线程循环方法返回了false,那么将代表着,工作线程将会结束。 return false; }
3.6.3.4、instance->setQuirks(quirks)实现分析:
设置该组件(标签特殊)配置信息类型
// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp] status_t OMXNodeInstance::setQuirks(OMX_U32 quirks) { // 该处理实际上是在检测该传入参数的有效性,即该参数表示的bit位标识符是否在允许的已定义范围内。 // ~取反运算符表示将作用变量的所有bit位取反,也就是将1和0互换,然后和参数进行&与运算, // 那么若得到的值大于0,则表示参数中包含了未定义的类型值即无效参数。 关于位运算不了解的请自行先了解。 // kQuirksMask见下面的实现 if (quirks & ~kQuirksMask) { return BAD_VALUE; } if (!strcmp(mName, "qcom.encoder.tme")) { // 相等时直接开启这两种buffer分配请求类型 // 注意:这个mName不是最初的组件名,它是构造函数中重新处理过的debug名 quirks = kRequiresAllocateBufferOnInputPorts | kRequiresAllocateBufferOnOutputPorts; } // 缓存 mQuirks = quirks; return OK; }
kQuirksMask实现:位表示法
// [frameworks/av/media/libstagefright/omx/include/stagefright/omx/OMXNodeInstance.h] // Quirk still supported, even though deprecated enum Quirks { kRequiresAllocateBufferOnInputPorts = 1, kRequiresAllocateBufferOnOutputPorts = 2, // 可以看出,该类型表示了上面两种类型 kQuirksMask = kRequiresAllocateBufferOnInputPorts | kRequiresAllocateBufferOnOutputPorts, };
3.6.3.5、new TWOmxNode(instance) 实现分析:
其实根据前面已有的分析就能知道该类实现原理,其实也是个类型转换代理类实现。也就是说将它转换为HIDL实现的Binder机制支持的数据类型来进行Binder交互,否则将不能交互。因此这里的父类其实也是HIDL语言实现的编译时自动生成的头文件,请查看前面关于这一块的阐述。
TWOmxNode类声明
// [frameworks/av/media/libstagefright/omx/include/stagefright/omx/1.0/WOmxNode.h] struct TWOmxNode : public IOmxNode { sp<IOMXNode> mBase; TWOmxNode(sp<IOMXNode> const& base); }
TWOmxNode构造函数实现
缓存代理类
// [frameworks/av/media/libstagefright/omx/1.0/WOmxNode.cpp] // TWOmxNode TWOmxNode::TWOmxNode(sp<IOMXNode> const& base) : mBase(base) { }
3.6.5、new LWOmxNode(node)实现分析:
LWOmxNode数据类型转换,也是代理对象实现。此处有必要分析下,因为这样子才完成了从native层到HAL层,再次HAL层到native层的完整数据转换流程。
LWOmxNode类声明和构造函数实现:
H2BConverter肯定是个模板实现类
// [frameworks/av/media/libmedia/include/media/omx/1.0/WOmxNode.h] /** * Wrapper classes for conversion * ============================== * * Naming convention: * - LW = Legacy Wrapper --- It wraps a Treble object inside a legacy object. * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object. */ struct LWOmxNode : public H2BConverter<IOmxNode, BnOMXNode> { // 默认构造函数实现 // 此次的CBase如下分析可以知道就是调用父类构造函数 LWOmxNode(sp<IOmxNode> const& base) : CBase(base) {} }
其实此时我们若只是简单搜索头文件并没有发现父类H2BConverter声明定义的头文件,其实通过全局代码搜索可以知道其实际上是个为了实现HIDL语言Binder服务功能的数据类型转换模板实现类,如下:
关于HIDL具体实现是比较复杂的,所以我们这里只目前需要简单看一下关于它的声明。
它的实现原理就如它的类名一样,就是将HAL层接口类转换为Binder层接口类型。即对于模板的两个类型类。
// [system/libhidl/transport/token/1.0/utils/include/hidl/HybridInterface.h] template <typename HINTERFACE, typename BNINTERFACE> class H2BConverter : public BNINTERFACE { public: // 类型别名定义,此处比较重要 typedef H2BConverter<HINTERFACE, BNINTERFACE> CBase; // Converter Base typedef typename BNINTERFACE::BaseInterface BaseInterface; typedef HINTERFACE HalInterface; typedef typename BaseInterface::HalVariant HalVariant; using BaseInterface::sGetHalTokenTransactionCode; H2BConverter(const sp<HalInterface>& base) : mBase{base} {} virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual status_t linkToDeath( const sp<IBinder::DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0); virtual status_t unlinkToDeath( const wp<IBinder::DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0, wp<IBinder::DeathRecipient>* outRecipient = nullptr); virtual HalVariant getHalVariant() const override { return { mBase }; } HalInterface* getBase() { return mBase.get(); } protected: // 缓存HAL层类型的接口类 sp<HalInterface> mBase; }
本章节内容分析结束
这篇关于Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】【Part 8】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-07-03微信支付提示下单账户与支付账户不一致-icode9专业技术文章分享
- 2024-07-03微信支付提示订单号重复-icode9专业技术文章分享
- 2024-07-02微服务启动nacos注册上去了,但是一直没有收到请求-icode9专业技术文章分享
- 2024-07-02如何检查文件的编码格式-icode9专业技术文章分享
- 2024-07-02sublime 更改编码格式-icode9专业技术文章分享
- 2024-06-30uniAPP 实现全屏左右滚动滚动的效果-icode9专业技术文章分享
- 2024-06-30如何在本地使用授权或插件-icode9专业技术文章分享
- 2024-06-30伪静态规则配置方法汇总-icode9专业技术文章分享
- 2024-06-29易优CMS安装常见问题汇总-icode9专业技术文章分享
- 2024-06-28易优新手必读安装教程-icode9专业技术文章分享