全新系列文章已更新:
- Android Media Framework - 开篇
- Android Media Framework(一)OpenMAX 框架简介
- Android Media Framework(二)OpenMAX 类型阅读与分析
- Android Media Framework(三)OpenMAX API阅读与分析
- Android Media Framework(四)Non-Tunneled组件的状态转换与buffer分配过程分析
- Android Media Framework(五)Tunnel Mode
- Android Media Framework(六)插件式编程与OMXStore
- Android Media Framework(七)MediaCodecService
- Android Media Framework(八)OMXNodeInstance - Ⅰ
- Android Media Framework(九)OMXNodeInstance - Ⅱ
- Android Media Framework(十)OMXNodeInstance - Ⅲ
- Android Media Framework(十一)OMXNodeInstance - Ⅳ
- Android Media Framework(十二)OMXNodeInstance - Ⅴ
- Android Media Framework(十三)ACodec - Ⅰ
- Android Media Framework(十四)ACodec - Ⅱ
- Android Media Framework(十五)ACodec - Ⅲ
- Android Media Framework(十六)ACodec - Ⅳ
上一篇我们了解了 GenericSource 需要依赖 IMediaExtractor 完成 demux 工作,这一篇我们就来学习 android media 框架中的第二个服务 media.extractor,看看 IMediaExtractor 是如何创建与工作的。
1、MediaExtractorService
media.extractor 和 media.player 是属于同一层级的 binder service,都用于提供媒体播放支持,media.player 用于提供播放器功能,而 media.extractor 主要用于本地文件播放的解封装工作。
MediaExtractorService 相关代码位于:
frameworks/av/services/mediaextractor
frameworks/av/media/libmedia
frameworks/av/media/libstagefright
media.extractor 中提供服务的主体是 MediaExtractorService ,创建 IMediaExtractor 需要借助 MediaExtractorService 的力量,这一点和 media.player 的设计是一致的,创建 IMediaPlayer 需要借助 MediaPlayerService 的力量。这两个 service 起着创建和管理实际工作实例的功能。
MediaExtractorService 所继承的 BnMediaExtractorService 由 aidl 文件生成,IMediaExtractorService.aidl
在 libmedia 目录下,总共有3个方法可供使用:
interface IMediaExtractorService {
IMediaExtractor makeExtractor(IDataSource source, @nullable @utf8InCpp String mime);
IDataSource makeIDataSource(in FileDescriptor fd, long offset, long length);
@utf8InCpp String[] getSupportedTypes();
}
接下来看 MediaExtractorService 中到底包含哪些内容。首先来看构造函数:
MediaExtractorService::MediaExtractorService() {
MediaExtractorFactory::LoadExtractors();
}
构造函数调用了 MediaExtractorFactory::LoadExtractors 将平台上所有的 extractor lib 加载到 MediaExtractorService 进程:
void MediaExtractorFactory::LoadExtractors() {
Mutex::Autolock autoLock(gPluginMutex);
std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
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));
}
}
}
}
lib 加载总共分为2步:
- 到
/apex/com.android.media/lib/extractor
/system/lib/lib
/system_ext/lib
这三个目录下寻找名字中包含 extractor.so 的 lib,打开这些 lib 并用 dlsym 找到 lib 中的GETEXTRACTORDEF
函数,最后将 libHandle,GETEXTRACTORDEF 的返回值ExtractorDef
以及 path 封装成一个 ExtractorPlugin 存到 list 当中; - 遍历 ExtractorPlugin 读取 ExtractorDef 中定义的可以支持的扩展名,存储到一个列表中。
初学的时候看这里觉得晕晕乎乎的,一层套一层让人很难理解,但是从框架的角度来看就很清除很多。
所有的 extractor 都要遵循设计规则,实现 GETEXTRACTORDEF
函数,函数的返回值是一个 ExtractorDef
对象,ExtractorDef 声明在 MediaExtractorPluginApi.h 中:
struct ExtractorDef {
// version number of this structure
const uint32_t def_version;
// A unique identifier for this extractor.
// See below for a convenience macro to create this from a string.
media_uuid_t extractor_uuid;
// Version number of this extractor. When two extractors with the same
// uuid are encountered, the one with the largest version number will
// be used.
const uint32_t extractor_version;
// a human readable name
const char *extractor_name;
union {
struct {
SnifferFunc sniff;
} v2;
struct {
SnifferFunc sniff;
// a NULL terminated list of container mime types and/or file extensions
// that this extractor supports
const char **supported_types;
} v3;
} u;
};
以下是结构体中成员的含义:
def_version
:这个结构体的版本;extractor_uuid
:当前 extractor 独一无二的 id,可以使用constUUID
方法来生成 ,使用方法查看该方法注释;extractor_version
:extractor 的版本,如果两个 extractor uuid 相同,那么加载时只会存储高版本的那个;extractor_name
:extractor 的名字,同样可以由我们自己定义;u
:它是一个联合体,我们只关注 v3,v3 中有一个 SnifferFunc,这就是后面选择 extractor 的依据;supported_types 表示当前 extractor 所支持的 mine type 或者 文件后缀,必须要以 NULL结尾。
MediaExtractorFactory::LoadExtractors 那么多步骤核心就是或者每个 extractor lib 所定义的 ExtractorDef。
使用 dumpsys media.extractor
查看服务的信息,可以看到加载到的 extractor 的名称,版本,路径和支持解析的文件类型,这些都是从 ExtractorDef 中获取到的。
generic_x86:/ $ dumpsys media.extractor
Available extractors:
AAC Extractor: plugin_version(3), uuid(4fd80eae03d24d729eb948fa6bb54613), version(1), path(/apex/com.android.media/lib/extractors/libaacextractor.so), supports: aac
AMR Extractor: plugin_version(3), uuid(c86639c92f3140aca715fa01b4493aaf), version(1), path(/apex/com.android.media/lib/extractors/libamrextractor.so), supports: amr awb
FLAC Extractor: plugin_version(3), uuid(1364b048cc454fda9934327d0ebf9829), version(1), path(/apex/com.android.media/lib/extractors/libflacextractor.so), supports: flac fl
MIDI Extractor: plugin_version(3), uuid(ef6cca0af8a243e6ba5fdfcd7c9a7ef2), version(1), path(/apex/com.android.media/lib/extractors/libmidiextractor.so), supports: imy mid midi mxmf ota rtttl rt
x smf xmf
MP3 Extractor: plugin_version(3), uuid(812a3f6cc8cf46deb5293774b14103d4), version(1), path(/apex/com.android.media/lib/extractors/libmp3extractor.so), supports: mp2 mp3 mpeg mpg mpga
MP4 Extractor: plugin_version(3), uuid(27575c6744174c548d3d8e626985a164), version(2), path(/apex/com.android.media/lib/extractors/libmp4extractor.so), supports: 3g2 3ga 3gp 3gpp 3gpp2 m4a m4r
m4v mov mp4 qt
MPEG2-PS/TS Extractor: plugin_version(3), uuid(3d1dcfebe40a436da574c2438a555e5f), version(1), path(/apex/com.android.media/lib/extractors/libmpeg2extractor.so), supports: m2p m2ts mts ts
Matroska Extractor: plugin_version(3), uuid(abbedd9238c44904a4c1b3f45f899980), version(1), path(/apex/com.android.media/lib/extractors/libmkvextractor.so), supports: mka mkv webm
Ogg Extractor: plugin_version(3), uuid(8cc5cd06f772495e8a62cba9649374e9), version(1), path(/apex/com.android.media/lib/extractors/liboggextractor.so), supports: oga ogg opus
WAV Extractor: plugin_version(3), uuid(7d61385858374a3884c5332d1cddee27), version(1), path(/apex/com.android.media/lib/extractors/libwavextractor.so), supports: wav
看到这里我会有一些疑问:为什么 MediaExtractor 要以服务的形式提供呢?
我想到的可能有这些:
- 如果要有一个进程要使用 extractor,按照 MediaExtractorFactory 的加载方法,每个进程都要加载所有的 lib,这是很耗费时间的。放到 MediaExtractorService 一个地方加载,其他地方就都可以使用了。
- 创建的所有的 IMediaExtractor 都可以用 MediaExtractorService 管理;
- 如果要新增 extractor,只要按照设计规则实现,编译push到指定目录下就可以生效,不用修改加载部分的代码;
但是,我觉得 extractor 不需要以服务的形式存在,以服务的形式提供反而会损失性能,按照 ffmpeg 的方式,用一个函数指针列表来存储 ExtractorDef 效率应该也很高。
带着这个问题我们一起来看下面的内容。
2、MediaExtractorFactory
4、IMediaExtractor
IMediaExtractor 的层级结构在上面的图中已经画出来了,它比 IDataSource 要简单很多,CMediaExtractor 和 MediaExtractorCUnwrapper 这两层完全可以忽略,只是接口封装而已,IMediaExtractor 就不再做过多了解。
5、IMediaSource
原文阅读:
Android Media Framework(九)MediaExtractorService
扫描下方二维码,关注公众号《青山渺渺》阅读音视频开发内容。