当前位置: 首页 > news >正文

Android12 MultiMedia框架之MediaExtractorService

上节学到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/mediaextractorclass mainuser mediaexgroup drmrpc mediadrmioprio rt 4writepid /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)");
#elseALOGI("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 */);
#endifsignal(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。

简单以图来总结下:

http://www.lryc.cn/news/388861.html

相关文章:

  • Chapter 8 Feedback
  • Administrators就最高了???system是什么??本地用户提权内网学习第三天 你知道uac是什么??
  • 回溯 | Java | LeetCode 39, 40, 131 做题总结(未完)
  • Linux系统上部署Whisper。
  • 申请一张含100个域名的证书-免费SSL证书
  • 爬数据是什么意思?
  • Pytorch实战(二)
  • wordpress 付费主题modown分享,可实现资源付费
  • 【INTEL(ALTERA)】NIOS II调试器中的重新启动按钮不起作用
  • Hive On Spark语法
  • 利用 fail2ban 保护 SSH 服务器
  • 在TkinterGUI界面显示WIFI网络摄像头(ESP32s3)视频画面
  • Yolov8训练时遇到报错SyntaxError: ‘image_weights‘ is not a valid YOLO argument.等问题解决方案
  • javaweb(四)——过滤器与监听器
  • 冗余电源的应用,哪些工作站支持冗余电源
  • [信号与系统]IIR滤波器与FIR滤波器相位延迟定量的推导。
  • Python海量数据处理脚本大集合:pyWhat
  • postgresql搭建
  • Web 品质标准
  • 深入理解PyTorch:原理与使用指南
  • 【MySQL事务】深刻理解事务隔离以及MVCC
  • 关于Mac mini 10G网口的问题
  • 计算机网络-第4章 网络层
  • pytorch跑手写体实验
  • 利用Java的`java.util.concurrent`包优化多线程性能
  • 软件著作权申请:开发者的重要保障与助力
  • WLAN Hostapd配置参数详解 - CN
  • Excel 宏录制与VBA编程 ——VBA编程技巧篇一 (Union方法、Resize方法、Cells方法、UseSelect方法、With用法)
  • 基于路径长度的样条插补算法(自动驾驶和路径跟踪控制适用)
  • net Framework OAuth2.0