Android12 MultiMedia框架之MediaExtractorService

15 篇文章 0 订阅
8 篇文章 0 订阅

上节学到setDataSource()时会创建各种Source,source用来读取音视频源文件,读取到之后需要demux出音、视频、字幕数据流,然后再送去解码。那么负责进行demux功能的media extractor模块是在什么时候阶段创建的?这里暂时不考虑APP创建的情况,以前面学过的GenericSource为例,它是在prepare阶段被创建的。本节暂时不分析GenericSource创建extractor的流程,先来看看MediaExtractorService的启动过程。

mediaextractor

MediaExtractorService的服务名为mediaextractor:

//frameworks/av/services/mediaextractor/mediaextractor.rc
service mediaextractor /system/bin/mediaextractor
    class main
    user mediaex
    group drmrpc mediadrm
    ioprio rt 4
    writepid /dev/cpuset/foreground/tasks

直接看main函数:

//frameworks/av/services/mediaextractor/main_extractorservice.cpp
int main(int argc __unused, char** argv)
{

#if __has_feature(hwaddress_sanitizer)
    ALOGI("disable media.extractor memory limits (hwasan enabled)");
#else
    ALOGI("enable media.extractor memory limits");
    limitProcessMemory(
        "ro.media.maxmem", /* property that defines limit */
        SIZE_MAX, /* upper limit in bytes */
        20 /* upper limit as percentage of physical RAM */);
#endif

    signal(SIGPIPE, SIG_IGN);

    //b/62255959: this forces libutis.so to dlopen vendor version of libutils.so
    //before minijail is on. This is dirty but required since some syscalls such
    //as pread64 are used by linker but aren't allowed in the minijail. By
    //calling the function before entering minijail, we can force dlopen.
    android::report_sysprop_change();

    SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath);

    strcpy(argv[0], "media.extractor");
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    MediaExtractorService::instantiate();

    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

由于MediaExtractorService继承自模板类BinderService,所以直接调用它的instantiate()来创建service且加入service manager中:

//frameworks/native/include/binder/BinderService.h
static void instantiate() { publish(); }

static status_t publish(bool allowIsolated = false,
                            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
    sp<IServiceManager> sm(defaultServiceManager());
    return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
                              dumpFlags);
}

接下来主要看MediaExtractorService构造函数做了什么:

//frameworks/av/services/mediaextractor/MediaExtractorService.cpp
MediaExtractorService::MediaExtractorService() {
    MediaExtractorFactory::LoadExtractors();
}

其直接调用到MediaExtractorFactory中的LoadExtractors()方法:

//frameworks/av/media/libstagefright/MediaExtractorFactory.cpp
// static
void MediaExtractorFactory::LoadExtractors() {
    Mutex::Autolock autoLock(gPluginMutex);

    if (gPluginsRegistered) {
        return;
    }

    gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);

    std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());

    android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
    if (mediaNs != NULL) {
        const android_dlextinfo dlextinfo = {
            .flags = ANDROID_DLEXT_USE_NAMESPACE,
            .library_namespace = mediaNs,
        };
        RegisterExtractors("/apex/com.android.media/lib"
#ifdef __LP64__
                "64"
#endif
                "/extractors", &dlextinfo, *newList);

    } else {
        ALOGE("couldn't find media namespace.");
    }

    RegisterExtractors("/system/lib"
#ifdef __LP64__
            "64"
#endif
            "/extractors", NULL, *newList);

    RegisterExtractors("/system_ext/lib"
#ifdef __LP64__
            "64"
#endif
            "/extractors", NULL, *newList);

    newList->sort(compareFunc);
    gPlugins = newList;

    for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
        if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
            for (size_t i = 0;; i++) {
                const char* ext = (*it)->def.u.v3.supported_types[i];
                if (ext == nullptr) {
                    break;
                }
                gSupportedExtensions.push_back(std::string(ext));
            }
        }
    }

    gPluginsRegistered = true;
}

简单描述下这段代码所做的操作:

  • 创建一个list用来保存即将获取到的指向ExtractorPlugin对象的sp指针。
  • 依次到如下目录通过RegisterExtractors()方法逐个注册ExtractorPlugin到list中:
    • /apex/com.android.media/lib(64)/extractors
    • /system/lib(64)/extractors
    • /system_ext/lib(64)/extractors
  • 将list内的内容按extractor_name从小到大的顺序重新排序,并将其保存到gPlugins中。
  • 最后一个for循环,主要是将各个extractor所支持的mime type或者文件扩展名保存到gSupportedExtensions中,可用于后续查询。

再简单看看RegisterExtractors()方法:

//frameworks/av/media/libstagefright/MediaExtractorFactory.cpp
void MediaExtractorFactory::RegisterExtractors(
        const char *libDirPath, const android_dlextinfo* dlextinfo,
        std::list<sp<ExtractorPlugin>> &pluginList) {
    ALOGV("search for plugins at %s", libDirPath);

    DIR *libDir = opendir(libDirPath);
    if (libDir) {
        struct dirent* libEntry;
        while ((libEntry = readdir(libDir))) {
            if (libEntry->d_name[0] == '.') {
                continue;
            }
            String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
            if (!libPath.contains("extractor.so")) {
                continue;
            }
            void *libHandle = android_dlopen_ext(
                    libPath.string(),
                    RTLD_NOW | RTLD_LOCAL, dlextinfo);
            if (libHandle == nullptr) {
                ALOGI("dlopen(%s) reported error %s", libPath.string(), strerror(errno));
                continue;
            }

            GetExtractorDef getDef =
                (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
            if (getDef == nullptr) {
                ALOGI("no sniffer found in %s", libPath.string());
                dlclose(libHandle);
                continue;
            }

            ALOGV("registering sniffer for %s", libPath.string());
            RegisterExtractor(
                    new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
        }
        closedir(libDir);
    } else {
        ALOGI("plugin directory not present (%s)", libDirPath);
    }
}

主要功能如下:

  • 遍历指定目录下的所有后缀为"extractor.so"的库并将其通过dlopen()打开。
  • 再通过dlsym()方法获取到GETEXTRACTORDEF()函数的指针。
  • 对于每一个extractor创建一个对应的ExtractorPlugin,然后将他们一个个的加入pluginList中。

到此,MediaExtractorService就启动完成了,所做的事情也是相当简单:加载目标目录下所有的extractor并保存到一个list中。

MediaExtractorService还提供了另外两个接口:makeExtractor()和makeIDataSource()。通过搜索code,大概总结一下:

  • makeIDataSource():提供给GenericSource调用,用于根据本地播放文件创建出一个IDataSource对象。这个对象进一步会被封装成一个TinyCacheSource对象,用于后面创建extractor。
  • makeExtractor():会被封装到MediaExtractorFactory::Create()方法中,该方法会被GenericSource调用,还会被JNI/JAVA调用来创建extractor。

简单以图来总结下:

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值