生命周期
音视频文件(或流)的播放控制是通过下图所示的状态机来管理的,图例说明:
椭圆代表的是播放器可能达到的状态
弧线代表的是驱动状态改变的控制操作
单箭头的弧线代表的是同步调用
双箭头的弧线代表的是异步调用
权限相关
媒体播放需要的权限根据其实现的功能主要有以下三个:
播放网络媒体需要INTERNET权限
<uses-permission android:name="android.permission.INTERNET" />
播放时保持屏幕常亮需要WAKE_LOCK权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
实现播放缓存功能可能需要读写SD卡权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
回调处理
MediaPlayer提供了比较多的回调监听处理,具体有:
setOnPreparedListener,播放器准备完毕
setOnVideoSizeChangedListener,播放器尺寸改变
setOnSeekCompleteListener,拖动完成
setOnCompletionListener,播放完成
setOnBufferingUpdateListener,缓存更新
setOnInfoListener,有可用信息或警告时
setOnErrorListener,发生错误时
MediaPlayer需要在拥有自己Looper的线程中创建
开发指南
Android多媒体框架有两个主要的类:MediaPlayer和AudioManager,前者主要负责音视频播放的基础API,后者主要管理设备上的音频资源和音频输出。
可以用来播放以下三种类型:
播放raw资源:
1 2 MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start();
播放本机资源:
1 2 3 4 5 6 Uri myUri = ....; MediaPlayer mediaPlayer = new MediaPlayer (); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();
播放网络资源:
1 2 3 4 5 6 String url = "http://........" ; MediaPlayer mediaPlayer = new MediaPlayer (); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); mediaPlayer.start();
注意:播放的网络资源必须是可以逐步下载的。同时要捕获IllegalArgumentException和IOException因为请求的网络资源可能是不存在的。
异步执行
注意某些操作需要较长时间(例如prepare方法调用),切勿从应用的界面线程中调用它 。
避免界面线程挂起,使用其他线程来准备MediaPlayer,并在准备工作完成后通知主线程。框架提供prepareAsync()方法,会在后台开始准备媒体,并立即返回。当媒体准备就绪后,系统会调用MediaPlayer.OnPreparedListener的onPrepared()方法(通过setOnPreparedListener配置)。
状态管理
MediaPlayer具有内部状态,在编写代码时要始终注意,通常情况下状态是按如下节奏变化的:
Idle -> setDataSource() -> Initialized ->
prepare()或prepareAsync() -> Prepared -> start(),pause()和seekTo()
-> stop() -> Stopped.
MediaPlayer会占用宝贵的系统资源,不再使用时要及时进行资源的释放:
1 2 mediaPlayer.release(); mediaPlayer = null ;
如果需要支持后台播放的功能,那么就要使用Service进行MediaPlayer的播放操作。官方建议使用MediaBrowserServiceCompat和MediaBrowserCompat来实现,它是一种C/S架构。通常是一个持有MediaPlayer的Service实例来进行播放的。
异步执行
即使是后台播放(Service通常和Activity一样默认运行在主线程的),Service中也不能进行耗时操作,也应采用异步执行并在准备完成后通知播放器进行播放。通常实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyService extends Service implements MediaPlayer .OnPreparedListener { private static final String ACTION_PLAY = "com.example.action.PLAY" ; MediaPlayer mediaPlayer = null ; public int onStartCommand (Intent intent, int flags, int startId) { ... if (intent.getAction().equals(ACTION_PLAY)) { mediaPlayer = ... mediaPlayer.setOnPreparedListener(this ); mediaPlayer.prepareAsync(); } } public void onPrepared (MediaPlayer player) { player.start(); } }
处理异步错误
同步的播放操作在发生错误或异常后马上就会得到通知,但异步的情形是就需要我们自己在错误回调里面进行处理了,在Service里处理异步错误的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MyService extends Service implements MediaPlayer .OnErrorListener { MediaPlayer mediaPlayer; public void initMediaPlayer () { mediaPlayer.setOnErrorListener(this ); } @Override public boolean onError (MediaPlayer mp, int what, int extra) { } }
WakeLock处理
当设备休眠时,通常会关闭那些不必要的功能(包括CPU和WiFi硬件),所以后台播放需要请求Wakelock,MediaPlayer处理WakeLock很简单,代码如下:
mediaPlayer = new MediaPlayer();
// ... 此处其他初始化操作 ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
播放网络资源时,要防止WiFi被关,请求WiFi的WakeLock代码如下:
1 2 3 4 5 6 7 8 WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock" ); wifiLock.acquire(); wifiLock.release();
注意 :在不必要的时候就要及时释放WakeLock,毕竟WakeLock会减少设备电池的寿命。
数字版权管理(DRM)
从Android
8.0(API级别26)开始,MediaPlayer包含支持播放受DRM保护的资料的API,其并不提供MediaDrm的完整功能,当前可处理以下内容:
受Widevine保护的本地媒体文件
受Widevine保护的远程/流式传输媒体文件
下面的代码片段展示了同步使用DRM MediaPlayer的一般过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 setDataSource(); setOnDrmConfigHelper(); prepare(); if (getDrmInfo() != null ) { prepareDrm(); getKeyRequest(); provideKeyResponse(); } start(); stop(); releaseDrm();
异步设置DRM
可以创建和注册用于进行DRM准备的MediaPlayer.OnDrmInfoListener以及用于启动播放器的MediaPlayer.OnDrmPreparedListener,从而异步初始化DRM,与prepareAsync()结合使用,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 setOnPreparedListener(); setOnDrmInfoListener(); setDataSource(); prepareAsync(); onDrmInfo() { prepareDrm(); getKeyRequest(); provideKeyResponse(); } onPrepared() { start(); }
处理加密媒体
从Android
8.0(API级别26)开始,MediaPlayer开始支持为基本的流类型H.264和AAC解密通用加密方案(CENC)和HLS样本级加密媒体(METHOD=SAMPLE-AES),之前支持全分段加密媒体(METHOD=AES-128)。
从ContentResolver检索媒体
可以通过查询ContentResolver找到外部媒体来完成该操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ContentResolver contentResolver = getContentResolver();Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;Cursor cursor = contentResolver.query(uri, null , null , null , null );if (cursor == null ) { } else if (!cursor.moveToFirst()) { } else { int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); do { long thisId = cursor.getLong(idColumn); String thisTitle = cursor.getString(titleColumn); } while (cursor.moveToNext()); }
要将其与MediaPlayer结合使用,您可以执行以下操作:
1 2 3 4 5 6 7 8 9 long id = ;Uri contentUri = ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); mediaPlayer = new MediaPlayer (); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), contentUri);
源码分析
注意:此处分析的是android-5.1.1_r18,是比较老的版本。
MediaPlayer中大部分的功能使用C++实现,Java这边做的工作大部分是JNI的调用,这篇文章主要分析了常用的几个接口对应C++实现和Media
Server。
本文来自于Media
Player 源码分析
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 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();
下面将按照这个顺序一步一步来分析。
构造函数
在MediaPlayer的构造函数中调用了native的android_media_MediaPlayer_native_setup
方法:
1 2 3 4 5 6 7 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 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来完成的。
设置数据源
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<IMediaPlayerService>(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<IMediaPlayerClient>,所以这里的参数列表是一个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中的函数。
设置数据源
经过了之前的折腾,我们先拿到了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的源码里并没有出现它。
PV_PLAYER
这个类型是Android最初采用的OpenCore,由于太臃肿已经被抛弃
SONIVOX_PLAYER 用来处理midi相关
NU_PLAYER 全能型,在安卓5.x上处于可选
STAGEFRIGHT_PLAYER 安卓5.x之前的主力即awesome
player,可以胜任除midi外全部的工作
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对应的方法。
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状态。
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 播放音频。
播放的过程到这里就结束了,接下来暂停和结束的部分就相对简单了。
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; }
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 (); }
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 (); } }
总结
MediaPlayer整体上的流程就是这些,其中相对复杂的地方集中在编解码和不同player的使用上。编解码相关的主要是OMX的硬解和软解,player主要是不同的Android版本对不同的player优先使用不同。
参考
Media
Playback
MediaPlayer
Media
深入理解
Android I