Android MediaPlayer的核心原理

本 文主要介紹的是Android中很重要也最爲複雜的媒體播放器(MediaPlayer)部分的架構。對於Android這樣一個完整又相對複雜的系統, 一個MediaPlayer功能的實現不在其具體的功能,而是具體功能如何適應Android系統Android MediaPlayer的主要具體實現在OpenCore的Player中,這部分不是本文的關注點。本文關注的是MediaPlayer系統的架構,其 他的一些Android的應用程序也使用類似的架構。 

對於開源事業在中國的發展,hanchao3c認爲應該共享的不僅僅是代碼,文 檔、設計思想、理念甚至對於技術的理解都應該得到充分的共享。 Android爲中國人進入大規模的開源項目提供了很好的機遇,對於走在技術前沿的人們,不應將技術視爲私有財產,而應該將自己理解更好地奉獻給大衆,提 高大衆的學習速度,從中也可以得到反饋,從而促進自己的進步。僅以此文奉獻給所有關係技術的朋友,希望可以拋磚引玉,促進我們共同的技術進步! 

第一部分 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客戶端的接口,描述如下所示: 

class IMediaPlayerClient: public IInterface 
{ 
public: 
DECLARE_META_INTERFACE(MediaPlayerClient); 
virtual void notify(int msg, int ext1, int ext2) = 0; 
}; 

class BnMediaPlayerClient: public BnInterface 
{ 
public: 
virtual status_t onTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
}; 
在 定義中,IMediaPlayerClient類繼承IInterface,並定義了一個MediaPlayer客戶端的接 口,BnMediaPlayerClient繼承了BnInterface,這是爲基於Android的基礎類Binder機制實現在進程通訊而構建的。 事實上,根據BnInterface類模版的定義BnInterface類相當於雙繼承了BnInterface和 ImediaPlayerClient。這是Android一種常用的定義方式。 

2.3 頭文件mediaplayer.h 
mediaplayer.h是對外的接口類,它最主要是定義了一個MediaPlayer類: 
class MediaPlayer : public BnMediaPlayerClient 
{ 
public: 
MediaPlayer(); 
~MediaPlayer(); 
void onFirstRef(); 
void disconnect(); 
status_t setDataSource(const char *url); 
status_t setDataSource(int fd, int64_t offset, int64_t length); 
status_t setVideoSurface(const sp& surface); 
status_t setListener(const sp& listener); 
status_t prepare(); 
status_t prepareAsync(); 
status_t start(); 
status_t stop(); 
status_t pause(); 
bool isPlaying(); 
status_t getVideoWidth(int *w); 
status_t getVideoHeight(int *h); 
status_t seekTo(int msec); 
status_t getCurrentPosition(int *msec); 
status_t getDuration(int *msec); 
status_t reset(); 
status_t setAudioStreamType(int type); 
status_t setLooping(int loop); 
status_t setVolume(float leftVolume, float rightVolume); 
void notify(int msg, int ext1, int ext2); 
static sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels); 
static sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels); 
//…… 
} 
從接口中可以看出MediaPlayer類剛好實現了一個MediaPlayer的基本操作,例如播放(start)、停止(stop)、暫停(pause)等。 
另外的一個類DeathNotifier在MediaPlayer類中定義,它繼承了IBinder類中的DeathRecipient類: 
class DeathNotifier: public IBinder:: DeathRecipient 
{ 
public: 
DeathNotifier() {} 
virtual ~DeathNotifier(); 
virtual void binderDied(const wp& who); 
}; 
事實上,MediaPlayer類正是間接地繼承了IBinder,而MediaPlayer:: DeathNotifier類繼承了IBinder:: DeathRecipient,這都是爲了實現進程間通訊而構建的。 

2.4 頭文件IMediaPlayer.h 
IMediaPlayer.h主要的的內容是一個實現MediaPlayer功能的接口,它的主要定義如下所示: 
class IMediaPlayer: public IInterface 
{ 
public: 
DECLARE_META_INTERFACE(MediaPlayer); 
virtual void disconnect() = 0; 
virtual status_t setVideoSurface(const sp& surface) = 0; 
virtual status_t prepareAsync() = 0; 
virtual status_t start() = 0; 
virtual status_t stop() = 0; 
virtual status_t pause() = 0; 
virtual status_t isPlaying(bool* state) = 0; 
virtual status_t getVideoSize(int* w, int* h) = 0; 
virtual status_t seekTo(int msec) = 0; 
virtual status_t getCurrentPosition(int* msec) = 0; 
virtual status_t getDuration(int* msec) = 0; 
virtual status_t reset() = 0; 
virtual status_t setAudioStreamType(int type) = 0; 
virtual status_t setLooping(int loop) = 0; 
virtual status_t setVolume(float leftVolume, float rightVolume) = 0; 
}; 
class BnMediaPlayer: public BnInterface 
{ 
public: 
virtual status_t onTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
}; 
在 IMediaPlayer類中,主要定義MediaPlayer的功能接口,這個類必須被繼承才能夠使用。值得注意的是,這些接口和 MediaPlayer類的接口有些類似,但是它們並沒有直接的關係。事實上,在MediaPlayer類的各種實現中,一般都會通過調用 IMediaPlayer類的實現類來完成。

2.5 頭文件IMediaPlayerService.h 
IMediaPlayerService.h用於描述一個MediaPlayer的服務,定義方式如下所示: 
class IMediaPlayerService: public IInterface 
{ 
public: 
DECLARE_META_INTERFACE(MediaPlayerService); 
virtual sp create(pid_t pid, const sp& client, const char* url) = 0; 
virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) = 0; 
virtual sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0; 
virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0; 
}; 
class BnMediaPlayerService: public BnInterface 
{ 
public: 
virtual status_t onTransact( uint32_t code, 
const Parcel& data, 
Parcel* reply, 
uint32_t flags = 0); 
}; 
由 於具有純虛函數,IMediaPlayerService 以及BnMediaPlayerService必須被繼承實現才能夠使用,在IMediaPlayerService定義的create和decode等 接口,事實上是必須被繼承者實現的內容。注意,create的返回值的類型是sp,這個IMediaPlayer正是提供實現功能的接口。 

第三部分 MediaPlayer的主要實現分析3.1 JAVA程序部分 

在packages/apps/Music/src/com/android/music/目錄的MediaPlaybackService.java文件中,包含了對MediaPlayer的調用。 
在MediaPlaybackService.java中包含對包的引用: 
import android.media.MediaPlayer; 
在MediaPlaybackService類的內部,定義了MultiPlayer類: 
private class MultiPlayer { 
private MediaPlayer mMediaPlayer = new MediaPlayer(); 
} 
MultiPlayer類中使用了MediaPlayer類,其中有一些對這個MediaPlayer的調用,調用的過程如下所示: 
mMediaPlayer.reset(); 
mMediaPlayer.setDataSource(path); 
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
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,如下所示: 
static JNINativeMethod gMethods[] = { 

{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, 

{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, 
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, 
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, 
{"_start", "()V", (void *)android_media_MediaPlayer_start}, 
{"_stop", "()V", (void *)android_media_MediaPlayer_stop}, 
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, 
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, 
{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, 
{"_pause", "()V", (void *)android_media_MediaPlayer_pause}, 
{"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, 
{"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition}, 
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration}, 
{"_release", "()V", (void *)android_media_MediaPlayer_release}, 
{"_reset", "()V", (void *)android_media_MediaPlayer_reset}, 
{"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType}, 
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping}, 
{"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, 
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt}, 
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, 
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, 
} 
JNINativeMethod的第一個成員是一個字符串,表示了JAVA本地調用方法的名稱,這個名稱是在JAVA程序中調用的名稱;第二個成員也是一個字符串,表示JAVA本地調用方法的參數和返回值;第三個成員是JAVA本地調用方法對應的C語言函數。 
其中android_media_MediaPlayer_reset函數的實現如下所示: 
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) 
{ 
sp mp = getMediaPlayer(env, thiz); 
if (mp == NULL ) { 
jniThrowException(env, "java/lang/IllegalStateException", NULL); 
return; 
} 
process_media_player_call( env, thiz, mp->reset(), NULL, NULL ); 
} 
在android_media_MediaPlayer_reset的調用中,得到一個MediaPlayer指針,通過對它的調用實現實際的功能。 
register_android_media_MediaPlayer用於將gMethods註冊爲的類"android/media/MediaPlayer",其實現如下所示。 
static int register_android_media_MediaPlayer(JNIEnv *env) 
{ 
jclass clazz; 
clazz = env->FindClass("android/media/MediaPlayer"); 
// ...... 
return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods)); 
} 
"android/media/MediaPlayer"對應JAVA的類android.media.MediaPlayer。 

3.3 mediaplayer的核心庫libmedia.so 
libs/media/mediaplayer.cpp文件用於實現mediaplayer.h提供的接口,其中一個重要的片段如下所示: 
const sp& MediaPlayer::getMediaPlayerService() 
{ 
Mutex::Autolock _l(mServiceLock); 
if (mMediaPlayerService.get() == 0) { 
sp sm = defaultServiceManager(); 
sp binder; 
do { 
binder = sm->getService(String16("media.player")); 
if (binder != 0) 
break; 
LOGW("MediaPlayerService not published, waiting..."); 
usleep(500000); // 0.5 s 
} while(true); 
if (mDeathNotifier == NULL) { 
mDeathNotifier = new DeathNotifier(); 
} 
binder->linkToDeath(mDeathNotifier); 
mMediaPlayerService = interface_cast(binder); 
} 
LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?"); 
return mMediaPlayerService; 
} 
其 中最重要的一點是binder = sm->getService(String16("media.player"));這個調用用來得到一個名稱爲"media.player"的 服務,這個調用返回值的類型爲IBinder,根據實現將其轉換成類型IMediaPlayerService使用。 
一個具體的函數setDataSource如下所示: 
status_t MediaPlayer::setDataSource(const char *url) 
{ 
LOGV("setDataSource(%s)", url); 
status_t err = UNKNOWN_ERROR; 
if (url != NULL) { 
const sp& service(getMediaPlayerService()); 
if (service != 0) { 
sp player(service->create(getpid(), this, url)); 
err = setDataSource(player); 
} 
} 
return err; 
} 
在函數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 
class BpMediaPlayerClient: public BpInterface 
{ 
public: 
BpMediaPlayerClient(const sp& impl) 
: BpInterface(impl){} 
virtual void notify(int msg, int ext1, int ext2) 
{ 
Parcel data, reply; 
data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor()); 
data.writeInt32(msg); 
data.writeInt32(ext1); 
data.writeInt32(ext2); 
remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY); 
} 
}; 
還需要實現定義宏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。 
class MediaPlayerService : public BnMediaPlayerService 
{ 
class Client : public BnMediaPlayer 
} 
在MediaPlayerService中具有如下一個靜態函數instantiate: 
void MediaPlayerService::instantiate() { 
defaultServiceManager()->addService( 
String16("media.player"), new MediaPlayerService()); 
} 
在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函數如下所示: 
static sp createPlayer(player_type playerType, void* cookie, 
notify_callback_f notifyFunc) 
{ 
sp p; 
switch (playerType) { 
case PV_PLAYER: 
LOGV(" create PVPlayer"); 
p = new PVPlayer(); 
break; 
case SONIVOX_PLAYER: 
LOGV(" create MidiFile"); 
p = new MidiFile(); 
break; 
case VORBIS_PLAYER: 
LOGV(" create VorbisPlayer"); 
p = new VorbisPlayer(); 
break; 
} 
//…… 
return p; 
} 
在 這裏根據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 的函數實現具體的功能。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章