本文系做者本身學習之所用,文章內容僅出自做者拙劣之思考,問題之處煩請不吝指教。html
MediaPlayer 能被用來控制音/視頻文件或流媒體的回放。Android中以MediaPlayer類做爲音視頻播放的基礎類,圍繞着他開展了一系列的處理。學習一個新的模塊,最簡單的步驟就是找到一個典型的應用程序,經過它的實現,來分析整個模塊的數據流和控制流。典型的MediaPlayer在Java處的接口包括視頻播放類VideoView以及音頻專用MediaPlayer類。java
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
任何華麗的語言都不如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的世界.
佈局
首先關注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。
啓動與獲取
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請求處理: