Android的MediaPlayer架構介紹

來源:互聯網
上載者:User
    本文主要介紹的是Android中很重要也最為複雜的媒體播放器(MediaPlayer)部分的架構。對於Android這樣一個完整又相對複雜的系統,一個MediaPlayer功能的實現不在其具體的功能,而是具體功能如何適應Android系統Android MediaPlayer的主要具體實現在OpenCore的Player中,這部分不是本文的關注點。本文關注的是MediaPlayer系統的架構,其他的一些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<br />{<br />public:<br /> DECLARE_META_INTERFACE(MediaPlayerClient);<br /> virtual void notify(int msg, int ext1, int ext2) = 0;<br />};</p><p>class BnMediaPlayerClient: public BnInterface<IMediaPlayerClient><br />{<br />public:<br /> virtual status_t onTransact( uint32_t code,<br /> const Parcel& data,<br /> Parcel* reply,<br /> uint32_t flags = 0);<br />};    在定義中,IMediaPlayerClient類繼承IInterface,並定義了一個MediaPlayer用戶端的介面,BnMediaPlayerClient繼承了BnInterface<IMediaPlayerClient>,這是為基於Android的基礎類Binder機制實現在進程通訊而構建的。事實上,根據BnInterface類模版的定義BnInterface<IMediaPlayerClient>類相當於雙繼承了BnInterface和ImediaPlayerClient。這是Android一種常用的定義方式。

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

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

2.5 標頭檔IMediaPlayerService.h
    IMediaPlayerService.h用於描述一個MediaPlayer的服務,定義方式如下所示:
class IMediaPlayerService: public IInterface<br />{<br />public:<br /> DECLARE_META_INTERFACE(MediaPlayerService);<br /> virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;<br /> virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;<br /> virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;<br /> virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;<br />};<br />class BnMediaPlayerService: public BnInterface<IMediaPlayerService><br />{<br />public:<br /> virtual status_t onTransact( uint32_t code,<br /> const Parcel& data,<br /> Parcel* reply,<br /> uint32_t flags = 0);<br />};<br />    由於具有純虛函數,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類:
private class MultiPlayer {<br /> private MediaPlayer mMediaPlayer = new MediaPlayer();<br /> }    MultiPlayer類中使用了MediaPlayer類,其中有一些對這個MediaPlayer的調用,調用的過程如下所示:
mMediaPlayer.reset();<br /> mMediaPlayer.setDataSource(path);<br /> 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[] = {<br /> {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},<br /> {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},<br /> {"prepare", "()V", (void *)android_media_MediaPlayer_prepare},<br /> {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},<br /> {"_start", "()V", (void *)android_media_MediaPlayer_start},<br /> {"_stop", "()V", (void *)android_media_MediaPlayer_stop},<br /> {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},<br /> {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},<br /> {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},<br /> {"_pause", "()V", (void *)android_media_MediaPlayer_pause},<br /> {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},<br /> {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},<br /> {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},<br /> {"_release", "()V", (void *)android_media_MediaPlayer_release},<br /> {"_reset", "()V", (void *)android_media_MediaPlayer_reset},<br /> {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},<br /> {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},<br /> {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},<br /> {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},<br /> {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},<br /> {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},<br />}    JNINativeMethod的第一個成員是一個字串,表示了JAVA本地調用方法的名稱,這個名稱是在JAVA程式中調用的名稱;第二個成員也是一個字串,表示JAVA本地調用方法的參數和傳回值;第三個成員是JAVA本地調用方法對應的C語言函數。
    其中android_media_MediaPlayer_reset函數的實現如下所示:
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)<br />{<br /> sp<MediaPlayer> mp = getMediaPlayer(env, thiz);<br /> if (mp == NULL ) {<br /> jniThrowException(env, "java/lang/IllegalStateException", NULL);<br /> return;<br /> }<br /> process_media_player_call( env, thiz, mp->reset(), NULL, NULL );<br />}    在android_media_MediaPlayer_reset的調用中,得到一個MediaPlayer指標,通過對它的調用實現實際的功能。
    register_android_media_MediaPlayer用於將gMethods註冊為的類"android/media/MediaPlayer",其實現如下所示。
static int register_android_media_MediaPlayer(JNIEnv *env)<br /> {<br /> jclass clazz;<br /> clazz = env->FindClass("android/media/MediaPlayer");<br /> // ......<br /> return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods));<br /> }    "android/media/MediaPlayer"對應JAVA的類android.media.MediaPlayer。
 
3.3 mediaplayer的核心庫libmedia.so
    libs/media/mediaplayer.cpp檔案用於實現mediaplayer.h提供的介面,其中一個重要的片段如下所示:
const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()<br />{<br /> Mutex::Autolock _l(mServiceLock);<br /> if (mMediaPlayerService.get() == 0) {<br /> sp<IServiceManager> sm = defaultServiceManager();<br /> sp<IBinder> binder;<br /> do {<br /> binder = sm->getService(String16("media.player"));<br /> if (binder != 0)<br /> break;<br /> LOGW("MediaPlayerService not published, waiting...");<br /> usleep(500000); // 0.5 s<br /> } while(true);<br /> if (mDeathNotifier == NULL) {<br /> mDeathNotifier = new DeathNotifier();<br /> }<br /> binder->linkToDeath(mDeathNotifier);<br /> mMediaPlayerService = interface_cast<IMediaPlayerService>(binder);<br /> }<br /> LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");<br /> return mMediaPlayerService;<br />}    其中最重要的一點是binder = sm->getService(String16("media.player"));這個調用用來得到一個名稱為"media.player"的服務,這個調用傳回值的類型為IBinder,根據實現將其轉換成類型IMediaPlayerService使用。
    一個具體的函數setDataSource如下所示:
status_t MediaPlayer::setDataSource(const char *url)<br />{<br /> LOGV("setDataSource(%s)", url);<br /> status_t err = UNKNOWN_ERROR;<br /> if (url != NULL) {<br /> const sp<IMediaPlayerService>& service(getMediaPlayerService());<br /> if (service != 0) {<br /> sp<IMediaPlayer> player(service->create(getpid(), this, url));<br /> err = setDataSource(player);<br /> }<br /> }<br /> return err;<br />}    在函數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<IMediaPlayerClient><br />{<br />public:<br /> BpMediaPlayerClient(const sp<IBinder>& impl)<br /> : BpInterface<IMediaPlayerClient>(impl){}<br /> virtual void notify(int msg, int ext1, int ext2)<br /> {<br /> Parcel data, reply;<br /> data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());<br /> data.writeInt32(msg);<br /> data.writeInt32(ext1);<br /> data.writeInt32(ext2);<br /> remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);<br /> }<br />};    還需要實現定義宏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<br /> {<br /> class Client : public BnMediaPlayer<br /> }    在MediaPlayerService中具有如下一個靜態函數instantiate:
void MediaPlayerService::instantiate() {<br /> defaultServiceManager()->addService(<br /> String16("media.player"), new MediaPlayerService());<br /> }    在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<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc)<br />{<br /> sp<MediaPlayerBase> p;<br /> switch (playerType) {<br /> case PV_PLAYER:<br /> LOGV(" create PVPlayer");<br /> p = new PVPlayer();<br /> break;<br /> case SONIVOX_PLAYER:<br /> LOGV(" create MidiFile");<br /> p = new MidiFile();<br /> break;<br /> case VORBIS_PLAYER:<br /> LOGV(" create VorbisPlayer");<br /> p = new VorbisPlayer();<br /> break;<br /> }<br /> //……<br /> return p;<br />}    在這雷根據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的函數實現具體的功能。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.