![Android音视频开发](https://wfqqreader-1252317822.image.myqcloud.com/cover/334/31186334/b_31186334.jpg)
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=1739344595-Uyf0NMh8Yhq2uVw4lQGIFFJu8bnE92GB-0-79203ef3a40d3616639c3f079c8c08c7)
对应看看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=1739344595-lYrkZaujG8EdRbyKJLOkdXC8DLtOBnEg-0-3250e481b1aa4060c80b57f37daa0d86)
在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=1739344595-KSdMbFGSSFycdA1R7XNYus8K48T8KgT6-0-6654d082154204a1fa3b565bedb2e513)
以上代码对应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=1739344595-NBcLyHdjpuBj0Ki7mc4cbHW9g09ybeAO-0-af6f612c4f0413afd4d5d1fc1d87f578)
等价于
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_52.jpg?sign=1739344595-kFJPzvgjc2VTYLCklYtQCzBrmPrUc2Nb-0-8212673f322f1a8a50b1856ad30f64a9)
即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=1739344595-8ZRoGwDhdoBhiOl455vl9PoEkVKkvl9d-0-1e8d4cd062d5891738b509e805f5573d)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_54.jpg?sign=1739344595-bs286QMmgJSO2qKoKaI18dP3sRgSIvQV-0-2d728d25927fe0bad703122228819ef8)
接下来重新看看MediaPlayer中头文件定义的函数声明,方便对比Client中的函数,以下代码在frameworks/av/include/media/mediaplayer.h中:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_55.jpg?sign=1739344595-caSadANQXqseReA5VrNtV3bjs9oYFn7X-0-2ff6d47d59d5aca7682bdd9fc2c855eb)
这里的函数和Client中的函数是一一对应的,两者通过Client的代理类联系在了一起:
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_56.jpg?sign=1739344595-49mkYG2JLBvT3B6IdSg2DSFe2YYJMvWk-0-7ce5c2cfb86d48ee2782f9a73cd7db74)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_57.jpg?sign=1739344595-qL30eDDAiV6Lgk1S4dh9k1m9hiciRbpv-0-1d43fedaec87e93b0a1cc2497e1a95e7)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_58.jpg?sign=1739344595-h49hsAF0FKGMvM253QmsB1AoT0AI2aRL-0-dc2f0b24bd796dac6a699615b9f194ec)
上面的两个函数,一个是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=1739344595-JKayXFn6De8tAiM6ifw7TLaJzD26Q6Go-0-b409e272bd43f0fa2ce9631b317b164f)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_60.jpg?sign=1739344595-balKQ7v47mEcBCXLGwmaCtJoM8KJhRmI-0-ed07a83b30589bf5a9a182080819012f)
在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=1739344595-9GzesTFSd2aohZ4WpX0lT0ChB5k85lYw-0-c0777bb322e4cf25bbaac0c47afb3b8c)
![](https://epubservercos.yuewen.com/13E7E8/16896237204360306/epubprivate/OEBPS/Images/txt002_62.jpg?sign=1739344595-oRRIOMBSDTon6g0SycH8kv3Axvs2AaYU-0-2b0ee5b4d7428f6e7eabe7641567de46)
总结一下上面的代码,在内部定义一个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)的接口。