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