1.起因 最近一直在看 C++ 同时想 C++ 看完后结合 ffpemg 和 qt写一个跨平台的播放器,恰好最近轮到我做组内 Presentation,就想着分析下 Android 中的 Media Server 看看 Google 在做播放架构的时候是怎么做的同时可以用到我后续的开发中。
Media Server 整体的架构师 C/S 架构,C 和 S 之间的通讯是 IPC,具体来说是 Binder。Media Server 中大量的用到了 Binder。整个架构将播放控制、视频、音频、相机等和多媒体有关的这些包装成不同的服务,通过 IPC 解耦。下图是 Google 关于 Android 中 Media 引擎 的架构做的关系图。
在系统 mediaserver 作为一个单独的进程,负责整个系统的音频视频编解码工作:
下面先从 Java 层的调用顺序开始看一下整个调用的流程。
MediaPlayer 中涉及到的主要函数都是通过 JNI 来完成的,MediaPlayer.java 对应的是 android_media_MediaPlayer.cpp 。其中的对应关系如下,省略了一部分不是特别重要的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 static JNINativeMethod gMethods[] = { { "nativeSetDataSource" , "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;" "[Ljava/lang/String;)V" , (void *)android_media_MediaPlayer_setDataSourceAndHeaders }, {"_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}, {"native_init" , "()V" , (void *)android_media_MediaPlayer_native_init}, {"native_setup" , "(Ljava/lang/Object;)V" , (void *)android_media_MediaPlayer_native_setup}, };
Java 这边的调用顺序通常是:
1 2 3 4 5 6 7 MediaPlayer mp = new MediaPlayer ();mp.setDataSource("/sdcard/test.mp3" ); mp.prepare(); mp.start(); mp.pause(); mp.stop(); mp.release();
下面将按照这个顺序一步一步来分析。
3.1 构造函数 在 MediaPlayer 的构造函数中调用了 native 的 android_media_MediaPlayer_native_setup 方法:
1 2 3 4 5 6 7 8 9 10 public MediaPlayer () { ... native_setup(new WeakReference <MediaPlayer>(this )); }
setup 方法中创建了 MediaPlayer,同时也设置了回调函数。其中最后一行的 setMediaPlayer 将 MediaPlayer 的指针保存成一个 Java 对象,之后可以看到 getMediaPlayer 通过同样的方法获取到该对象的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static void android_media_MediaPlayer_native_setup (JNIEnv *env, jobject thiz, jobject weak_this) { ALOGV ("native_setup" ); sp<MediaPlayer> mp = new MediaPlayer (); if (mp == NULL ) { jniThrowException (env, "java/lang/RuntimeException" , "Out of memory" ); return ; } sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener (env, thiz, weak_this); mp->setListener (listener); setMediaPlayer (env, thiz, mp); } static sp<MediaPlayer> setMediaPlayer (JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player) { Mutex::Autolock l (sLock) ; sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField (thiz, fields.context); if (player.get ()) { player->incStrong ((void *)setMediaPlayer); } if (old != 0 ) { old->decStrong ((void *)setMediaPlayer); } env->SetLongField (thiz, fields.context, (jlong)player.get ()); return old; }
这里解释两点:
1 .由于指针的大小和 long 的大小是一样的,所以可以通过 SetLongField 和 GetLongField 来保存函数指针。2 .由于 shared_ptr 和 weak_ptr 是 C++11 中才加入的,所以源码中实现了 sp 和 wp 作为智能指针来使用,这里可以看到代码中手动控制了引用计数。
现在一个 mediaplayer 对象就创建好了,其他的 jni 中对应的方法几乎都是调用 mediaplayer 来完成的。
3.2 设置数据源 setDataSource 对应的是 jni 中的这个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static void android_media_MediaPlayer_setDataSourceFD (JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { sp<MediaPlayer> mp = getMediaPlayer (env, thiz); if (mp == NULL ) { jniThrowException (env, "java/lang/IllegalStateException" , NULL ); return ; } if (fileDescriptor == NULL ) { jniThrowException (env, "java/lang/IllegalArgumentException" , NULL ); return ; } int fd = jniGetFDFromFileDescriptor (env, fileDescriptor); ALOGV ("setDataSourceFD: fd %d" , fd); process_media_player_call ( env, thiz, mp->setDataSource (fd, offset, length), "java/io/IOException" , "setDataSourceFD failed." ); }
可以看到函数开始先做了空指针的判断,之后调用了 mp->setDataSource(fd, offset, length) 方法来给 mediaplayer 设置数据源,同时 process_media_player_call 函数对 setDataSource 返回值做判断是否设置成功以及失败后抛出异常。
mediaplayer 中的 setDataSource 函数分为三种,分别对应播放文件、网络和流三种情况,现在只看播放文件的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 status_t MediaPlayer::setDataSource (int fd, int64_t offset, int64_t length) { ALOGV ("setDataSource(%d, %" PRId64 ", %" PRId64 ")" , fd, offset, length); status_t err = UNKNOWN_ERROR; const sp<IMediaPlayerService>& service (getMediaPlayerService()) ; if (service != 0 ) { sp<IMediaPlayer> player (service->create(this , mAudioSessionId)) ; if ((NO_ERROR != doSetRetransmitEndpoint (player)) || (NO_ERROR != player->setDataSource (fd, offset, length))) { player.clear (); } err = attachNewPlayer (player); } return err; }
这个函数分为三个部分,逐个分析。
这里先是调用了 getMediaPlayerService 方法获取到一个 IMediaPlayerService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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" )); if (binder != 0 ) { break ; } ALOGW ("Media player service not published, waiting..." ); usleep (500000 ); } while (true ); if (sDeathNotifier == NULL ) { sDeathNotifier = new DeathNotifier (); } binder->linkToDeath (sDeathNotifier); sMediaPlayerService = interface_cast <IMediaPlayerService>(binder); } ALOGE_IF (sMediaPlayerService == 0 , "no media player service!?" ); return sMediaPlayerService; }
IServiceManager 是 IInterface 类型,和用 AIDL 生成的接口相同,IInterface 中包含了 binder 和 remote 两个东西。在 ndk 中对应的是 BnInterface 和 BpInterface。在 Java 中,本地 Client 通过 ServiceConnection 中的 onServiceConnected(ComponentName name, IBinder service) 方法获得 Service 中在 onBind 时候返回的 binder,同理,这里通过 ServiceManager 的 getService 获得和 media.player 这个 Service 通信用的 binder。
到这里为止我们只拿到了 media.player Service 的 binder,要想调用接口中的方法还需要通过 asInterface 方法来获得与之对应的 IInterface 接口。这里的 interface_cast(binder) 方法就可以获得对应的接口,那么 interface_cast 是怎么做到的呢,其实很简单,利用了模板封装了 asInterface 的操作:
1 2 3 4 template <typename INTERFACE> inline sp<INTERFACE> interface_cast (const sp<IBinder>& obj) { return INTERFACE::asInterface (obj); }
现在我们就拿到了 media.player Service 的接口。
看到这里会有一个疑问,这个 media.player 的 Service 是什么时候启动的呢。我们根据 media.player 这个线索找到了下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void MediaPlayerService::instantiate () { defaultServiceManager ()->addService (String16 ("media.player" ), new MediaPlayerService ()); } int main (int argc __unused, char ** argv) { ... sp<ProcessState> proc (ProcessState::self()) ; sp<IServiceManager> sm = defaultServiceManager (); ALOGI ("ServiceManager: %p" , sm.get ()); AudioFlinger::instantiate (); MediaPlayerService::instantiate (); CameraService::instantiate (); AudioPolicyService::instantiate (); SoundTriggerHwService::instantiate (); registerExtensions (); ProcessState::self ()->startThreadPool (); IPCThreadState::self ()->joinThreadPool (); }
其中 MediaPlayerService 位于 MediaPlayerService.cpp 中,而 main 函数位于 main_mediaserver.cpp 。还记得在本文最开始的那张图么,其中的 mediaserver 对应的就是这个。它是在开机的时候启动的,被写在了启动脚本中:
这样在系统启动的时候这个进程就开启了,同时里面的 Service 也就启动了。
service->create(this, mAudioSessionId) 方法通过 IPC 的方式调用 MediaPlayerService 中的 create 方法获得 IMediaPlayer ,IMediaPlayer 从名字就可以看出是一个 IInterface,所以 MediaPlayer 也是通过 IPC 来调用的。先看这个方法做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 sp<IMediaPlayer> MediaPlayerService::create (const sp<IMediaPlayerClient>& client, int audioSessionId) { pid_t pid = IPCThreadState::self ()->getCallingPid (); int32_t connId = android_atomic_inc (&mNextConnId); sp<Client> c = new Client ( this , pid, connId, client, audioSessionId, IPCThreadState::self ()->getCallingUid ()); ALOGV ("Create new client(%d) from pid %d, uid %d, " , connId, pid, IPCThreadState::self ()->getCallingUid ()); wp<Client> w = c; { Mutex::Autolock lock (mLock) ; mClients.add (w); } return c; }
首先调用的时候传的参数是 MediaPlayer 本身,MediaPlayer 除了继承 IMediaDeathNotifier 同时还继承了 BnMediaPlayerClient,而 BnMediaPlayerClient 又继承了 BnInterface,所以这里的参数列表是一个 client 引用。其中 Client 的实现也在 MediaPlayerService.cpp 这个文件中,他的构造函数用来保存这些对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MediaPlayerService::Client::Client ( const sp<MediaPlayerService>& service, pid_t pid, int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId, uid_t uid) { ALOGV ("Client(%d) constructor" , connId); mPid = pid; mConnId = connId; mService = service; mClient = client; mLoop = false ; mStatus = NO_INIT; mAudioSessionId = audioSessionId; mUID = uid; mRetransmitEndpointValid = false ; mAudioAttributes = NULL ; #if CALLBACK_ANTAGONIZER ALOGD ("create Antagonizer" ); mAntagonizer = new Antagonizer (notify, this ); #endif }
最后 create 方法返回一个 MediaPlayer 的接口,这个接口通过 IPC 用来调用 Client 中的函数。
3.2.3 设置数据源 经过了之前的折腾,我们先拿到了 MediaPlayerService 的接口,通过 MediaPlayerService 的接口又拿到了 MediaPlayer 的接口,接下来就要进行这个函数的最终目的设置数据源 。
同样是通过 IPC 调用了 MediaPlayer 的 setDataSource,定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 status_t MediaPlayerService::Client::setDataSource (int fd, int64_t offset, int64_t length){ ... player_type playerType = MediaPlayerFactory::getPlayerType (this , fd, offset, length); sp<MediaPlayerBase> p = setDataSource_pre (playerType); if (p == NULL ) { return NO_INIT; } setDataSource_post (p, p->setDataSource (fd, offset, length)); return mStatus; }
首先是获取播放器类型,播放器类型定义在 MediaPlayerInterface.h 中:
1 2 3 4 5 6 7 8 9 10 11 enum player_type { PV_PLAYER = 1 , SONIVOX_PLAYER = 2 , STAGEFRIGHT_PLAYER = 3 , NU_PLAYER = 4 , TEST_PLAYER = 5 , };
下面说下这几种类型是做什么的,其中的每一个都对应一个工厂来创建对应的 Player,由于 PV_PLAYER 已经被抛弃了,所以在 5.1 的源码里并没有出现它
1.PV_PLAYER 这个类型是 Android 最初采用的 OpenCore,由于太臃肿已经被抛弃 2.SONIVOX_PLAYER 用来处理 midi 相关 3.NU_PLAYER 全能型,在 5.x 上处于可选 4.STAGEFRIGHT_PLAYER 5.x 之前的主力即 awesome player ,可以胜任除 midi 外全部的工作 5.TEST_PLAYER 测试用
这些 player 都是由对应的 factory 创建的,对应的实现在 MediaPlayerFactory.cpp 中,其中的代码比较简单,这里就不分析了,主要是匹配不同类型对应不同的分数,然后选取分高的 player 创建。当前的主力是 STAGEFRIGHT_PLAYER 也就是 awesome player,而 NU_PLAYER 是未来的主力,从 Android M 目前的源码 中也可以看出代码中只剩下了 NU_PLAYER 和 STAGEFRIGHT_PLAYER,其中 NU_PLAYER 负责网络和流的播放,STAGEFRIGHT_PLAYER 负责有 DRM 和文件的播放。
这里我们就以目前的主力 STAGEFRIGHT_PLAYER 播放器继续往下分析。获取到播放器类型后就到了创建播放器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre (player_type playerType) { ... sp<MediaPlayerBase> p = createPlayer (playerType); if (p == NULL ) { return p; } ... return p; } sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer (player_type playerType) { sp<MediaPlayerBase> p = mPlayer; if ((p != NULL ) && (p->playerType () != playerType)) { ALOGV ("delete player" ); p.clear (); } if (p == NULL ) { p = MediaPlayerFactory::createPlayer (playerType, this , notify); } ... return p; }
这里从 MediaPlayerFactory 创建了对应的播放器,之后使用创建好的 player 设置数据源:
1 2 3 4 status_t StagefrightPlayer::setDataSource (int fd, int64_t offset, int64_t length) { ALOGV ("setDataSource(%d, %lld, %lld)" , fd, offset, length); return mPlayer->setDataSource (dup (fd), offset, length); }
这里的 mPlayer 就是 AwesomePlayer,AwesomePlayer 在 StagefrightPlayer 类的构造函数中创建:
1 2 3 4 StagefrightPlayer::StagefrightPlayer (): mPlayer (new AwesomePlayer) { ALOGV ("StagefrightPlayer" ); mPlayer->setListener (this ); }
从 StagefrightPlayer 的源码可以看到对象中的 AwesomePlayer 来完成。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 status_t AwesomePlayer::setDataSource ( int fd, int64_t offset, int64_t length) { Mutex::Autolock autoLock (mLock) ; reset_l (); sp<DataSource> dataSource = new FileSource (fd, offset, length); ... return setDataSource_l (dataSource); } status_t AwesomePlayer::setDataSource_l (const sp<DataSource> &dataSource) { sp<MediaExtractor> extractor = MediaExtractor::Create (dataSource); if (extractor == NULL ) { return UNKNOWN_ERROR; } if (extractor->getDrmFlag ()) { checkDrmStatus (dataSource); } return setDataSource_l (extractor); }
这个分离器其实和 ffpmeg 中的 demuxer 一样,作用是将数据中的音频部分和视频部分分开,然后获取到对应的类型,调用对应的解码器进行解码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 status_t AwesomePlayer::setDataSource_l (const sp<MediaExtractor> &extractor) { int64_t totalBitRate = 0 ; mExtractor = extractor; for (size_t i = 0 ; i < extractor->countTracks (); ++i) { sp<MetaData> meta = extractor->getTrackMetaData (i); int32_t bitrate; if (!meta->findInt32 (kKeyBitRate, &bitrate)) { const char *mime; CHECK (meta->findCString (kKeyMIMEType, &mime)); ALOGV ("track of type '%s' does not publish bitrate" , mime); totalBitRate = -1 ; break ; } totalBitRate += bitrate; } sp<MetaData> fileMeta = mExtractor->getMetaData (); if (fileMeta != NULL ) { int64_t duration; if (fileMeta->findInt64 (kKeyDuration, &duration)) { mDurationUs = duration; } } mBitrate = totalBitRate; bool haveAudio = false ; bool haveVideo = false ; for (size_t i = 0 ; i < extractor->countTracks (); ++i) { sp<MetaData> meta = extractor->getTrackMetaData (i); const char *_mime; CHECK (meta->findCString (kKeyMIMEType, &_mime)); String8 mime = String8 (_mime); if (!haveVideo && !strncasecmp (mime.string (), "video/" , 6 )) { setVideoSource (extractor->getTrack (i)); haveVideo = true ; ... } else if (!haveAudio && !strncasecmp (mime.string (), "audio/" , 6 )) { setAudioSource (extractor->getTrack (i)); haveAudio = true ; ... if (!strcasecmp (mime.string (), MEDIA_MIMETYPE_AUDIO_VORBIS)) { sp<MetaData> fileMeta = extractor->getMetaData (); int32_t loop; if (fileMeta != NULL && fileMeta->findInt32 (kKeyAutoLoop, &loop) && loop != 0 ) { modifyFlags (AUTO_LOOPING, SET); } } } else if (!strcasecmp (mime.string (), MEDIA_MIMETYPE_TEXT_3GPP)) { addTextSource_l (i, extractor->getTrack (i)); } } ... return OK; }
这里通过分离器来确定数据源中是音频还是视频,然后对 player 的 videotrack 和 audiotrack 进行相对应的设置。
到此为止 setDataSource 的流程就算是走完了,如果继续往下分析的话就到了视频的编解码的知识了。下面先用几张图来梳理一下这个过程(画的不是很规范)。
流程图:
类图:
类图中的 Client 是 MediaPlayerService 的内部类。MediaPlayerService 通过 IPC 和 MediaPlayerService 通信获得 Client,Client 中包含了 StagefrightPlayer,而 StagefrightPlayer 最终通过调用 AwesomePlayer 对应的方法。
3.3 Prepare 通过之前的经验我们可以很快的知道 prepare 应该对应的是 AwesomePlayer 中的 prepare:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 status_t AwesomePlayer::prepare () { ... return prepare_l (); } status_t AwesomePlayer::prepare_l () { ... status_t err = prepareAsync_l (); ... return mPrepareResult; } status_t AwesomePlayer::prepareAsync_l () { if (mFlags & PREPARING) { return UNKNOWN_ERROR; } if (!mQueueStarted) { mQueue.start (); mQueueStarted = true ; } modifyFlags (PREPARING, SET); mAsyncPrepareEvent = new AwesomeEvent ( this , &AwesomePlayer::onPrepareAsyncEvent); mQueue.postEvent (mAsyncPrepareEvent); return OK; }
mQueue 是 TimedEventQueue ,TimedEventQueue 和 Handler 很相似,使用 pthread 和 队列来管理消息。在这里通过异步方式回调 onPrepareAsyncEvent 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 void AwesomePlayer::onPrepareAsyncEvent () { Mutex::Autolock autoLock (mLock) ; beginPrepareAsync_l (); } void AwesomePlayer::beginPrepareAsync_l () { if (mFlags & PREPARE_CANCELLED) { ALOGI ("prepare was cancelled before doing anything" ); abortPrepare (UNKNOWN_ERROR); return ; } if (mUri.size () > 0 ) { status_t err = finishSetDataSource_l (); if (err != OK) { abortPrepare (err); return ; } } if (mVideoTrack != NULL && mVideoSource == NULL ) { status_t err = initVideoDecoder (); if (err != OK) { abortPrepare (err); return ; } } if (mAudioTrack != NULL && mAudioSource == NULL ) { status_t err = initAudioDecoder (); if (err != OK) { abortPrepare (err); return ; } } modifyFlags (PREPARING_CONNECTED, SET); if (isStreamingHTTP ()) { postBufferingEvent_l (); } else { finishAsyncPrepare_l (); } } void AwesomePlayer::finishAsyncPrepare_l () { if (mIsAsyncPrepare) { if (mVideoSource == NULL ) { notifyListener_l (MEDIA_SET_VIDEO_SIZE, 0 , 0 ); } else { notifyVideoSize_l (); } notifyListener_l (MEDIA_PREPARED); } mPrepareResult = OK; modifyFlags ((PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED), CLEAR); modifyFlags (PREPARED, SET); mAsyncPrepareEvent = NULL ; mPreparedCondition.broadcast (); if (mAudioTearDown) { if (mPrepareResult == OK) { if (mExtractorFlags & MediaExtractor::CAN_SEEK) { seekTo_l (mAudioTearDownPosition); } if (mAudioTearDownWasPlaying) { modifyFlags (CACHE_UNDERRUN, CLEAR); play_l (); } } mAudioTearDown = false ; } }
经过一番设置后,prepare 的过程也算是完成了。这一部分主要是对 player 的状态进行设置,通过消息机制让让调用端也知道这边的 player 状态。
3.4 start Java 代码这边的 start 方法对应的是 StagefrightPlayer 中的 start,其中又调用了 player 的 play 方法
1 2 3 4 status_t StagefrightPlayer::start () { ALOGV ("start" ); return mPlayer->play (); }
对应的是 AwesomePlayer 中的 play 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 status_t AwesomePlayer::play () { ATRACE_CALL (); Mutex::Autolock autoLock (mLock) ; modifyFlags (CACHE_UNDERRUN, CLEAR); return play_l (); } status_t AwesomePlayer::play_l () { modifyFlags (SEEK_PREVIEW, CLEAR); if (mFlags & PLAYING) { return OK; } mMediaRenderingStartGeneration = ++mStartGeneration; if (!(mFlags & PREPARED)) { status_t err = prepare_l (); if (err != OK) { return err; } } modifyFlags (PLAYING, SET); modifyFlags (FIRST_FRAME, SET); if (mDecryptHandle != NULL ) { int64_t position; getPosition (&position); mDrmManagerClient->setPlaybackStatus (mDecryptHandle, Playback::START, position / 1000 ); } if (mAudioSource != NULL ) { if (mAudioPlayer == NULL ) { createAudioPlayer_l (); } CHECK (!(mFlags & AUDIO_RUNNING)); if (mVideoSource == NULL ) { status_t err = startAudioPlayer_l (false ); if ((err != OK) && mOffloadAudio) { ALOGI ("play_l() cannot create offload output, fallback to sw decode" ); int64_t curTimeUs; getPosition (&curTimeUs); delete mAudioPlayer; mAudioPlayer = NULL ; if (!(mFlags & AUDIOPLAYER_STARTED)) { mAudioSource->stop (); } modifyFlags ((AUDIO_RUNNING | AUDIOPLAYER_STARTED), CLEAR); mOffloadAudio = false ; mAudioSource = mOmxSource; if (mAudioSource != NULL ) { err = mAudioSource->start (); if (err != OK) { mAudioSource.clear (); } else { mSeekNotificationSent = true ; if (mExtractorFlags & MediaExtractor::CAN_SEEK) { seekTo_l (curTimeUs); } createAudioPlayer_l (); err = startAudioPlayer_l (false ); } } } if (err != OK) { delete mAudioPlayer; mAudioPlayer = NULL ; modifyFlags ((PLAYING | FIRST_FRAME), CLEAR); if (mDecryptHandle != NULL ) { mDrmManagerClient->setPlaybackStatus (mDecryptHandle, Playback::STOP, 0 ); } return err; } } } if (mTimeSource == NULL && mAudioPlayer == NULL ) { mTimeSource = &mSystemTimeSource; } if (mVideoSource != NULL ) { postVideoEvent_l (); if (mAudioSource != NULL && mVideoSource != NULL ) { postVideoLagEvent_l (); } } if (mFlags & AT_EOS) { seekTo_l (0 ); } uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted | IMediaPlayerService::kBatteryDataTrackDecoder; if ((mAudioSource != NULL ) && (mAudioSource != mAudioTrack)) { params |= IMediaPlayerService::kBatteryDataTrackAudio; } if (mVideoSource != NULL ) { params |= IMediaPlayerService::kBatteryDataTrackVideo; } addBatteryData (params); if (isStreamingHTTP ()) { postBufferingEvent_l (); } return OK; }
视频的播放是通过不断的 postVideoEvent_l 来实现画面的播放,postVideoEvent_l 会向队列中 post 一个 videoEvent,这个 event 最终又会触发 AwesomePlayer::onVideoEvent 的方法, onVideoEvent 方法中又会调用 postVideoEvent_l,形成循环,最终实现视频的播放。startAudioPlayer_l 最终会调用到 AudioPlayer 播放音频。
播放的过程到这里就结束了,接下来暂停和结束的部分就相对简单了。
3.5 pause pause 对应到 AwesomePlayer 中是 pause_l 这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 status_t AwesomePlayer::pause_l (bool at_eos) { ... notifyListener_l (MEDIA_PAUSED); mMediaRenderingStartGeneration = ++mStartGeneration; cancelPlayerEvents (true ); if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { mAudioPlayer->pause (at_eos ); if (mOffloadAudio) { postAudioTearDownEvent (kOffloadPauseMaxUs); } modifyFlags (AUDIO_RUNNING, CLEAR); } if (mFlags & TEXTPLAYER_INITIALIZED) { mTextDriver->pause (); modifyFlags (TEXT_RUNNING, CLEAR); } modifyFlags (PLAYING, CLEAR); if (mDecryptHandle != NULL ) { mDrmManagerClient->setPlaybackStatus (mDecryptHandle, Playback::PAUSE, 0 ); } ... return OK; }
3.6 stop 然而 stop 和 pause 并没什么区别,代码中的注释也是挺有意思
1 2 3 4 5 6 7 8 9 status_t StagefrightPlayer::stop () { ALOGV ("stop" ); return pause (); } status_t StagefrightPlayer::pause () { ALOGV ("pause" ); return mPlayer->pause (); }
3.7 release MeidaPlayer 对应 jni 中的 android_media_MediaPlayer_release 方法:
1 2 3 4 5 6 7 8 9 10 11 12 static void android_media_MediaPlayer_release (JNIEnv *env, jobject thiz) { ALOGV ("release" ); decVideoSurfaceRef (env, thiz); sp<MediaPlayer> mp = setMediaPlayer (env, thiz, 0 ); if (mp != NULL ) { mp->setListener (0 ); mp->disconnect (); } }
setListener 注释中说的很明白了,就是把 listener 置空。diconnect 这里是断开了了和 mediaserver 的连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 void MediaPlayer::disconnect () { ALOGV ("disconnect" ); sp<IMediaPlayer> p; { Mutex::Autolock _l(mLock); p = mPlayer; mPlayer.clear (); } if (p != 0 ) { p->disconnect (); } }
4.总结 MediaPlayer 整体上的流程就是这些,其中相对复杂的地方集中在编解码和不同 player 的使用上。编解码相关的主要是 OMX 的硬解和软解,player 主要是不同的 Android 版本对不同的 player 优先使用不同。