關(guān)鍵字: android的mediaplayer架構(gòu)介紹
轉(zhuǎn)載自hanchao3c Android開發(fā)者論壇原創(chuàng))
本文由中國的Androidin社區(qū)的hanchao3c原創(chuàng),主要介紹的是Android中很重要也最為復(fù)雜的媒體播放器(MediaPlayer)部分的架構(gòu)。對于Android這樣一個完整又相對復(fù)雜的系統(tǒng),一個MediaPlayer功能的實現(xiàn)不在其具體的功能,而是具體功能如何適應(yīng)Android系統(tǒng)Android MediaPlayer的主要具體實現(xiàn)在OpenCore的Player中,這部分不是本文的關(guān)注點。本文關(guān)注的是MediaPlayer系統(tǒng)的架構(gòu),其他的一些Android的應(yīng)用程序也使用類似的架構(gòu)。
對于開源事業(yè)在中國的發(fā)展,hanchao3c認為應(yīng)該共享的不僅僅是代碼,文檔、設(shè)計思想、理念甚至對于技術(shù)的理解都應(yīng)該得到充分的共享。Android為中國人進入大規(guī)模的開源項目提供了很好的機遇,對于走在技術(shù)前沿的人們,不應(yīng)將技術(shù)視為私有財產(chǎn),而應(yīng)該將自己理解更好地奉獻給大眾,提高大眾的學(xué)習(xí)速度,從中也可以得到反饋,從而促進自己的進步。僅以此文奉獻給所有關(guān)系技術(shù)的朋友,希望可以拋磚引玉,促進我們共同的技術(shù)進步!
第一部分 MediaPlayer概述
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video兩個應(yīng)用程序都是調(diào)用MediaPlayer實現(xiàn)的。
MediaPlayer在底層是基于OpenCore(PacketVideo)的庫實現(xiàn)的,為了構(gòu)建一個MediaPlayer程序,上層還包含了進程間通訊等內(nèi)容,這種進程間通訊的基礎(chǔ)是Android基本庫中的Binder機制。
以開源的Android為例MediaPlayer的代碼主要在以下的目錄中:
JAVA程序的路徑:
packages/apps/Music/src/com/android/music/
JAVA本地調(diào)用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
這部分內(nèi)容編譯成為目標(biāo)是libmedia_jni.so。
主要的頭文件在以下的目錄中:
frameworks/base/include/media/
多媒體底層庫在以下的目錄中:
frameworks/base/media/libmedia/
這部分的內(nèi)容被編譯成庫libmedia.so。
多媒體服務(wù)部分:
frameworks/base/media/libmediaplayerservice/
文件為mediaplayerservice.h和mediaplayerservice.cpp
這部分內(nèi)容被編譯成庫libmediaplayerservice.so。
基于OpenCore的多媒體播放器部分
external/opencore/
這部分內(nèi)容被編譯成庫libopencoreplayer.so。
從程序規(guī)模上來看,libopencoreplayer.so是主要的實現(xiàn)部分,而其他的庫基本上都是在其上建立的封裝和為建立進程間通訊的機制。
第二部分 MediaPlayer的接口與架構(gòu)
(hanchao3c Android開發(fā)者論壇原創(chuàng),轉(zhuǎn)載請注明)
2.1 整體框架圖
MediaPlayer的各個庫之間的結(jié)構(gòu)比較復(fù)雜,可以用下圖的表示:
1.png (62.54 KB)
2008-11-22 00:11
在各個庫中,libmedia.so位于核心的位置,它對上層的提供的接口主要是MediaPlayer類,類libmedia_jni.so通過調(diào)用MediaPlayer類提供對JAVA的接口,并且實現(xiàn)了android.media.MediaPlayer類。
libmediaplayerservice.so是Media的服務(wù)器,它通過繼承l(wèi)ibmedia.so的類實現(xiàn)服務(wù)器的功能,而libmedia.so中的另外一部分內(nèi)容則通過進程間通訊和libmediaplayerservice.so進行通訊。libmediaplayerservice.so的真正功能通過調(diào)用OpenCore Player來完成。
MediaPlayer部分的頭文件在frameworks/base/include/media/目錄中,這個目錄是和libmedia.so庫源文件的目錄frameworks/base/media/libmedia/相對應(yīng)的。主要的頭文件有以下幾個:
IMediaPlayerClient.h
mediaplayer.h
IMediaPlayer.h
IMediaPlayerService.h
MediaPlayerInterface.h
在這些頭文件mediaplayer.h提供了對上層的接口,而其他的幾個頭文件都是提供一些接口類(即包含了純虛函數(shù)的類),這些接口類必須被實現(xiàn)類繼承才能夠使用。
整個MediaPlayer庫和調(diào)用的關(guān)系如下圖所示:
2.png (46.74 KB)
2008-11-22 00:11
整個MediaPlayer在運行的時候,可以大致上分成Client和Server兩個部分,它們分別在兩個進程中運行,它們之間使用Binder機制實現(xiàn)IPC通訊。從框架結(jié)構(gòu)上來看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三個類定義了MeidaPlayer的接口和架構(gòu),MediaPlayerService.cpp和mediaplayer.coo兩個文件用于MeidaPlayer架構(gòu)的實現(xiàn),MeidaPlayer的具體功能在PVPlayer(庫libopencoreplayer.so)中的實現(xiàn)。
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<IMediaPlayerClient>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在定義中,IMediaPlayerClient類繼承IInterface,并定義了一個MediaPlayer客戶端的接口,BnMediaPlayerClient繼承了BnInterface<IMediaPlayerClient>,這是為基于Android的基礎(chǔ)類Binder機制實現(xiàn)在進程通訊而構(gòu)建的。事實上,根據(jù)BnInterface類模版的定義BnInterface<IMediaPlayerClient>類相當(dāng)于雙繼承了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>& surface);
status_t setListener(const sp<MediaPlayerListener>& 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<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels);
static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);
//……
}
從接口中可以看出MediaPlayer類剛好實現(xiàn)了一個MediaPlayer的基本操作,例如播放(start)、停止(stop)、暫停(pause)等。
另外的一個類DeathNotifier在MediaPlayer類中定義,它繼承了IBinder類中的DeathRecipient類:
class DeathNotifier: public IBinder:: DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp<IBinder>& who);
};
事實上,MediaPlayer類正是間接地繼承了IBinder,而MediaPlayer:: DeathNotifier類繼承了IBinder:: DeathRecipient,這都是為了實現(xiàn)進程間通訊而構(gòu)建的。
2.4 頭文件IMediaPlayer.h
IMediaPlayer.h主要的的內(nèi)容是一個實現(xiàn)MediaPlayer功能的接口,它的主要定義如下所示:
class IMediaPlayer: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayer);
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp<ISurface>& 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<IMediaPlayer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在IMediaPlayer類中,主要定義MediaPlayer的功能接口,這個類必須被繼承才能夠使用。值得注意的是,這些接口和MediaPlayer類的接口有些類似,但是它們并沒有直接的關(guān)系。事實上,在MediaPlayer類的各種實現(xiàn)中,一般都會通過調(diào)用IMediaPlayer類的實現(xiàn)類來完成。
2.5 頭文件IMediaPlayerService.h
IMediaPlayerService.h用于描述一個MediaPlayer的服務(wù),定義方式如下所示:
class IMediaPlayerService: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
由于具有純虛函數(shù),IMediaPlayerService 以及BnMediaPlayerService必須被繼承實現(xiàn)才能夠使用,在IMediaPlayerService定義的create和decode等接口,事實上是必須被繼承者實現(xiàn)的內(nèi)容。注意,create的返回值的類型是sp<IMediaPlayer>,這個IMediaPlayer正是提供實現(xiàn)功能的接口。
第三部分 MediaPlayer的主要實現(xiàn)分析
(hanchao3c Android開發(fā)者論壇原創(chuàng),轉(zhuǎn)載請注明)
3.1 JAVA程序部分
在packages/apps/Music/src/com/android/music/目錄的MediaPlaybackService.java文件中,包含了對MediaPlayer的調(diào)用。
在MediaPlaybackService.java中包含對包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService類的內(nèi)部,定義了MultiPlayer類:
private class MultiPlayer {
private MediaPlayer mMediaPlayer = new MediaPlayer();
}
MultiPlayer類中使用了MediaPlayer類,其中有一些對這個MediaPlayer的調(diào)用,調(diào)用的過程如下所示:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(path);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
reset、setDataSource和setAudioStreamType等接口就是通過JAVA本地調(diào)用(JNI)來實現(xiàn)的。
3.2 MediaPlayer的JAVA本地調(diào)用部分
MediaPlayer的JAVA本地調(diào)用部分在目錄frameworks/base/media/jni/的android_media_MediaPlayer.cpp中的文件中實現(xiàn)。
android_media_MediaPlayer.cpp之中定義了一個JNINativeMethod(JAVA本地調(diào)用方法)類型的數(shù)組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本地調(diào)用方法的名稱,這個名稱是在JAVA程序中調(diào)用的名稱;第二個成員也是一個字符串,表示JAVA本地調(diào)用方法的參數(shù)和返回值;第三個成員是JAVA本地調(diào)用方法對應(yīng)的C語言函數(shù)。
其中android_media_MediaPlayer_reset函數(shù)的實現(xiàn)如下所示:
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> 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的調(diào)用中,得到一個MediaPlayer指針,通過對它的調(diào)用實現(xiàn)實際的功能。
register_android_media_MediaPlayer用于將gMethods注冊為的類"android/media/MediaPlayer",其實現(xiàn)如下所示。
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"對應(yīng)JAVA的類android.media.MediaPlayer。
(hanchao3c Android開發(fā)者論壇原創(chuàng),轉(zhuǎn)載請注明)
3.3 mediaplayer的核心庫libmedia.so
libs/media/mediaplayer.cpp文件用于實現(xiàn)mediaplayer.h提供的接口,其中一個重要的片段如下所示:
const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
{
Mutex::Autolock _l(mServiceLock);
if (mMediaPlayerService.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> 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<IMediaPlayerService>(binder);
}
LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
return mMediaPlayerService;
}
其中最重要的一點是binder = sm->getService(String16("media.player"));這個調(diào)用用來得到一個名稱為"media.player"的服務(wù),這個調(diào)用返回值的類型為IBinder,根據(jù)實現(xiàn)將其轉(zhuǎn)換成類型IMediaPlayerService使用。
一個具體的函數(shù)setDataSource如下所示:
status_t MediaPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
status_t err = UNKNOWN_ERROR;
if (url != NULL) {
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, url));
err = setDataSource(player);
}
}
return err;
}
在函數(shù)setDataSource函數(shù)中,調(diào)用getMediaPlayerService得到了一個IMediaPlayerService,又從IMediaPlayerService中得到了IMediaPlayer類型的指針,通過這個指針進行著具體的操作。
其他一些函數(shù)的實現(xiàn)也與setDataSource類似。
libmedia.so中的其他一些文件與頭文件的名稱相同,它們是:
libs/media/IMediaPlayerClient.cpp
libs/media/IMediaPlayer.cpp
libs/media/IMediaPlayerService.cpp
為了實現(xiàn)Binder的具體功能,在這些類中還需要實現(xiàn)一個BpXXX的類,例如IMediaPlayerClient.cpp的實現(xiàn)如下所示:l
class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
{
public:
BpMediaPlayerClient(const sp<IBinder>& impl)
: BpInterface<IMediaPlayerClient>(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);
}
};
還需要實現(xiàn)定義宏IMPLEMENT_META_INTERFACE,這個宏將被展開,生成幾個函數(shù):
IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");
以上的實現(xiàn)都是基于Binder框架的實現(xiàn)方式,只需要按照模版實現(xiàn)即可。其中BpXXX的類為代理類(proxy),BnXXX的類為本地類(native)。代理類的transact函數(shù)和本地類的onTransact函數(shù)實現(xiàn)對應(yīng)的通訊。
3.4 media服務(wù)libmediaservice.so
frameworks/base/media\libmediaplayerservice目錄中的MediaPlayerService.h和MediaPlayerService.cpp用于實現(xiàn)一個
servers/media/的服務(wù),MediaPlayerService是繼承BnMediaPlayerService的實現(xiàn),在這個類的內(nèi)部又定義了類Client,MediaPlayerService::Client繼承了BnMediaPlayer。
class MediaPlayerService : public BnMediaPlayerService
{
class Client : public BnMediaPlayer
}
在MediaPlayerService中具有如下一個靜態(tài)函數(shù)instantiate:
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
在instantiate函數(shù)中,調(diào)用IServiceManager的一個函數(shù)addService,向其中增加了一個名為"media.player"的服務(wù)。
這個名為"media.player"的服務(wù)和mediaplayer.cpp中調(diào)用getService中得到的使用一樣名稱。因此,在這里調(diào)用addService增加服務(wù)在mediaplayer.cpp中可以按照名稱"media.player"來使用。這就是使用Binder實現(xiàn)進程間通訊的(IPC)的作用,事實上這個MediaPlayerService類是在服務(wù)中運行的,而mediaplayer.cpp調(diào)用的功能在應(yīng)用中運行,二者并不是一個進程。但是在mediaplayer.cpp卻像一個進程的調(diào)用一樣調(diào)用MediaPlayerService的功能。
在MediaPlayerService.cpp中的createPlayer函數(shù)如下所示:
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
notify_callback_f notifyFunc)
{
sp<MediaPlayerBase> 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;
}
在這里根據(jù)playerType的類型建立不同的播放器:對于大多數(shù)情況,類型將是PV_PLAYER,這時會調(diào)用了new PVPlayer()建立一個PVPlayer,然后將其指針轉(zhuǎn)換成MediaPlayerBase來使用;對于Mini文件的情況,類型為SONIVOX_PLAYER,將會建立一個MidiFile;對于Ogg Vorbis格式的情況,將會建立一個VorbisPlayer。
(OGG Vobis是一種音頻壓縮格式,與MP3等的音樂格式類似,它具有完全免費、開放和沒有專利限制的特點。)
值得注意的是PVPlayer、MidiFile和VorbisPlayer三個類都是繼承MediaPlayerInterface得到的,而MediaPlayerInterface又是繼承MediaPlayerBase得到的,因此三者具有相同接口類型。只有建立的時候會調(diào)用各自的構(gòu)造函數(shù),在建立之后,將只通過MediaPlayerBase接口來MediaPlayerBase控制它們。
在frameworks/base/media/libmediaplayerservice目錄中,MidiFile.h和MidiFile.cpp的實現(xiàn)MidiFile,VorbisPlayer.h和VorbisPlayer.cpp實現(xiàn)一個VorbisPlayer。
3.5 OpenCorePlayer的實現(xiàn)libopencoreplayer.so
OpenCore Player在external/opencore/中實現(xiàn),這個實現(xiàn)是一個基于OpenCore的Player的實現(xiàn)。具體實現(xiàn)的文件為playerdriver.cpp。其中實現(xiàn)了兩個類:PlayerDriver和PVPlayer。PVPlayer通過調(diào)用PlayerDriver的函數(shù)實現(xiàn)具體的功能。