第一部分 MediaPlayer概述
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video兩個應用程序都是調用MediaPlayer實現的。
MediaPlayer在底層是基於 OpenCore(PacketVideo)的庫實現的,為了構建一個MediaPlayer程序,上層還包含了進程間通訊等內容,這種進程間通訊的基礎是Android基本庫中的Binder機制。
以開源的Android為例, MediaPlayer的代碼主要在以下的目錄中:
JAVA程序的路徑:
packages/apps/Music/src/com/android/music/
JAVA類的路徑:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地調用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
這部分內容編譯成為目標是 libmedia_jni.so。
主要的頭文件在以下的目錄中:
frameworks/base/include/media/
多媒體底層庫在以下的目錄中:
frameworks/base/media/libmedia/
這部分的內容被編譯成庫 libmedia.so。
多媒體服務部分:
frameworks/base/media/libmediaplayerservice/
文件為mediaplayerservice.h和mediaplayerservice.cpp
這部分內容被編譯成庫 libmediaplayerservice.so。
基於OpenCore的多媒體播放器部分
external/opencore/
這部分內容被編譯成庫 libopencoreplayer.so。
從程序規模上來看,libopencoreplayer.so是主要的實現部分,而其他的庫基本上都是在其上建立的封裝和為建立進程間通訊的機制。
第二部分 MediaPlayer的接口與架構
2.1 整體框架圖
MediaPlayer的各個庫之間的結構比較復雜,可以用下圖的表示
在各個庫中,libmedia.so位於核心的位置,它對上層的提供的接口主要是MediaPlayer類,類libmedia_jni.so通過調用MediaPlayer類提供對JAVA的接口,並且實現了android.media.MediaPlayer類。
libmediaplayerservice.so是Media的服務器,它通過繼承libmedia.so的類實現服務器的功能,而libmedia.so中的另外一部分內容則通過進程間通訊和libmediaplayerservice.so進行通訊。libmediaplayerservice.so的真正功能通過調用OpenCore Player來完成。
MediaPlayer部分的頭文件在frameworks/base/include/media/目錄中,這個目錄是和libmedia.so庫源文件的目錄frameworks/base/media/libmedia/相對應的。主要的頭文件有以下幾個:
IMediaPlayerClient.h
mediaplayer.h
IMediaPlayer.h
IMediaPlayerService.h
MediaPlayerInterface.h
在這些頭文件mediaplayer.h提供了對上層的接口,而其他的幾個頭文件都是提供一些接口類(即包含了純虛函數的類),這些接口類必須被實現類繼承才能夠使用。
整個MediaPlayer庫和調用的關系如下圖所示:
整個MediaPlayer在運行的時候,可以大致上分成Client和Server兩個部分,它們分別在兩個進程中運行,它們之間使用Binder機制實現IPC通訊。從框架結構上來看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三個類定義了MeidaPlayer的接口和架構,MediaPlayerService.cpp和mediaplayer.coo兩個文件用於MeidaPlayer架構的實現,MeidaPlayer的具體功能在PVPlayer(庫libopencoreplayer.so)中的實現。
2.2 頭文件IMediaPlayerClient.h
IMediaPlayerClient.h用於描述一個MediaPlayer客戶端的接口,描述如下所示:
2.3 頭文件mediaplayer.h
mediaplayer.h是對外的接口類,它最主要是定義了一個MediaPlayer類:
從接口中可以看出MediaPlayer類剛好實現了一個MediaPlayer的基本操作,例如播放(start)、停止(stop)、暫停(pause)等。
另外的一個類DeathNotifier在MediaPlayer類中定義,它繼承了IBinder類中的DeathRecipient類:
事實上,MediaPlayer類正是間接地繼承了IBinder,而MediaPlayer:: DeathNotifier類繼承了IBinder:: DeathRecipient,這都是為了實現進程間通訊而構建的。
2.4 頭文件IMediaPlayer.h
IMediaPlayer.h主要的的內容是一個實現MediaPlayer功能的接口,它的主要定義如下所示:
在IMediaPlayer類中,主要定義MediaPlayer的功能接口,這個類必須被繼承才能夠使用。值得注意的是,這些接口和MediaPlayer類的接口有些類似,但是它們並沒有直接的關系。事實上,在MediaPlayer類的各種實現中,一般都會通過調用IMediaPlayer類的實現類來完成。
2.5 頭文件IMediaPlayerService.h
IMediaPlayerService.h用於描述一個MediaPlayer的服務,定義方式如下所示:
由於具有純虛函數,IMediaPlayerService 以及BnMediaPlayerService必須被繼承實現才能夠使用,在IMediaPlayerService定義的create和decode等接口,事實上是必須被繼承者實現的內容。注意,create的返回值的類型是sp<IMediaPlayer>,這個IMediaPlayer正是提供實現功能的接口。
第三部分 MediaPlayer的主要實現分析
3.1 JAVA程序部分
在packages/apps/Music/src/com/android/music/目錄的MediaPlaybackService.java文件中,包含了對MediaPlayer的調用。在MediaPlaybackService.java中包含對包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService類的內部,定義了MultiPlayer類:
MultiPlayer類中使用了MediaPlayer類,其中有一些對這個MediaPlayer的調用,調用的過程如下所示:
reset、setDataSource和setAudioStreamType等接口就是通過JAVA本地調用(JNI)來實現的。
3.2 MediaPlayer的JAVA本地調用部分
MediaPlayer的JAVA本地調用部分在目錄frameworks/base/media/jni/的android_media_MediaPlayer.cpp中的文件中實現。
android_media_MediaPlayer.cpp之中定義了一個JNINativeMethod(JAVA本地調用方法)類型的數組gMethods,如下所示:
JNINativeMethod的第一個成員是一個字符串,表示了JAVA本地調用方法的名稱,這個名稱是在JAVA程序中調用的名稱;第二個成員也是一個字符串,表示JAVA本地調用方法的參數和返回值;第三個成員是JAVA本地調用方法對應的C語言函數。
其中android_media_MediaPlayer_reset函數的實現如下所示:
在android_media_MediaPlayer_reset的調用中,得到一個MediaPlayer指針,通過對它的調用實現實際的功能。
register_android_media_MediaPlayer用於將gMethods注冊為的類"android/media/MediaPlayer",其實現如下所示。
"android/media/MediaPlayer"對應JAVA的類android.media.MediaPlayer。
3.3 mediaplayer的核心庫libmedia.so
libs/media/mediaplayer.cpp文件用於實現mediaplayer.h提供的接口,其中一個重要的片段如下所示:
其中最重要的一點是binder = sm->getService(String16("media.player"));這個調用用來得到一個名稱為"media.player"的服務,這個調用返回值的類型為IBinder,根據實現將其轉換成類型IMediaPlayerService使用。
一個具體的函數setDataSource如下所示:
在函數setDataSource函數中,調用getMediaPlayerService得到了一個IMediaPlayerService,又從IMediaPlayerService中得到了IMediaPlayer類型的指針,通過這個指針進行着具體的操作。
其他一些函數的實現也與setDataSource類似。
libmedia.so中的其他一些文件與頭文件的名稱相同,它們是:
libs/media/IMediaPlayerClient.cpp
libs/media/IMediaPlayer.cpp
libs/media/IMediaPlayerService.cpp
為了實現Binder的具體功能,在這些類中還需要實現一個BpXXX的類,例如IMediaPlayerClient.cpp的實現如下所示:l
還需要實現定義宏IMPLEMENT_META_INTERFACE,這個宏將被展開,生成幾個函數:
IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");
以上的實現都是基於Binder框架的實現方式,只需要按照模版實現即可。其中BpXXX的類為代理類(proxy),BnXXX的類為本地類(native)。代理類的transact函數和本地類的onTransact函數實現對應的通訊。
3.4 media服務libmediaservice.so
frameworks/base/media\libmediaplayerservice目錄中的MediaPlayerService.h和MediaPlayerService.cpp用於實現一個
servers/media/的服務,MediaPlayerService是繼承BnMediaPlayerService的實現,在這個類的內部又定義了類Client,MediaPlayerService::Client繼承了BnMediaPlayer。
在MediaPlayerService中具有如下一個靜態函數instantiate:
在instantiate函數中,調用IServiceManager的一個函數addService,向其中增加了一個名為"media.player"的服務。
這個名為"media.player"的服務和mediaplayer.cpp中調用getService中得到的使用一樣名稱。因此,在這里調用addService增加服務在mediaplayer.cpp中可以按照名稱"media.player"來使用。這就是使用Binder實現進程間通訊的(IPC)的作用,事實上這個MediaPlayerService類是在服務中運行的,而mediaplayer.cpp調用的功能在應用中運行,二者並不是一個進程。但是在mediaplayer.cpp卻像一個進程的調用一樣調用MediaPlayerService的功能。
在MediaPlayerService.cpp中的createPlayer函數如下所示:
在這里根據playerType的類型建立不同的播放器:對於大多數情況,類型將是PV_PLAYER,這時會調用了new PVPlayer()建立一個PVPlayer,然后將其指針轉換成MediaPlayerBase來使用;對於Mini文件的情況,類型為SONIVOX_PLAYER,將會建立一個MidiFile;對於Ogg Vorbis格式的情況,將會建立一個VorbisPlayer。
(OGG Vobis是一種音頻壓縮格式,與MP3等的音樂格式類似,它具有完全免費、開放和沒有專利限制的特點。)
值得注意的是PVPlayer、MidiFile和VorbisPlayer三個類都是繼承MediaPlayerInterface得到的,而MediaPlayerInterface又是繼承MediaPlayerBase得到的,因此三者具有相同接口類型。只有建立的時候會調用各自的構造函數,在建立之后,將只通過MediaPlayerBase接口來MediaPlayerBase控制它們。
在frameworks/base/media/libmediaplayerservice目錄中,MidiFile.h和MidiFile.cpp的實現MidiFile,VorbisPlayer.h和VorbisPlayer.cpp實現一個VorbisPlayer。
3.5 OpenCorePlayer的實現libopencoreplayer.so
OpenCore Player在external/opencore/中實現,這個實現是一個基於OpenCore的Player的實現。具體實現的文件為playerdriver.cpp。其中實現了兩個類:PlayerDriver和PVPlayer。PVPlayer通過調用PlayerDriver的函數實現具體的功能。