- Android音视频开发
- 何俊林
- 1286字
- 2020-08-27 17:06:39
2.4 C++中MediaPlayer的C/S架构
在前面几节中,都是通过Java层调用到JNI层中,而JNI层向下到C++层并未介绍。本节首先分析Java层的一个函数在C++层MediaPlayer中的过程(路径为frameworks/av/media/libmedia/MediaPlayer.cpp)。
下面找一个我们熟悉的setDataSource函数来看看C(Client)/S(Server)模式的过程。setDataSource函数如下:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_47.jpg?sign=1739583930-DZaC72pvymFvvlJAIMvTin1Kt0iQd7Yk-0-c44f6203da0779af20fee5c4a6c22c52)
对应看看MediaPlayerService.cpp中的create函数,MediaPlayerService.cpp在C++ 6.0源码中处于frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中,代码如下:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_49.jpg?sign=1739583930-VZbiTwlZJjJUfFW1xHS9gQ7CFJ3MaUf4-0-628172f1d1465d0315603856631eb2c7)
在new Client中,有一个IPCThreadState。在Android中ProcessState是客户端和服务器端公共的部分,作为Binder通信的基础。ProcessState是一个singleton类,每个进程只有一个对象,这个对象负责打开Binder驱动,建立线程池,让其进程里面的所有线程都能通过Binder通信。
与之相关的是IPCThreadState,每个线程都有一个IPCThreadState实例登记在Linux线程的上下文附属数据中,主要负责Binder的读取、写入和请求处理。IPCThreadState在构造的时候获取进程的ProcessState并记录在自己的成员变量mProcess中,通过mProcess可以获得Binder的句柄。IPCThreadState通过IPCThreadState::transact把data及handle等填充进binder_transaction_data,在两个进程间通信。
这里这个Client到底是什么?我们又得追踪一下,在frameworks/av/media/libmediaplayerservice/MediaPlayerService.h中,如下:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_50.jpg?sign=1739583930-8tbba8EcucURSkrK1X2RKxJBkRzjH43b-0-cae5fb7bec104904ca455bba000f3648)
以上代码对应Java层的MediaPlayer相关方法。如果还记得图2-3的话,可以从整体上理解这个Client属于什么角色及位置。继承BnMediaPlayer,并包含了IMediaPlayer相关接口。
总结一下上面的代码,Client类的继承关系为Client->BnMediaPlayer->IMediaPlayer。分析上面的代码可以看出,create函数构造了一个Client对象,并将此Client对象添加到MediaPlayerService类的全局列表mClients中,这是一个SortedVector,紧接着执行player->setDataSource(url,headers),即Clients::setDataSource,因此在setDataSource中有如下语句:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_51.jpg?sign=1739583930-yMGzQETtiUGo6wwwnYyimqGCAYqG8mD6-0-1fdc352a443a6ad6b0eb022cdce525f8)
等价于
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_52.jpg?sign=1739583930-1pqPHJdpgGyT06pz69fznlv6VqfrRLIx-0-fd41f808284ea2d08707136740768ff5)
即player最终是用Client对象来初始化的,可以直接认为player==Client。
这时候问题来了,在C++中,这个Client及MediaPlayer又是什么关系呢?
• Client是MediaPlayerService内部的一个类,我们从上面的代码已知,因为MediaPlayerService运行在服务器端,故Client也运行在服务器端。
• Client在MediaPlayerService.h中,那接着看看MediaPlayerService中的实现,实现过程中调用过MediaPlayerService类的一些函数,同样回到setDataSource。代码如下:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_53.jpg?sign=1739583930-XjR3PnIR4tUsBqbmJdgHiCgADn2c0ZLC-0-a6dec71886aed9291554deecf7af39cb)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_54.jpg?sign=1739583930-gUIOVHoLm2ib63X7KBVyEzrJpFmSqcOW-0-0e2382e284d142780c67fa27f22e094c)
接下来重新看看MediaPlayer中头文件定义的函数声明,方便对比Client中的函数,以下代码在frameworks/av/include/media/mediaplayer.h中:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_55.jpg?sign=1739583930-jFcAfM7g6VoiVwp7UgXsGzS24otabh6M-0-7416bf74561f1920bd837ff5cbaf8cd7)
这里的函数和Client中的函数是一一对应的,两者通过Client的代理类联系在了一起:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_56.jpg?sign=1739583930-aNgSwu2sraYVcgrsWt65MczersoOrtHI-0-644e329053eead936f7572727af97282)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_57.jpg?sign=1739583930-I4Wd1QGhur0hilSC8EGw2l0166L7M2qR-0-78250e9999511bd7831fc8a619560456)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_58.jpg?sign=1739583930-7AS2qmmoZjmwmDFFW8I0P5RwAzenfO9E-0-491df3e3a4f3de070764e599b59cb7d2)
上面的两个函数,一个是MediaPlayer的setDataSource,会调到attachNewPlayer函数,这个函数最终会调用服务器端Client对应的函数。到这里可能有读者会想,IMediaPlayer.h和mediaplayer.h的区别是什么?那么下面介绍一下IMediaPlayer.h、mediaplayer.h、ImediaPlayer-Client.h的区别。
• 从包结构上看:IMediaPlayer和IMediaPlayerClient.h都在frameworks/av/media/libmedia包中,而mediaplayer.h在/av/include/media包中(前面已有代码贴出)。
• 从功能上看:它们肩负的职责也不一样。
这里贴出IMediaPlayer.h及IMediaPlayerClient.h的代码,IMediaPlayer.h位于frameworks/av/media/libmedia包中:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_59.jpg?sign=1739583930-wG8eT5mhehlPgiwU664qJMgSSr8J4MTD-0-f95411761894e74a46ff5812fba087c6)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_60.jpg?sign=1739583930-eHnLaeGTj0GxEwF1BW7fKWOUvlcf0rjF-0-e6178a779ecfdf8beb58b76aeea77bf3)
在IMediaPlayer.h中定义的基本上都是虚函数,而我们知道虚函数在C++中用于实现多态性(Polymorphism),多态性是将接口与具体实现代码进行了分离,用形象的语言来解释就是以共同的方法实现,但因个体差异而采用不同的策略。所以它的功能是实现MediaPlayer功能的接口,看到onTransact函数,自然联想Binder通信,把底层的Parcel指针类型数据向上层的另一个进程传递。
再分析一下IMediaPlayerClient.h,同样位于frameworks/av/media/libmedia包中:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_61.jpg?sign=1739583930-I0tJmi0rAhMOSxfjVZb1kG4ToIN9j4wW-0-fd181baa13344cf1db7feaf8f1cfcad6)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_62.jpg?sign=1739583930-n91Ph33q5JVLAk1PVuqmbPfbqdnshMqD-0-1e749036be264a4dfeaeb71b99a4d371)
总结一下上面的代码,在内部定义一个BpMediaPlayerClient类(也就是Client的父类),然后它有一个onTransact函数。一般onXXX都是被动回调过来的,不是由自己控制的,如Activity中的onCreate、onPause、onStart函数,这些函数都是在其他地方处理并通知到Activity中的。这里也是一样的,onTransact作为Binder通信中的回调函数,前面介绍到player实际上是C/S模式的,IMediaPlayerClient.h的功能是描述一个MediaPlayer客户端的接口。
综上所述,mediaplayer.h的功能是对外(JNI层)的接口类,它最主要的是定义了一个MediaPlayer类(C++层),我们在android_media_MediaPlayer.cpp中就引入了media/mediaplayer.h;IMediaPlayer.h则是一个实现MediaPlayer(C++层)功能的接口;而IMediaPlayerClient.h的功能是描述一个MediaPlayer客户端(这里暂且理解为前面说的Client)的接口。