一 前言
在前面已经介绍过使用
MediaPlayer播放音频,那么本篇就来看看用它是如何播放视频。
二 视频播放
播放视频步骤:
(1)创建MediaPlayer对象实例;(2)在界面布局中定义SurfaceView组件,或者在程序中创建SurfaceView对象,给SurfaceView添加callback监听;(3)给MediaPlayer 设置播放监听;(4) 调用setDataSource方法装载资源文件,并将播放的视频图像输送到SurfaceView(通过surfaceView.getHolder获取SurfaceHolder对象实例,然后把它作为参数,调用MediaPlayer的setDisplay方法就可以了);(5)调用 prepare()或prepareAsync()方法来做播放前的准备工作;(6)调用MediaPlayer的start、stop、pause等方法控制播放过程;
1 创建MediaPlayer对象实例代码如下:
private MediaPlayer player;
@AfterViews
void initData(){
player = new MediaPlayer();
// loadRaw();
}
2 给SurfaceView添加callback监听
private void setHolder() {
//给SurfaceView添加CallBack监听
holder = surfaceView.getHolder();
//为了可以播放视频或者使用Camera预览,我们需要指定其Buffer类型
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new Callback() {
/**
* Activity进入后台会触发该方法
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Toast.makeText(MediaPlayerSurfaceActivity.this, "Surface被销毁", Toast.LENGTH_SHORT).show();
}
/**
*
* Activity进入前台会触发该方法
*
* 当SurfaceView中的Surface被创建的时候被调用*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
Toast.makeText(MediaPlayerSurfaceActivity.this, "Surface被创建", Toast.LENGTH_SHORT).show();
if (position > 0){
//在指定了MediaPlayer播放的容器后,我们就可以使用prepare或者prepareAsync来准备播放了
// try {
// player.prepare();
// } catch (IOException e) {
// e.printStackTrace();
// }
player.prepareAsync();
}
}
/** 当Surface尺寸等参数改变时触发 */
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
}
});
}
说明:position用来记录视频当前播放的位置。
3 给MediaPlayer 设置播放监听
@SuppressLint("NewApi") @SuppressWarnings("deprecation")
private void setPlayerListener() {
//设置播放完监听
player.setOnCompletionListener(new OnCompletionListener() {
/** 当MediaPlayer播放完成后触发 */
@Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(MediaPlayerSurfaceActivity.this, "播放完成", Toast.LENGTH_SHORT).show();
btn_start.setText("重播");
}
});
//播放错误监听
player.setOnErrorListener(new OnErrorListener() {
@TargetApi(Build.VERSION_CODES.CUPCAKE) @Override
public boolean onError(MediaPlayer mp, int what, int extra) {
switch (what) {
//播放器坏了
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Toast.makeText(MediaPlayerSurfaceActivity.this, "播放器坏了", Toast.LENGTH_SHORT).show();
break;
//未知错误
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Toast.makeText(MediaPlayerSurfaceActivity.this, "未知错误", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return false;
}
});
//详细监听
player.setOnInfoListener(new OnInfoListener() {
/** 当一些特定信息出现或者警告时触发 */
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch(what){
case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING: //视频交叉紊乱
Toast.makeText(MediaPlayerSurfaceActivity.this, "视频交叉紊乱", Toast.LENGTH_SHORT).show();
break;
case MediaPlayer.MEDIA_INFO_METADATA_UPDATE: //更新
Toast.makeText(MediaPlayerSurfaceActivity.this, "更新", Toast.LENGTH_SHORT).show();
break;
case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: //无法解码
Toast.makeText(MediaPlayerSurfaceActivity.this, "无法解码", Toast.LENGTH_SHORT).show();
break;
case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE: //不能搜索
Toast.makeText(MediaPlayerSurfaceActivity.this, "不能搜索", Toast.LENGTH_SHORT).show();
break;
}
return false;
}
});
//准备监听 如果是使用异步加载的方式必须要设置该监听,在onPrepared方法中调用start()方法
player.setOnPreparedListener(new OnPreparedListener() {
/** 当prepare完成后,该方法触发,在这里我们播放视频*/
@Override
public void onPrepared(MediaPlayer mp) {
if (position > 0){
play();
//从指定位置开始播放
player.seekTo(position);
position = 0;
}
player.start();
}
});
player.setOnSeekCompleteListener(new OnSeekCompleteListener() {
/** seek操作完成时触发 */
@Override
public void onSeekComplete(MediaPlayer mp) {
}
});
/** 当video大小改变时触发 这个方法在设置player的source后至少触发一次 */
player.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
}
});
}
说明:如果调用MediaPlayer调用prepareAsyn方法做播放前的准备(也就是异步的),那么MediaPlayer就是设置OnPreparedListener监听,实现onPrepared方法,只有在资源准备好的情况下,才能调用start方法播放资源,否则的话MediaPlayer是无法获取播放资源的。
4 给SurfaceView装载资源文件并将播放的视频图像输送到SurfaceView
void play() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
surfaceView.setForeground(null);
}
if (TextUtils.isEmpty(videoPath)){
return;
}
player.reset();
//将要播放的视频图像输送到surfaceView
player.setDisplay(holder);
try {
player.setDataSource(videoPath);
// player.prepare();
player.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
//首先取得video的宽和高
vWidth = player.getVideoWidth();
vHeight = player.getVideoHeight();
if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){
//如果video的宽或者高超出了当前屏幕的大小,则要进行缩放
float wRatio = (float)vWidth/(float)currDisplay.getWidth();
float hRatio = (float)vHeight/(float)currDisplay.getHeight();
//选择大的一个进行缩放
float ratio = Math.max(wRatio, hRatio);
vWidth = (int)Math.ceil((float)vWidth/ratio);
vHeight = (int)Math.ceil((float)vHeight/ratio);
//设置surfaceView的布局参数
surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight - 100));
//然后开始播放视频
// player.start();
}
// player.start();
}
说明:videoPath 为视频的本地地址,获取本地视频的方法如下:
/**
*
* 从媒体库中查询 获取视频缩略图 新视频增加后需要SDCard重新扫描才能给新增加的文件添加缩略图,灵活性差,而且不是很稳定,适合简单应用
* @createAuthor luzhenbang
* @updateAuthor
* @updateInfo (此处输入修改内容,若无修改可不写.)
* @param context
* @param contentResolver
* @return 视频集合 key : 视频路径 value :视频压缩后的位图
*/
@SuppressLint("NewApi")
public static HashMap<String , Bitmap> getVideoMapThumbnail(Context context, ContentResolver contentResolver) {
ContentResolver testcr = context.getContentResolver();
String[] projection = { MediaStore.Video.Media.DATA, MediaStore.Video.Media._ID, };
// String whereClause = MediaStore.Video.Media.DATA + " = '" + Videopath + "'";、
//查询多媒体数据库
Cursor cursor = testcr.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null,null, MediaStore.Video.Media.DEFAULT_SORT_ORDER);
int _id = 0;
String videoPath = "";
HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
if (cursor == null || cursor.getCount() == 0) {
return null;
}
if (cursor.moveToFirst()) {
int _idColumn = cursor.getColumnIndex(MediaStore.Video.Media._ID);
int _dataColumn = cursor.getColumnIndex(MediaStore.Video.Media.DATA);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
do {
//视频id
_id = cursor.getInt(_idColumn);
//视频路径
videoPath = cursor.getString(_dataColumn);
Bitmap bitmap = MediaStore.Video.Thumbnails.getThumbnail(contentResolver, _id, Images.Thumbnails.MINI_KIND,options);
bitmaps.put(videoPath, bitmap);
} while (cursor.moveToNext());
}
cursor.close();
return bitmaps;
}
工具类LocalVideoUtil。
5 调用prepare()或prepareAsync()方法来做播放前的准备工作
player.prepareAsync();
说明:这一步集成到上述第四步,这里不再累述。
6 调用MediaPlayer的start方法开始播放
player.start();
说明:start方法是在OnPreparedListener接口的onPrepared方法中调用,回头参考步骤4。