说起播放器,首先想到的是VideoView。当我们对VedioView初始化结束后,要设置播放资源,这时调用了setVideoURI(Uri uri),查看该方法:
/**
* Sets video URI.
*
* @param uri the URI of the video.
*/
public void setVideoURI(Uri uri) {
setVideoURI(uri, null);
}
/**
* Sets video URI using specific headers.
*
* @param uri the URI of the video.
* @param headers the headers for the URI request.
* Note that the cross domain redirection is allowed by default, but that can be
* changed with key/value pairs through the headers parameter with
* "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
* to disallow or allow cross domain redirection.
*/
public void setVideoURI(Uri uri, Map<String, String> headers) {
mUri = uri;
mHeaders = headers;
mSeekWhenPrepared = 0;
openVideo();//打开资源
requestLayout();
invalidate();
}
此时我们查看openVideo()方法,发现里面创建了一个MediaPlayer对象,然后使用该对象设置了一系列的监听。
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioAttributes(mAudioAttributes);
mMediaPlayer.setScreenOnWhilePlaying(true);
//进行异步prepare,prepare()适合播放本地视频,但是最好都用prepareAsync()
mMediaPlayer.prepareAsync();
继续查看prepareAsync()方法
/**
* Prepares the player for playback, asynchronously.
*
* After setting the datasource and the display surface, you need to either
* call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
* which returns immediately, rather than blocking until enough data has been
* buffered.
*
* @throws IllegalStateException if it is called in an invalid state
*/
public native void prepareAsync() throws IllegalStateException;
发现prepareAsync()是一个native方法,由此我们发现MediaPlayer主要负责和底层库进行交流,通过JNI调用底层C代码进行视频解码,MediaPlayer中加载了media_jni。
static {
System.loadLibrary("media_jni");
native_init();
}
当底层执行完毕后,MediaPlayer中的EventHandler会收到MEDIA_PREPARED的消息,这时我们VideoView注册的监听便能执行。准备好之后当然是开始播放了,我们调用VideoView的start()方法,该方法又调用了MediaPlayer的start()方法,最终还是调用了native方法。其它方法也是如此。
private native void _start() throws IllegalStateException;
当视频可以播放的时候,我们需要一个可以控制视频播放暂停的面板。系统恰好为我们提供了一个。
* MediaController,继承自FrameLayout。通过
videoView.setMediaController(new MediaController(context))将gaiview同VideoView进行了关联。但MediaController是如何控制视频的播放呢?查看MediaController发现,该类中定义了一个接口:
public interface MediaPlayerControl {
void start();
void pause();
int getDuration();
int getCurrentPosition();
void seekTo(int pos);
boolean isPlaying();
int getBufferPercentage();
boolean canPause();
boolean canSeekBackward();
boolean canSeekForward();
/**
* Get the audio session id for the player used by this VideoView. This can be used to
* apply audio effects to the audio track of a video.
* @return The audio session, or 0 if there was an error.
*/
int getAudioSessionId();
}
而VideoView是MediaPlayerControl的实现类。通过mMediaController.setMediaPlayer(this)将自己传递给MediaController。之后的操作就可以通过接口回调传递过来了。
//和MediaController建立连接
private void attachMediaController() {
if (mMediaPlayer != null && mMediaController != null) {
mMediaController.setMediaPlayer(this);
View anchorView = this.getParent() instanceof View ?
(View)this.getParent() : this;
//添加锚点视图
mMediaController.setAnchorView(anchorView);
mMediaController.setEnabled(isInPlaybackState());
}
}
补充:VideoView继承自SurfaceView。SurfaceView默认使用双缓冲技术,支持在子线程程中绘制图像,这样就不会阻塞主线程了,所以它更适合游戏和视频播放器的开发

被折叠的 条评论
为什么被折叠?



