多媒体框架
多媒體播放框架
多媒體核心框架涉及到的代碼文件有: java層: frameworks/base/media/java/android/media/MediaPlayer.javajni層: frameworks/base/media/jni/android_media_MediaPlayer.cpp
native層: frameworks/av/media/libmedia/Mediaplayer.cpp frameworks/av/include/media/Mediaplayer.h frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp frameworks/av/media/libmediaplayerservice/libmediaplayerservice/MediaPlayerService.h frameworks/av/media/libmediaplayerservice/libmediaplayerservice/MediaPlayerFactory.cpp MediaPlayerFactory.cpp以下就是具體的播放器的實(shí)現(xiàn)了。
媒體播放的抽象層:
frameworks/av/include/media/MediaPlayerInterface.h 以上文件是播放器的abstract base class,類似于硬件抽象層(HAL),是每個(gè)具體的播放器都需要根據(jù)這個(gè)基類來實(shí)現(xiàn)對(duì)應(yīng)的MediaPlayerInterface。多媒體播放的核心框架主要涉及到兩條典型的數(shù)據(jù)流: 一個(gè)是命令+命令的響應(yīng)(cmd+respond) 一個(gè)是事件(unsolicited) ? ? ?但凡用戶跟后臺(tái)服務(wù)一般都有如上的兩個(gè)接口,如phone,wifi等核心模塊,莫不是遵循以上的結(jié)構(gòu),只不過實(shí)現(xiàn)方式有差異。在這里命令和事件通道都是通過ibinder來實(shí)現(xiàn),而phone和wifi模塊的命令和事件通道都是通過socket通訊機(jī)制來實(shí)現(xiàn)的。那么為什么phone和wifi不通過ibinder來實(shí)現(xiàn)呢?因?yàn)閜hone的后臺(tái)服務(wù)pppd以及wifi的后臺(tái)服務(wù)wpa_supplicant,他們是linux的原生態(tài)代碼,這些代碼是在android誕生之前就有的,所以當(dāng)他們提供的通訊機(jī)制就是使用的socket,那android本著最少修改的原則,當(dāng)然就是還是原用了他本身自帶的socket通訊來實(shí)現(xiàn)的;而多媒體服務(wù)這塊,則是android自己重新規(guī)劃實(shí)現(xiàn)的,所以他自然就使用他們自己的ibinder通訊機(jī)制。在很多場(chǎng)景下,android在條件合適的情況下,一般都會(huì)優(yōu)先使用ibinder通訊,因?yàn)閕binder通訊相比socket通訊效率更高,安全性更好,這個(gè)后面單獨(dú)開blog來論述。 在講native層的代碼框架前,先上過類圖:
frameworks/av/include/media/IMediaPlayer.h定義了ImediaPlayer的binder接口 frameworks/av/include/media/IMediaPlayerService.h定義了IMediaPlayerService的binder接口 frameworks/av/include/media/IMediaPlayerClient.h定義了IMediaPlayerClient的binder接口
上圖中的黃色標(biāo)識(shí)部分就是多媒體框架中的后臺(tái)服務(wù)(media.player service),他運(yùn)行于一個(gè)獨(dú)立的進(jìn)程中,他既包含了binder的服務(wù)器端(BnMediaPlayerService,BnMediaPlayer),也包含了binder的客服端(BpMediaPlayerClient);而藍(lán)色標(biāo)識(shí)的部分,則是媒體播放器的客服端,即player user process,他同樣運(yùn)行于用戶所在的進(jìn)程,客服端同樣也包含binder的服務(wù)器端(BnMediaPlayerClient),也包含了binder的客服端(BpMediaPlayer,BpMediaPlayerService) 另外media.player service進(jìn)程包含了兩個(gè)binder實(shí)例,即兩個(gè)binder服務(wù)器端(BnMediaPlayerService,BnMediaPlayer),他們?cè)谕粋€(gè)進(jìn)程里。那當(dāng)一個(gè)remote ipc過來時(shí),他們?cè)趺磪^(qū)分出來該調(diào)用是該路由給BnMediaPlayerService還是路由給BnMediaPlayer呢?該問題同樣在我的另外的blog中來闡述。在這里只是提出這個(gè)問題,為下一篇blog做些知識(shí)上的鋪墊。
從binder的分類來講: IMediaPlayerService對(duì)應(yīng)的binder是注冊(cè)到service manager中去的實(shí)名binder,見如下代碼段: void MediaPlayerService::instantiate() {defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService()); }
而ImediaPlayer和IMediaPlayerClient對(duì)應(yīng)的binder都是匿名binder,在這里先說這些binder的作用。IMediaPlayerService?binder其實(shí)只是起個(gè)橋梁的作用,目的是為了獲取兩個(gè)匿名binder:ImediaPlayer和IMediaPlayerClient。而ImediaPlayer binder對(duì)應(yīng)前面講個(gè)的用于應(yīng)用通過這個(gè)binder向media service后臺(tái)服務(wù)發(fā)送命令和接受命令的響應(yīng);而IMediaPlayerClient則是被media service后臺(tái)服務(wù)用來向應(yīng)用返回媒體播放事件的。 IMediaPlayerService實(shí)名binder的獲取方式: // establish binder interface to MediaPlayerService /*static*/const sp<IMediaPlayerService>& IMediaDeathNotifier::getMediaPlayerService() {ALOGV("getMediaPlayerService");Mutex::Autolock _l(sServiceLock);if (sMediaPlayerService == 0) {sp<IServiceManager> sm = defaultServiceManager();//實(shí)名服務(wù)的管理者sp<IBinder> binder;do {binder = sm->getService(String16("media.player"));//提供實(shí)名服務(wù)對(duì)應(yīng)的名字,返回的是binder實(shí)例在客服端進(jìn)程中的引用句柄號(hào)。if (binder != 0) {break;}ALOGW("Media player service not published, waiting...");usleep(500000); // 0.5 s} while (true);if (sDeathNotifier == NULL) {sDeathNotifier = new DeathNotifier();}binder->linkToDeath(sDeathNotifier);sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);//將Bpbinder cast to BpMediaPlayerService}ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");return sMediaPlayerService; }而匿名binder是怎么獲取的呢? 分兩部分講,首先說IMediaPlayerClient?binder是如果告訴后臺(tái)服務(wù)的: virtual sp<IMediaPlayer> create(//frameworks/av/media/libmedia/IMediaPlayerService.cppconst sp<IMediaPlayerClient>& client, int audioSessionId) {Parcel data, reply;data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());data.writeStrongBinder(client->asBinder());//將IMediaPlayerClient?binder實(shí)體傳遞到binder驅(qū)動(dòng)中data.writeInt32(audioSessionId);remote()->transact(CREATE, data, &reply);return interface_cast<IMediaPlayer>(reply.readStrongBinder());//獲取ImediaPlayer binder實(shí)體在當(dāng)前進(jìn)程中的引用句柄,并將它封裝在BpMediaPlayer實(shí)例中。}
而服務(wù)器端是如何將ImediaPlayer binder實(shí)體傳遞到應(yīng)用段所在的進(jìn)程的呢? status_t BnMediaPlayerService::onTransact(//frameworks/av/media/libmedia/IMediaPlayerService.cppuint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {switch (code) {case CREATE: {CHECK_INTERFACE(IMediaPlayerService, data, reply);sp<IMediaPlayerClient> client =interface_cast<IMediaPlayerClient>(data.readStrongBinder());//獲取BpMediaPlayerClientint audioSessionId = data.readInt32();sp<IMediaPlayer> player = create(client, audioSessionId);//創(chuàng)建BnMediaPlayer實(shí)體reply->writeStrongBinder(player->asBinder());//將BnMediaPlayer binder實(shí)體寫到驅(qū)動(dòng)中。return NO_ERROR;} break;case DECODE_URL: {
以上就是匿名binder實(shí)體如何通過實(shí)名binder實(shí)體來進(jìn)行傳遞的概要說明。
而播放器的播放調(diào)用過程如下: MediaPlayer ?mMediaPlayer = new MediaPlayer( );
mMediaPlayer.setDataSource(mContext, mUri);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.prepareAsync();
mMediaPlayer.start();
下面以setDataSource調(diào)用過程為列來說明用戶的命令是如何從客服端發(fā)送到媒體后臺(tái)服務(wù)的。
MediaPlayer::setDataSource //MediaPlayer.cpp|-->const sp<IMediaPlayerService>& service(getMediaPlayerService());//獲取BpMeidaPlayerService|-->sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//返回BpMediaPlayer|-->player->setDataSource(url, headers)//調(diào)用BpMediaPlayer.setDataSource(ImediaPlayer.cpp)以上調(diào)用會(huì)通過binder調(diào)用到binder實(shí)體對(duì)應(yīng)的函數(shù):MediaPlayerService::Client::setDataSource|-->MediaPlayerFactory::getPlayerType()//獲取播放器的類型|-->setDataSource_pre(playerType)//根據(jù)playerType創(chuàng)建具體的播放器實(shí)例,例如ffplayer(見ffplayer.cpp) |-->p->setDataSource//調(diào)用具體播放器的setDataSource函數(shù)
下面再說下媒體播放器的事件是如何通知到用戶的。分兩部分來論述:媒體后臺(tái)服務(wù)器端和用戶端 媒體后臺(tái)服務(wù)器端: MediaPlayerService::Client::notify //MediaPlayerService.cpp|-->sp<IMediaPlayerClient> c;|-->c = client->mClient;//獲取BpMediaPlayerClient,即binder的客服端 |-->c->notify(); 以上調(diào)用會(huì)通過binder調(diào)用到BnMediaPlayer,即binder實(shí)體端:? MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)//MediaPlayer.cpp
|-->listener->notify() 而以上MediaPlayerService::Client::notify函數(shù)會(huì)通過如下過程傳遞到具體的播放器中,以供播放器來上報(bào)事件。 MediaPlayerService::Client::createPlayer |--->MediaPlayerFactory::createPlayer(playerType, this, notify)|--->p->setNotifyCallback //MediaPlayerInterface.h|->mNotify = notifyFunc|->sendEvent //上面初始化的mNotify函數(shù),被sendEvent函數(shù)調(diào)用|->mNotify(mCookie, msg, ext1, ext2, obj);
所以在具體的播放器實(shí)現(xiàn)中,可以直接調(diào)用MediaPlayerBase::sendEvent來將事件發(fā)送到用戶。
播放器的用戶端: 回到MediaPlayer.cpp文件中的MediaPlayer::setListener函數(shù),該函數(shù)設(shè)置了偵聽者,到收到事件時(shí),會(huì)調(diào)用該偵聽者。該偵聽者的初始化過程如下: android_media_MediaPlayer_native_setup//android_media_MediaPlayer.cpp|-->sp<MediaPlayer> mp = new MediaPlayer();|-->sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);|-->mp->setListener(listener);//即為<span style="font-size:18px;">MediaPlayer::setListener函數(shù)</span>以上過程,將<span style="font-size:18px;">偵聽者設(shè)置為JNIMediaPlayerListener,所以當(dāng)事件產(chǎn)生時(shí),會(huì)調(diào)用 </span> void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)|-->env->CallStaticVoidMethod(mClass, fields.post_event...);//從jni調(diào)到MediaPlayer.java|--->postEventFromNative //MediaPlayer.java|-->Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);|-->mp.mEventHandler.sendMessage(m);//MediaPlayer.mEventHandler = new EventHandler(this, looper);
具體到用戶的播放器界面中,用戶可以通過實(shí)現(xiàn)如下函數(shù): MediaPlayer.OnCompletionListener
MediaPlayer.OnTimedTextListener
MediaPlayer.OnInfoListener MediaPlayer.OnPreparedListener MediaPlayer.OnBufferingUpdateListener MediaPlayer.OnBufferingUpdateListener 來實(shí)現(xiàn)對(duì)相應(yīng)事件的處理。
相關(guān)的參考文檔: http://developer.android.com/guide/topics/media/mediaplayer.html
http://developer.android.com/guide/topics/media/audio-capture.html#audiocapture
總結(jié)
- 上一篇: node.js毕业设计客户拜访系统小程序
- 下一篇: CF 706div2