Android MediaPlayer架构 -- MediaPlayer的建立过程

本文系做者本身学习之所用,文章内容仅出自做者拙劣之思考,问题之处烦请不吝指教。html

  MediaPlayer 能被用来控制音/视频文件或流媒体的回放。Android中以MediaPlayer类做为音视频播放的基础类,围绕着他开展了一系列的处理。学习一个新的模块,最简单的步骤就是找到一个典型的应用程序,经过它的实现,来分析整个模块的数据流和控制流。典型的MediaPlayer在Java处的接口包括视频播放类VideoView以及音频专用MediaPlayer类。java

  1、 一个简单的视频播放demo app

  Android中实现视频的播放能够采用MediaPlayer+SurfaceView配合的方式,其实Android还为开发人员提供了另一种更简单的播放视频媒体的方式,那就是VideoView。VideoView类,其实质是用MediaPlayer类来实现的,只是因为其是视频播放,不得不和Surfaceview挂上够,才将其独立出来。使得其有以下的结构:android

1 public class VideoView extends SurfaceView 2         implements MediaPlayerControl, SubtitleController.Anchor { 3     private static final String TAG = "VideoView"; 4  ...... 5 }

  在Android中,提供了VideoView组件用于播放视频文件。想要使用VideoView组件播放视频,首先须要在布局文件中建立该组件,而后在Activity中获取该组件,并应用其setVideoPath()方法或setVideoURI()方法加载要播放的视频,最后调用start()方法来播放视频。另外,VideoView组件还提供了stop()和pause()方法,用于中止或暂停视频的播放。架构

  在APP中,VideoView的典型简单使用以下:app

1     mMediaController =new MediaController(this); 2     mVideoView = (VideoView) findViewById(R.id.videoView); 3     mVideoView.setVideoPath("/sdcard/1080P24FPS.mp4"); // 设置档案路径
4     mVideoView.setMediaController(mMediaController); // 设置播放器的控制器
5     mVideoView.start(); // 开始播放

  先看看效果就是下面这个样子,短短几行代码一个播放器就作好了,还能够进行暂停,快进,快退,进度条控制。dom

  PS:VideoView还提供许多其余播放控制API,在此不作重点介绍,以上代码也仅仅是我的demo,不免有误,谨慎参考使用。ide

2、 VideoView中setVideoPath的处理

  任何华丽的语言都不如source code来的简单直接,上代码:
函数

 1     /**
 2  * Sets video path.  3  *  4  * @param path the path of the video.  5      */
 6     public void setVideoPath(String path) {  7  setVideoURI(Uri.parse(path));  8  }  9 
10     /**
11  * Sets video URI. 12  * 13  * @param uri the URI of the video. 14      */
15     public void setVideoURI(Uri uri) { 16         setVideoURI(uri, null); 17  } 18 
19     /**
20  * Sets video URI using specific headers. 21  * 22  * @param uri the URI of the video. 23  * @param headers the headers for the URI request. 24  * Note that the cross domain redirection is allowed by default, but that can be 25  * changed with key/value pairs through the headers parameter with 26  * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value 27  * to disallow or allow cross domain redirection. 28      */
29     public void setVideoURI(Uri uri, Map<String, String> headers) { 30         mUri = uri; 31         mHeaders = headers; 32         mSeekWhenPrepared = 0; 33         openVideo(); // openVideo的处理,让最终的处理权交给了MediaPlayer
34  requestLayout(); 35  invalidate(); 36     }

  通过setVideoPath(String path) --> setVideoURI(Uri uri) --> setVideoURI(Uri uri, Map<String, String> headers) 的调用流程,程序最终来到了openVideo()这一函数中:
oop

 1     private void openVideo() {  2  ......  3             mMediaPlayer = new MediaPlayer();  4 
 5  mMediaPlayer.setDataSource(mContext, mUri, mHeaders);  6  mMediaPlayer.setDisplay(mSurfaceHolder);  7             
 8  mMediaPlayer.prepareAsync();  9  ....... 10     }

  在上面的代码中能够清楚的看到,咱们首先new 了一个MediaPlayer类的对象,而后去setDataSource,到这里VideoView::openVideo的处理让最终的处理权交给了MediaPlayer。接下来咱们就进入MediaPlayer的世界.
布局

3、 MediaPlayer的世界

  3.1 new MediaPlayer对象过程

  首先关注MediaPlayer对象的建立过程,这也是分析android源码的一个基本要求。依次经过Java --> JNI(libmedia_jni.so) -- > Frameworks(libmedia.so)的处理流程。

  MediaPlayer.java 构造函数,这一部分在 Android MediaPlayer架构 -- 前言小知识点(一)也有分析

    public MediaPlayer() { super(new AudioAttributes.Builder().build()); Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } mTimeProvider = new TimeProvider(this); mOpenSubtitleSources = new Vector<InputStream>(); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ native_setup(new WeakReference<MediaPlayer>(this)); }

  能够看到,在使用VideoView中到建立MediaPlayer会通过:new VideoView——> new MediaPlayer ——>native_setup 这样一个典型的对象创建过程,并传递到JNI。

  native_setup主要用于本地C++层的对象的创建,在JNI代码(frameworks\base\media\jni\android_media_MediaPlayer.cpp)中能够找到对应的native函数:

 1 static void
 2 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)  3 {  4     ALOGV("native_setup");  5     sp<MediaPlayer> mp = new MediaPlayer(); // 实例化一个native MediaPlayer(frameworks\av\media\libmedia\mediaplayer.cpp)
 6     if (mp == NULL) {  7         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");  8         return;  9  } 10 
11     // create new listener and give it to MediaPlayer
12     sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); 13     mp->setListener(listener); 14 
15     // Stow our new C++ MediaPlayer in an opaque field in the Java object.
16  setMediaPlayer(env, thiz, mp); 17 }

  进入JNI作android_media_MediaPlayer_native_setup处理:sp<MediaPlayer> mp = new MediaPlayer() 这个native MediaPlayer会去和media service进行交互实现真正的播放功能,使得最终处理进入C++的世界。   

  3.2 setDataSource过程

  MediaPlayer java class中提供了多种setDataSource方法来设置不一样的URI播放流,在此咱们以播放本地档案为例来介绍处理流程:

  VideoView::setVideoURI() --> MediaPlayer::setDataSource(mContext, mUri, mHeaders); --> MediaPlayer::setDataSource(uri.toString()) --> MediaPlayer::setDataSource(path, null, null) --> MediaPlayer::setDataSource(fd) --> setDataSource(fd, 0, 0x7ffffffffffffffL) --> _setDataSource(fd, offset, length)

  最后会调到 _setDataSource(fd, offset, length),看这个方法被声明为 native method

1     private native void _setDataSource(MediaDataSource dataSource) 2           throws IllegalArgumentException, IllegalStateException;

  在JNI层咱们找到该方法对应的JNI method实现:

1     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},

    android_media_MediaPlayer_setDataSourceFD()方法定义以下:

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." ); }

  上面这段代码能够看到最终调用了status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)

//*********************************************************************************************************************************************************************

  MediaPlayer的C++代码位于/frameworks/av/media/libmedia/mediaplayer.cpp, 编译后造成一个libmedia.so。

  下面来看这个API的处理,接下去都只分析framework层的C++的处理流

 

 1 status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)  2 {  3     ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);  4     status_t err = UNKNOWN_ERROR;  5     const sp<IMediaPlayerService>& service(getMediaPlayerService());  6     if (service != 0) {  7         sp<IMediaPlayer> player(service->create(this, mAudioSessionId));  8         if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
 9             (NO_ERROR != player->setDataSource(fd, offset, length))) { 10  player.clear(); 11  } 12         err = attachNewPlayer(player); 13  } 14     return err; 15 }

 

  典型的Binder C/S架构,获取MediaPlayerService的proxy,经过MediaPlayerService来建立一个player,而后对这个player调用setDataSource

  3.3 MediaPlayerService的工做

  启动与获取

  MediaPlayerService同其余的Binder Service同样,做为一个server对外提供服务,它是在mediaserver中启动的:

  /frameworks/av/media/mediaserver/main_mediaserver.cpp

 

 1 int main(int argc __unused, char **argv __unused)  2 {  3  signal(SIGPIPE, SIG_IGN);  4 
 5     sp<ProcessState> proc(ProcessState::self());  6     sp<IServiceManager> sm(defaultServiceManager());  7     ALOGI("ServiceManager: %p", sm.get());  8  InitializeIcuOrDie();  9     MediaPlayerService::instantiate(); //启动MediaPlayerService
10  ResourceManagerService::instantiate(); 11  registerExtensions(); 12     ProcessState::self()->startThreadPool(); 13     IPCThreadState::self()->joinThreadPool(); 14 }

   在/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中对instantiate()方法的定义:

1 void MediaPlayerService::instantiate() { 2     defaultServiceManager()->addService( 3             String16("media.player"), new MediaPlayerService()); 4 }

 

   在上面这段代码中咱们注册了一个名为“media.player"的Binder Service,也就是MediaPlayerService,以后就能够经过 binder = sm->getService(String16("media.player"));来请求这个服务了

   Player的建立

  获取MediaPlayerService后就要去create player: sp<IMediaPlayer> player(service->create(this, mAudioSessionId));

  create请求处理:

相关文章
相关标签/搜索