Android中播放视频一般有三种实现方式,第一种是调用其他视频播放应用;第二种是使用系统Android封装的VideoView;第三种使用MediaPlayer和SurfaceView。第一种方式当然是最简单的,两句话就搞定;第二种方式和第三种方式实质是一样的,VideoView就是系统利用MediaPlayer和SurfaceView封装的一个视频播放类,一般的视频播放的话,用这个就可以了;第三种方式稍微复杂些,不过也是最自由的。本篇博客将分享如何使用MediaPlayer和SurfaceView来实现本地视频的播放,并为视频播放添加一些播放控制。全景视频的播放同样用到MediaPlayer和“SurfaceView”,不过这里的“SurfaceView”是继承自SurfaceView的GLSurfaceView,它的使用也远远比SurfaceView复杂,所以这部分我们暂且不表,先利用本篇博客的例子熟悉一下MediaPlayer和SurfaceView。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.renkangchen.surfaceviewplayertest.MainActivity">
<SurfaceView
android:id="@+id/mysurfaceview"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
FrameLayout>
布局很简单,添加一个SurfaceView控件即可,为了控制视频播放,还可以添加相应的播放(暂停)按钮,进度条之类的控件。视频播放控制会在后面的博文中分享。
提到MediaPlayer,就不得不“祭”出这张图了:
Android开发者官网的这张图很清晰地展示了MediaPlayer的生命周期,这是使用MediaPlayer时经常需要参考的一张图。生命周期控制得不好的话,很容易会出现各种bug。使用
MediaPlayer myMediaPlayer = new MediaPlayer();
来获得一个MediaPlayer的实例,还可以使用静态方法create来创建实例,两种方法的差异在于:create方法中直接指定了要播放的文件,不需要使用 setDataSource来设置,也不需要使用prepare()或者prepareAsync()来进入prepare状态。
这里我使用了默认构造方法来获得实例,所以还需要使用:
Uri videoUri = Uri.parse("http:/XXXXX/sample.mp4");
try {
myMediaPlayer.setDataSource(MainActivity.this, videoUri);
myMediaPlayer.prepareAsync();
}catch(Exception e) {
e.printStackTrace();
}
这样的方法来设置要播放的资源并异步进行播放器准备。
如果播放器没有准备好就直接开始播放,会出现播放器错误,那么如何知道播放器是否准备好呢?
myMediaPlayer.setOnPreparedListener(this);
给播放器添加上准备完成监听,然后在回调函数中做播放处理就可以了:
@Override
public void onPrepared(MediaPlayer mp) {
myMediaPlayer.start();
...
}
不再使用时,记得释放MediaPlayer:
if(null != myMediaPlayer )
myMediaPlayer.release();
myMediaPlayer = null;
}
所以MediaPlayer的使用流程为:创建实例,设置资源,就绪,播放,释放。
SurfaceView和一般的View的区别在于SurfaceView允许在非UI线程改变其内容,而一般的View不可以。因为SurfaceView可能在Activity的OnDestroy()之前被销毁,所以需要对此作处理。
SurfaceView使用SurfaceHolder来进行管理,
SurfaceHolder mySurfaceHolder = mySurfaceview.getHolder();
接着对SurfaceHolder添加回调addCallback,
mySurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Uri videoUri = Uri.parse("http:/XXXXX/sample.mp4");
try {
myMediaPlayer.setDataSource(MainActivity.this, videoUri);
myMediaPlayer.prepareAsync();
}catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if(null != myMediaPlayer )
myMediaPlayer.release();
myMediaPlayer = null;
}
});
}
surfaceCreated在surface创建时调用,我们把MediaPlayer的“准备工作放在这里”;surfaceChanged在surface尺寸变化时调用;surfaceDestroyed在surface被销毁时调用,我们在这里释放MediaPlayer。
完成了上述的工作,只需要修改MediaPlayer的Prepared回调
@Override
public void onPrepared(MediaPlayer mp) {
myMediaPlayer.start();
myMediaPlayer.setDisplay(mySurfaceHolder);
myMediaPlayer.setScreenOnWhilePlaying(true);
mySurfaceHolder.setKeepScreenOn(true);
}
通过setDisplay(mySurfaceHolder)设置显示视频到surface;setScreenOnWhilePlaying(true)设置surfaceview保持在屏幕上。
为了便于调试,也可以为MediaPlayer设置setOnErrorListener(this)来监听播放过程中出现的错误,如果播放网络视频,还可以设置setOnBufferingUpdateListener(this)来监听缓冲情况。
MediaPlayer
android视频播放(二) 利用android原生的MediaPlayer+SurfaceView