Android 9.0 multimedia框架解析(一)加载media extractor组件过程

Extractor

Extractor在multimedia框架扮演着解析器的角色,用于解析文件的封装。extractor会把视频文件解析成音频流和视频流,把音频文件解析成音频流。在这里,我只针播放音频文件这一场景进行总结。

类说明:

1.FileSource:根据构造时传递的文件描述符或者文件路径对多媒体文件进行读取等操作。
2.RemoteDataSource:binder server。在加载解析器的过程中,FileSource被保存位RemoteDataSource的本地对象,供代理进行文件读取的操作。所以,通过RemoteDataSource这个binder服务,extractor可以跨进程访问文件数据。
3.CallbackDataSource:顾名思义就是用于回调DataSource,在加载extractor过程中,它保存了RemoteDataSource的远程对象在自己的私有变量sp mIDataSource中,通过mIDataSource,CallbackDataSource跨进程操作FileSource,从而访问多媒体文件。
4.TinyCacheSource:从字面上理解就是:小型缓冲数据源。它保存了CallbackDataSource的对象,通过CallbackDataSource访问多媒体文件。
5.MediaExtractorFactory:工具类,里面都是static函数。用于加载extractor组件,创建idatasource等
6.MediaExtractor:extractor组件需继承它。在8.0版本中,它是一个binder server;而在9.0的版本中,它只是一个类而已,通过其他binder服务来调用它,间接binder化。通过它可以获得解析器的音频轨或者视频轨的信息。
7.RemoteMediaExtractor:binder server。RemoteMediaExtractor跟RemoteDataSource的作用是相似的,它也是将MediaExtractor保存为私有变量中,它的binder client通过它可以访问到MediaExtractor,间接解析多媒体文件。
8.NuMediaExtractor:在NuMediaExtractor中,会去保存RemoteMediaExtractor的远程代理对象,也就是binder client。它其实就是针对MediaExtractor的再一次封装,比如它在调用RemoteMediaExtractor获取文件格式的时候,获取得到的是MetaData,然后它会对MetaData进行处理,转化成AMessage,然后再送给调用者。这个类一般是调用SoundPool解析音频文件的时候才会用到的。使用MediaPlayer播放音频文件是不会走这个类的。
9.ExtractorPlugin:解析器组件。每一个extractor so都对应一个组件,这个组件会保存so库的句柄,路径及库中定义的解析器信息。

总bouml时序图

下面这张时序图展现了开机时,SoundPool解析ogg等多媒体文件时加载解析器的过程。SoundPool跟mediaplayer一样都是用来播放音频的接口,它一般用于解析和播放短小的音频,例如按键音,警告音等。
Android 9.0 multimedia框架解析(一)加载media extractor组件过程_第1张图片

上面这个时序图包含以下几个过程:
1.创建FileSource,然后为它创建binder server:RemoteDataSource,目的是为了提供IDataSource接口,跨进程访问FileSource。
2.创建TinyCacheSource,封装IDataSource接口,供media.extractor进程(pe -e | grep media可以看到这个进程)使用。
3.更新Extractors,打开system/lib/extractor下的所有extractor,并保存到MediaExtractorFactory的gPlugins成员中。
4.将之前创建的TinyCacheSource依次传递给每一个extractor,调用每一个extractor的sniff函数,如果extractor可以解析此文件,则会设置标志,之后就是使用这个extractor来解析这个文件。
5.创建RemoteMediaExtractor,封装IMediaExtractor接口,以便解码时使用。

分析DataSource

之所以分析DataSource,是因为有的开源解码器需要根据文件路径名来进行初始化操作,这就需要分析DataSource的相关代码,了解到如何获取文件路径。在soundpool和mediaplayer设置数据源setDataSorce的时候会创建FileSource,它的构造函数如下,可见,在构造的时候,FileSource就已经根据传递进来的文件描述符fd获取到文件的路径名。并且,它提供了toString接口去获得文件路径名。

FileSource::FileSource(const char *filename){
    mFd = open(filename, O_LARGEFILE | O_RDONLY);
    mName = String8::format( "FileSource(fd(%s), %lld, %lld)", nameForFd(fd).c_str(),(long long) mOffset,(long long) mLength);
}
class FileSource : public DataSource {
    virtual String8 toString() { return mName; }
 }

既然通过FileSource就可以获取到文件路径名,那么我们只需要分析FileSource的相关框架就行了。下图是FileSOurce的相关类图:
Android 9.0 multimedia框架解析(一)加载media extractor组件过程_第2张图片
在上面的分析和bouml中,我们知道了RemoteDataSource这一侧属于binder server。它的代码片如下:

class RemoteDataSource : public BnDataSource {
    virtual String8 toString()  {
        return mName;
    }
    explicit RemoteDataSource(const sp<DataSource> &source) {
        mSource = source;
        mName = String8::format("RemoteDataSource(%s)", mSource->toString().string());
    }
}

可以看出它提供了toString接口,返回了一个String8类型的mName,这个mName在构造的时候已经赋值了。因为这个参数source是FileSource,所以调用的是FileSource的toString。可见binder server这一侧已经保存了文件路径名,并且提供了binder接口toString。这样只要使用binder 接口IDataSource的toString()就可以获取到文件的路径名。
从DataSource的类图关系中可以知道:CallbackDataSource拥有IDataSource binder接口;TinyCacheSource的mSource就是CallbackDataSource,并且TinyCacheSource的toString()就是调用mSource的toString()。所以,只要调用TinyCacheSource的toString就可以获得FileSource保存的文件路径名。
现在来看下上面的时序图。new TinyCacheSource 创建出来的对象作为localSource参数传递给了MediaExtractorFactory::CreateFromService,然后经过一系列内部调用在调用(*it)->def.sniff的时候把刚才传递进来的localSource作为参数传递进去。这里的sniff就是每一个extractor的静态函数:

static MediaExtractor::CreatorFunc Sniff( DataSourceBase *source, float *confidence, void **meta,MediaExtractor::FreeMetaFunc *freeMeta)

根据上面的分析及类图关系,要想获得文件路径名,可以这样做:

static MediaExtractor::CreatorFunc Sniff( DataSourceBase *source,...){
	 sp<DataSource> dataSource = static_cast<DataSource*>(source);
	str = dataSource->toString();
	ALOGD("file name: %s",str.c_str());
}

获取的路径名是这样的:

TinyCacheSource(CallbackDataSource(493->496, RemoteDataSource(FileSource(fd(/storage/emulated/0/Music/music/lunisolar.
ape), 0, 26460428))))

然后自己用字符串截取函数截取下就ok了。

更新extractor

Android 9.0 multimedia框架解析(一)加载media extractor组件过程_第3张图片
在调用MediaExtractorFactory::UpdateExtractors时会去遍历system/lib/extractors/下的所有so库,调用dlopen,对每一个存在GETEXTRACTORDEF符号的库的信息保存到ExtractorPlugin中(当然这里会判断是否已经注册了),最后把这个信息列表保存到gPlugins。

class MediaExtractorFactory {
	static std::shared_ptr<List<sp<ExtractorPlugin>>> gPlugins;
}

创建IMediaExtractor

Android 9.0 multimedia框架解析(一)加载media extractor组件过程_第4张图片
获取到IMediaExtractor就可以访问binder server RemoteMediaExtractor。开头说过RemoteMediaExtractor里面保存有MediaExtractor,一个MediaExtractor对应一个RemoteMediaExtractor。这里的MediaExtractor是个功能基类,谷歌自带的或者自己实现的解析器都需要继承它。比如系统中的解析器有:MP3Extractor、MPEG2TSExtractor.cpp、AACExtractor.cpp、FLACExtractor.cpp等等。
从时序图中可以看出,在MediaExtractorFactory::sniff中,会取出gPlugins保存的每一个extractor库,然后调用它们的sniff函数。例如MP3Extractor中的sniff函数会去判断这个数据源是不是属于MP3格式的,如果是则会设置confidence为正数。MediaExtractorFactory::sniff会返回confidence值最大的那个extractor的CreatorFunc。CreatorFunc是每一个extractor 库需要创建的一个static函数,用于构造extractor。

//MediaExtractorFactory.cpp
sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(...){
	MediaExtractor::CreatorFunc creator = NULL;	//sniff返回后的CreatorFunc会保存到这里
	creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
	MediaExtractor *ret = creator(source.get(), meta);		//相当于调用具体extractor的CreateExtractor函数,如果是mp3的source,则调用的是下面这个函数
	return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);		//创建IMediaExtractor
}
//MP3Extractor.cpp
static MediaExtractor* CreateExtractor(DataSourceBase *source,void *meta) {	
    Mp3Meta *metaData = static_cast<Mp3Meta *>(meta);
    return new MP3Extractor(source, metaData);
}

CreateIMediaExtractorFromMediaExtractor是InterfaceUtils.cpp里面的函数,我在画时序图的时候为了画面简洁,把它画到MediaExtractorFactory里面去了。这个函数把刚才创建的MediaExtractor,还有属于它的DataSource,ExtractorPlugin都传递给RemoteMediaExtractor,并构造RemoteMediaExtractor。
因为MediaExtractor仅仅是一个本地类,无法提供跨进程访问接口,所以需要一个binder server为它提供接口,这个server就是RemoteMediaExtractor。来看看RemoteMediaExtractor的相关代码

//RemoteMediaExtractor.cpp
RemoteMediaExtractor::RemoteMediaExtractor(MediaExtractor *extractor, const sp<DataSource> &source,const sp<RefBase> &plugin)
    :mExtractor(extractor),mSource(source),mExtractorPlugin(plugin) {
    ......
 }
 size_t RemoteMediaExtractor::countTracks() {
    return mExtractor->countTracks();
}
//RemoteMediaExtractor.h
 class RemoteMediaExtractor : public BnMediaExtractor { // IMediaExtractor wrapper to the MediaExtractor.
 }

可以看出RemoteMediaExtractor就是MediaExtractor的wrapper(封装),只要获得IMediaExtractor,就可以跨进程访问MediaExtractor,比如MP3Extractor。最后就是调用registerMediaExtractor把IMediaExtractor保存到ExtractorInstance里面,方便后面使用。

你可能感兴趣的:(Android,9.0,multimedia)