美文网首页
OMX Service Codec加载

OMX Service Codec加载

作者: Nothing_655f | 来源:发表于2022-02-23 12:20 被阅读0次

    OMX Service Codec加载

    看一个总体的框架图

    image-20210906100244903.png

    图来自 https://blog.csdn.net/VNanyesheshou/article/details/115027180

    我这看的平台是Android 9

    av/services/mediacodec/main_codecservice.cpp

        // Registration of customized codec services
        void *registrantLib = dlopen(
                "libmedia_codecserviceregistrant.so",
                RTLD_NOW | RTLD_LOCAL);
        if (registrantLib) {
            RegisterCodecServicesFunc registerCodecServices =
                    reinterpret_cast<RegisterCodecServicesFunc>(
                    dlsym(registrantLib, "RegisterCodecServices"));
            if (registerCodecServices) {
                registerCodecServices();
            } else {
                LOG(WARNING) << "Cannot register additional services "
                        "-- corrupted library.";
            }
        } else {
            // Default codec services
            using namespace ::android::hardware::media::omx::V1_0;
            sp<IOmxStore> omxStore = new implementation::OmxStore();
            if (omxStore == nullptr) {
                LOG(ERROR) << "Cannot create IOmxStore HAL service.";
            } else if (omxStore->registerAsService() != OK) {
                LOG(ERROR) << "Cannot register IOmxStore HAL service.";
            }
            sp<IOmx> omx = new implementation::Omx();
            if (omx == nullptr) {
                LOG(ERROR) << "Cannot create IOmx HAL service.";
            } else if (omx->registerAsService() != OK) {
                LOG(ERROR) << "Cannot register IOmx HAL service.";
            } else {
                LOG(INFO) << "IOmx HAL service created.";
            }
        }
    

    我们这里主要看默认的case, 创建OmxStore, Omx, 分别注册服务

    OmxStore 初始化解析

    sp<IOmxStore> omxStore = new implementation::OmxStore();
    

    上述omxStore调用了默认的构造函数,看到 cpp中的实现都是带参数的

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

    OmxStore::OmxStore(
            const char* owner,
            const char* const* searchDirs,
            const char* mainXmlName,
            const char* performanceXmlName,
            const char* profilingResultsXmlPath) {
        MediaCodecsXmlParser parser(searchDirs,
                mainXmlName,
                performanceXmlName,
                profilingResultsXmlPath);
        mParsingStatus = toStatus(parser.getParsingStatus());
    
        const auto& serviceAttributeMap = parser.getServiceAttributeMap();
        mServiceAttributeList.resize(serviceAttributeMap.size());
        size_t i = 0;
        for (const auto& attributePair : serviceAttributeMap) {
            ServiceAttribute attribute;
            attribute.key = attributePair.first;
            attribute.value = attributePair.second;
            mServiceAttributeList[i] = std::move(attribute);
            ++i;
        }
    
        const auto& roleMap = parser.getRoleMap();
        mRoleList.resize(roleMap.size());
        i = 0;
        for (const auto& rolePair : roleMap) {
            RoleInfo role;
            role.role = rolePair.first;
            role.type = rolePair.second.type;
            role.isEncoder = rolePair.second.isEncoder;
            // TODO: Currently, preferPlatformNodes information is not available in
            // the xml file. Once we have a way to provide this information, it
            // should be parsed properly.
            role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
            hidl_vec<NodeInfo>& nodeList = role.nodes;
            nodeList.resize(rolePair.second.nodeList.size());
            size_t j = 0;
            for (const auto& nodePair : rolePair.second.nodeList) {
                NodeInfo node;
                node.name = nodePair.second.name;
                node.owner = owner;
                hidl_vec<NodeAttribute>& attributeList = node.attributes;
                attributeList.resize(nodePair.second.attributeList.size());
                size_t k = 0;
                for (const auto& attributePair : nodePair.second.attributeList) {
                    NodeAttribute attribute;
                    attribute.key = attributePair.first;
                    attribute.value = attributePair.second;
                    attributeList[k] = std::move(attribute);
                    ++k;
                }
                nodeList[j] = std::move(node);
                ++j;
            }
            mRoleList[i] = std::move(role);
            ++i;
        }
    
        mPrefix = parser.getCommonPrefix();
    }
    

    于是去.h 中看看默认参数是什么,这里指定了默认参数在 MediaCodecsXmlParser 中定义

    struct OmxStore : public IOmxStore {
        OmxStore(
                const char* owner = "default",
                const char* const* searchDirs
                    = MediaCodecsXmlParser::defaultSearchDirs,
                const char* mainXmlName
                    = MediaCodecsXmlParser::defaultMainXmlName,
                const char* performanceXmlName
                    = MediaCodecsXmlParser::defaultPerformanceXmlName,
                const char* profilingResultsXmlPath
                    = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
        // ...            
        }
    

    在 MediaCodecsXmlParser.h 路劲中可以看到默认路径定义如下

        // Treblized media codec list will be located in /odm/etc or /vendor/etc.
        static constexpr char const* defaultSearchDirs[] =
                {"/odm/etc", "/vendor/etc", "/etc", nullptr};
        static constexpr char const* defaultMainXmlName =
                "media_codecs.xml";
        static constexpr char const* defaultPerformanceXmlName =
                "media_codecs_performance.xml";
        static constexpr char const* defaultProfilingResultsXmlPath =
                "/data/misc/media/media_codecs_profiling_results.xml";
    

    具体再去跟踪 cpp中的构造函数,可以看到 OMXStore初始化通过MediaCodecsXmlParser加载media_codecs.xml、media_codecs_performance.xml,并将信息保存在mServiceAttributeList、mRoleList中

    OMX 初始化解析

    初始化完OmxStore后,接下来就初始化 OMX的主要服务了

    Omx::Omx() :
        mMaster(new OMXMaster()),
        mParser() {
    }
    

    OMXMaster 加载和管理 plugin,包括硬编码和软编码,这里主要是 addVendorPlugin 和 addPlugin(new SoftOMXPlugin); 这两个函数的实现

    OMXMaster::OMXMaster()
        : mVendorLibHandle(NULL) {
    
        pid_t pid = getpid();
        char filename[20];
        snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
        int fd = open(filename, O_RDONLY);
        if (fd < 0) {
          ALOGW("couldn't determine process name");
          strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));
        } else {
          ssize_t len = read(fd, mProcessName, sizeof(mProcessName));
          if (len < 2) {
            ALOGW("couldn't determine process name");
            strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));
          } else {
            // the name is newline terminated, so erase the newline
            mProcessName[len - 1] = 0;
          }
          close(fd);
        }
    
        addVendorPlugin();
        addPlugin(new SoftOMXPlugin);
    }
    

    Vendor Plugin

    addVendorPlugin加载共享库libstagefrighthw.so,查找createOMXPlugin函数,并调用createOMXPlugin 获得OMXPluginBase类型对象,然后调用 addPlugin((*createOMXPlugin)());

    以Mstar/Mtk 为例,libstagefrighthw 的实现是在 MS_OMX_Plugin.cpp这个文件中

    Android.mk

    LOCAL_MODULE := libstagefrighthw
    
    LOCAL_SRC_FILES := MS_OMX_Plugin.cpp
    

    MS_OMX_Plugin.cpp

    OMXPluginBase *createOMXPlugin() {
        return new MSOMXPlugin;
    }
    

    OMXPluginBase 是有Android 定义的,用来对接hal vendor接口的实现, 其主要包含四个虚拟方法, 硬件厂商接入自己的编解码器,需要继承OMXPluginBase 类,并实现抽象方法。

    frameworks/native/headers/media_plugin/media/hardware/OMXPluginBase.h

    struct OMXPluginBase {
        OMXPluginBase() {}
        virtual ~OMXPluginBase() {}
    
        virtual OMX_ERRORTYPE makeComponentInstance(
                const char *name,
                const OMX_CALLBACKTYPE *callbacks,
                OMX_PTR appData,
                OMX_COMPONENTTYPE **component) = 0;
    
        virtual OMX_ERRORTYPE destroyComponentInstance(
                OMX_COMPONENTTYPE *component) = 0;
    
        virtual OMX_ERRORTYPE enumerateComponents(
                OMX_STRING name,
                size_t size,
                OMX_U32 index) = 0;
    
        virtual OMX_ERRORTYPE getRolesOfComponent(
                const char *name,
                Vector<String8> *roles) = 0;
    
    private:
        OMXPluginBase(const OMXPluginBase &);
        OMXPluginBase &operator=(const OMXPluginBase &);
    };
    

    更多vendor的细节可以去 MS_OMX_Plugin.cpp 里面寻找

    Software Plugin

    Software Plugin 定义是在 SoftOMXPlugin.cpp,其接口和定义同样遵循 OMXPluginBase 结构的要求,同样需要实现上述的OMXPluginBase.h 抽象方法

    struct SoftOMXPlugin : public OMXPluginBase {
    }
    

    而 Software Plugin 定义的组件支持在这里

    static const struct {
        const char *mName;
        const char *mLibNameSuffix;
        const char *mRole;
    
    } kComponents[] = {
        // two choices for aac decoding.
        // configurable in media/libstagefright/data/media_codecs_google_audio.xml
        // default implementation
        { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
        // alternate implementation
        { "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" },
        { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
        { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
        { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
        { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
        { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
        { "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },
        { "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },
        { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
        { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
        { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
        { "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },
        { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
        { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
        { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
        { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
        { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
        { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
        { "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },
        { "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },
        { "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },
        { "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },
        { "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },
        { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
        { "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },
        { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
        { "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
    };
    

    这里的例子是加载了两个插件,当然也可以根据需要再添加更多的插件进来,而每个插件中还有组件的定义,比如说 SoftOMXPlugin 这个插件里面 有支持OMX.google.h264.decoder 的组件和 OMX.google.hevc.decoder,在这里面也可以添加想要支持的组件

    回到 *addPlugin(OMXPluginBase plugin) 方法,可以先看下 OMXMaster.h中定义的数据结构

        List<OMXPluginBase *> mPlugins;
        KeyedVector<String8, OMXPluginBase *> mPluginByComponentName;
        KeyedVector<OMX_COMPONENTTYPE *, OMXPluginBase *> mPluginByInstance;
    

    结合如下函数方法可以看到 mPlugins 维护了目前系统之有多少组插件(Vendor, Software ....), 而 mPluginByComponentName 维护了每个插件中支持的组件能力

    void OMXMaster::addPlugin(OMXPluginBase *plugin) {
        Mutex::Autolock autoLock(mLock);
    
        mPlugins.push_back(plugin);
    
        OMX_U32 index = 0;
    
        char name[128];
        OMX_ERRORTYPE err;
        while ((err = plugin->enumerateComponents(
                        name, sizeof(name), index++)) == OMX_ErrorNone) {
            String8 name8(name);
    
            if (mPluginByComponentName.indexOfKey(name8) >= 0) {
                ALOGE("A component of name '%s' already exists, ignoring this one.",
                     name8.string());
    
                continue;
            }
    
            mPluginByComponentName.add(name8, plugin);
        }
    
        if (err != OMX_ErrorNoMore) {
            ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu "
                 "components", err, mPluginByComponentName.size());
        }
    }
    

    而 mPluginByInstance 这个数据结构则要看如下 makeComponentInstance 这个函数的实现,这个函数是实例化组件中的插件,比如说要初始化 OMX.google.h264.decoder这个组件,就会调用 makeComponentInstance(),而如果实例化成功了,即 err = OMX_ErrorNone 就会添加进来到 mPluginByInstance。

    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);
    
        *component = NULL;
    
        ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
    
        if (index < 0) {
            return OMX_ErrorInvalidComponentName;
        }
    
        OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
        OMX_ERRORTYPE err =
            plugin->makeComponentInstance(name, callbacks, appData, component);
    
        if (err != OMX_ErrorNone) {
            return err;
        }
    
        mPluginByInstance.add(*component, plugin);
    
        return err;
    }
    

    那么什么时候会出现 makeComponentInstance 失败了呢?一个常见的例子是hw codec 如果没有hashkey lisence 就会初始初始化,可以看到目前平台简单的流程如下

    makeComponentInstance --> mGetHandle --> MS_OMX_GetHandle --> OMX_GetHandle -- > check from MS_OMX_ScanSupportedCodec

    01-01 00:00:01.652  3321  3499 I OMXClient: IOmx service obtained
    01-01 00:00:01.653  3336  3921 I OMXMaster: makeComponentInstance(OMX.MS.WMA.Decoder) in omx@1.0-service process
    01-01 00:00:01.653  3336  3921 E media.codec: Failed to allocate omx component 'OMX.MS.WMA.Decoder'  err=InvalidComponentName(0x80001002)
    01-01 00:00:01.653  3321  3499 W OmxInfoBuilder: Fail to add mime audio/x-ms-wma to codec OMX.MS.WMA.Decoder after software codecs
    

    那么什么时候会调取用到 makeComponentInstance ?可以接着往下看

    MediaCodecList初始化

    前面介绍了一些 OMX的初始化,包括解析media_codec.xml 数据,那么什么时候会使用到解析的数据并如何更新到系统中

    MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {
            mInitCheck = builder->buildMediaCodecList(&writer);
        }
    

    frameworks/av/media/libstagefright/OmxInfoBuilder.cpp 先去获取前面OmxStore 解析的media_codec.xml中支持的codec 列表,即 roles 和 serviceAttributes 数据

    status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
        // List service attributes (global settings)
        Status status;
        hidl_vec<IOmxStore::RoleInfo> roles;
        auto transStatus = omxStore->listRoles(
                [&roles] (
                const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
                    roles = inRoleList;
                });
    
        hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
        transStatus = omxStore->listServiceAttributes(
                [&status, &serviceAttributes] (
                Status inStatus,
                const hidl_vec<IOmxStore::ServiceAttribute>& inAttributes) {
                    status = inStatus;
                    serviceAttributes = inAttributes;
                });
         // 接下来遍历 xml 中的节点数据 
         for (const IOmxStore::RoleInfo& role : roles) {
            const hidl_string& typeName = role.type;
            bool isEncoder = role.isEncoder;    // xml中有有声明encoder和decoder 的支持组件
            bool preferPlatformNodes = role.preferPlatformNodes; 
            /* 这个属性注释如下,如果优先平台的组件需要添加到软解组件下面,下面的判断用回到 preferPlatformNodes */
            // If preferPlatformNodes is true, hardware nodes must be added after
            // platform (software) nodes. hwCodecs is used to hold hardware nodes
            // that need to be added after software nodes for the same role.
            std::vector<const IOmxStore::NodeInfo*> hwCodecs;
            for (const IOmxStore::NodeInfo& node : role.nodes) {
                const hidl_string& nodeName = node.name;
                bool isSoftware = hasPrefix(nodeName, "OMX.google");
                MediaCodecInfoWriter* info;
                // 对软解组件和硬解组件分别归类到 swCodecName2Info、hwCodecName2Info
    
            }
            // If preferPlatformNodes is true, hardware nodes will not have been
            // added in the loop above, but rather saved in hwCodecs. They are
            // going to be added here.
            if (preferPlatformNodes) {
                for (const IOmxStore::NodeInfo *node : hwCodecs) {
                    MediaCodecInfoWriter* info;
                    const hidl_string& nodeName = node->name;
                    auto c2i = hwCodecName2Info.find(nodeName);
    
                    std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
                            info->addMime(typeName.c_str());
                    if (queryCapabilities(
                            *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
                        ALOGW("Fail to add mime %s to codec %s "
                              "after software codecs",
                              typeName.c_str(), nodeName.c_str());
                        info->removeMime(typeName.c_str());
                    }
                }
            }
         }
    }
    

    上面的 buildMediaCodecList 这个函数挺长的,过了一遍下来后其主要是获取media_codec.xml 中解析的节点组件数据,然后还有一个重要的作用就是将硬件的组件的支持能力更新到MediaCodecInfo::mCaps 中

    简单介绍下这个数据接口的作用 ,方便理解里面的逻辑

    MediaCodecInfo:: KeyedVector<AString, sp<Capabilities> > mCaps; 
    

    这是一个Vector 数组,不过带有支持的组件名,举个例子

    mCaps[2] = {
        <"OMX.MS.AC3.Decoder", Capabilities(audio/ac3)>,
        <"OMX.MS.AC3.Decoder", Capabilities(audio/ac3p)>
    }
    

    通过 info->addMime 添加 audio/ac3p 到 OMX.MS.AC3.Decoder组件

    然后 queryCapabilities 调用 makeComponentInstance等流程检查平台是否支持

    如果不支持则 info->removeMime 移除OMX.MS.AC3.Decoder 对 audio/ac3p的支持

    而这个mCaps 的MediaCodecInfo会进一步提供给 MediaCodecList::findCodecByType 和 MediaCodecList::findMatchingCodecs 使用,然后进一步封装后给player在初始化decoder的时候用于查找目前系统支持的codec 组件

    看MediaCodecList 这块过程中打开了一些打印调试和理清

    diff --git a/frameworks/av/media/libmedia/MediaCodecInfo.cpp b/frameworks/av/media/libmedia/MediaCodecInfo.cpp
    index 26588c0..c74c39d 100644
    --- a/frameworks/av/media/libmedia/MediaCodecInfo.cpp
    +++ b/frameworks/av/media/libmedia/MediaCodecInfo.cpp
    @@ -14,7 +14,7 @@
      * limitations under the License.
      */
    
    -//#define LOG_NDEBUG 0
    +#define LOG_NDEBUG 0
     #define LOG_TAG "MediaCodecInfo"
     #include <utils/Log.h>
    
    @@ -192,7 +192,9 @@ void MediaCodecInfo::getSupportedMimes(Vector<AString> *mimes) const {
    
     const sp<MediaCodecInfo::Capabilities>
     MediaCodecInfo::getCapabilitiesFor(const char *mime) const {
    +    ALOGD("%s Enter", __FUNCTION__);
         ssize_t ix = getCapabilityIndex(mime);
    +    ALOGD("%s ix:%d", __FUNCTION__, ix);
         if (ix >= 0) {
             return mCaps.valueAt(ix);
         }
    @@ -225,6 +227,8 @@ sp<MediaCodecInfo> MediaCodecInfo::FromParcel(const Parcel &parcel) {
             if (caps == NULL)
                 return NULL;
             if (info != NULL) {
    +            ALOGD("%s %d codec:%s mime:%s",
    +                 __FUNCTION__, __LINE__, info->mName.c_str(), mime.c_str());
                 info->mCaps.add(mime, caps);
             }
         }
    @@ -246,7 +250,9 @@ status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
    
     ssize_t MediaCodecInfo::getCapabilityIndex(const char *mime) const {
         if (mime) {
    +        ALOGD("%s mCaps.size:%d", __FUNCTION__, mCaps.size());
             for (size_t ix = 0; ix < mCaps.size(); ix++) {
    +            ALOGD("search mime:%s in [%d] %s", mime, ix, mCaps.keyAt(ix).c_str());
                 if (mCaps.keyAt(ix).equalsIgnoreCase(mime)) {
                     return ix;
                 }
    @@ -284,6 +290,8 @@ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
         }
         sp<MediaCodecInfo::Capabilities> caps = new MediaCodecInfo::Capabilities();
         mInfo->mCaps.add(AString(mime), caps);
    +    ALOGD("%s %d codec:%s mime:%s",
    +                 __FUNCTION__, __LINE__, mInfo->mName.c_str(), mime);
         return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
                 new MediaCodecInfo::CapabilitiesWriter(caps.get()));
     }
    @@ -291,6 +299,8 @@ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
     bool MediaCodecInfoWriter::removeMime(const char *mime) {
         ssize_t ix = mInfo->getCapabilityIndex(mime);
         if (ix >= 0) {
    +        ALOGD("%s %d codec:%s mime:%s",
    +                 __FUNCTION__, __LINE__, mInfo->mName.c_str(), mime);
             mInfo->mCaps.removeItemsAt(ix);
             return true;
         }
    diff --git a/frameworks/av/media/libstagefright/MediaCodecList.cpp b/frameworks/av/media/libstagefright/MediaCodecList.cpp
    index eaff283..ffe3083 100644
    --- a/frameworks/av/media/libstagefright/MediaCodecList.cpp
    +++ b/frameworks/av/media/libstagefright/MediaCodecList.cpp
    @@ -14,7 +14,7 @@
      * limitations under the License.
      */
    
    -//#define LOG_NDEBUG 0
    +#define LOG_NDEBUG 0
     #define LOG_TAG "MediaCodecList"
     #include <utils/Log.h>
    
    @@ -243,8 +243,10 @@ ssize_t MediaCodecList::findCodecByType(
         size_t numCodecInfos = mCodecInfos.size();
         for (; startIndex < numCodecInfos; ++startIndex) {
             const MediaCodecInfo &info = *mCodecInfos[startIndex];
    -
    +        ALOGD("%s %s", __FUNCTION__, info.getCodecName());
             if (info.isEncoder() != encoder) {
    +            ALOGD("%s isEncoder:%d == encoder:%d",
    +                    __FUNCTION__, info.isEncoder(), encoder);
                 continue;
             }
             sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
    

    调试文件的文件在 log\mediacodeclist.log

    相关文章

      网友评论

          本文标题:OMX Service Codec加载

          本文链接:https://www.haomeiwen.com/subject/ghsolrtx.html