分享

Android 9.0 multimedia框架解析(三)prepareAsync过程

 开花结果 2020-04-28

概述

先来回顾一下上文讲的MediaPlayerService::Client的setDataSource流程:首先创建NuplayerDriver和Nuplayer;然后通过NuplayerDriver以异步的方式设置数据源,在设置的过程中会构造GenericSource,把文件描述符等信息保存在GenericSource中;最后Nuplayer会通知NuplayerDriver我已经设置完数据源了,并且把NuplayerDriver中的状态设置为STATE_UNPREPARED,也就是未准备状态。由于setDataSource已经把状态设置为unprepare状态,所以下一个操作就是prepare操作了。我这里以prepareAsync为入口分析multimedia的处理流程。

类说明

1.RemoteMediaExtractor:binder server。RemoteMediaExtractor跟RemoteDataSource的作用是相似的,它也是将MediaExtractor保存为私有变量中,它的binder client通过它可以访问到MediaExtractor,间接解析多媒体文件。

2.MediaExtractor:extractor组件需继承它。在8.0版本中,它是一个binder server;而在9.0的版本中,它只是一个类而已,通过其他binder服务来调用它,间接binder化。通过它可以获得解析器的音频轨或者视频轨的信息。

3.RemoteMediaSource:binder server。为MediaSource提供跨进程访问的接口。

4.MediaSource:extractor组件需要集成它。通过它可以读取文件的数据。

5.剩下的一些类说明可以参考Android 9.0 multimedia框架解析(一)加载media extractor组件过程.

总bouml时序图

下面的蓝色边框内的时序就是prepare相关的。

  

时序图说明:

prepare过程中有以下几个重要过程:

1.创建DataSource。前面的setDataSource仅仅是设置文件信息而已,这里才是真正的创建相关的数据源。

2.加载对应的解析器。并通过解析器创建解析器的source,这个source是MediaTrack格式,用于获取解析之后的文件数据。

3.从解析器中读取出已经解析完毕的数据,准备发送给ACodec解码。

创建DataSource

在上一节的分析中,我们知道了setDataSource过程的是:创建NuPlayerDriver、Nuplayer,然后调用它们的相关接口去创建GenericSource并把文件描述符等信息保存到GenericSource中。这个过程并没有创建DataSource,其实创建DataSource的真正过程是在这里。

在setDataSource结束的时候,设置了NuPlayerDriver的mState状态为STATE_UNPREPARED。这个mState在prepareAsync中会做判断,如果是STATE_UNPREPARED,则调用NuPlayer的prepareAsync。

由于之前创建GenericSource的时候把GenericSource的父类指针Source传递给NuPlayer的mSource了,所以NuPlayer可以调用mSource来直接操作GenericSource。从NuPlayerDriver的prepareAsync到GenericSource的onPrepareAsync过程有两个消息发送接受过程,这样过程我不会去贴代码赘述,直接看bouml时序图,根据控制块的颜色来区分调用就行。

我们直接来看GenericSource的onPrepareAsync过程。

可以看出onPrepareAsync过程就是创建IDataSource,然后根据IDataSource创建本地的DataSource。这个过程跟 Android 9.0 multimedia框架解析(一)加载media extractor组件过程 的“分析DataSource”是类似的,这里就简单说明一下,通过IDataSource可以访问binder server端的FileSource,也就是源文件。

加载解析器

加载解析器的过程也跟 Android 9.0 multimedia框架解析(一)加载media extractor组件过程 的“更新extractor”和“创建IMediaExtractor”类似。这里只需要知道通过IMediaExtractor,可以访问到MediaExtractor(比如MP3Extractor)

这里的IMediaSource、IMediaExtractor、MediaTrack和MediaExtractor这些类有点乱,我这里整理个类图展示下它们的关系。

  

类图说明:

1.箭头说明可以在这里看这篇博客UML类图(Class Diagram)中类与类之间的关系及表示方式。

2.这里的实线和虚线箭头(非三角形)(Association关系)严格来说应该指向父类的,比如RemoteMediaExtractor的实线箭头应该指向MediaExtractor。但是这样子又无法直接地展示类与类之间的关系。而且在android c++中,一个基类会有多个子类,如果把Association关系都表示在基类那里,那类图会显得毫无意义。

一个解析器包含有MediaExtractor,MediaTrack。MediaTrack相当于音轨,如果是音频文件一般只有一个音轨那就是音频轨,如果是视频文件那至少有两个音轨,一个是音乐的一个是视频的。开发者需要继承MediaTrack,重写read函数,提供解封装之后的数据流。比如MP3Source就是继承了MediaTrack。而MediaExtractor负责初始化解析,提供文件的详细信息,比如有多少个音轨;根据需求提供音轨,比如MP3Extractor::getTrack会直接返回MP3Source对象。

MP3Source和MP3Extractor都是本地类,没有remote功能。所以这里就需要一个wrapper来作为binder server提供给其他模块调用。这里就产生了RemoteMediaExtractor和RemoteMediaSource这两个wrapper。所以MediaExtractorFactory::Create(dataSource, NULL)返回的是RemoteMediaExtractor的父类IMediaExtractor,extractor->getTrack(i)返回的是RemoteMediaSource的父类IMediaSource。这样GenericSource只需要调用IMediaExtractor和IMediaSource就可以访问到解析器了,读取解析之后的文件资源了。

在类图顶端,有一个TinyCacheSource,它是一个DataSource,它的类图关系可以看 Android 9.0 multimedia框架解析(一)加载media extractor组件过程 的“分析DataSource”。这个类中有一个IDataSource,这个IDataSource是RemoteDataSource提供的,目的也是提供remote功能。

所以可以看出这些Remote*****都是一个提供remote功能的wrapper,比较本地类是没有跨进程调用的功能。

启动MP3Source

调用完initFromDataSource之后会调用finishPrepareAsync,然后调用startSources。这里会识别mAudioTrack.mSource和mVideoTrack.mSource是否非空,也就是识别音频解析器和视频解析器。在这里我只有音频解析器,所以只调用了mAudioTrack.mSource->start(),这个mAudioTrack.mSource前面已经赋值为IMediaSource,所以这个是调用到了MP3Source的start方法。

MP3Source的start方法也就是分配内存,设置一些播放位置信息,时间戳等信息。

从MP3Source read数据

在finishPrepareAsync中,还会去调用postReadBuffer,发送kWhatReadBuffer消息给自己的handle,然后调用到onReadBuffer(msg),这个函数会去调用readBuffer(trackType)。

下一节分析start过程…



原文链接:https://blog.csdn.net/qq_27136111/java/article/details/95357167

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多