上節學到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。
簡單以圖來總結下: