由于工作中经常用到视频多媒体相关系列操作,在此总结一下这个视频播放的使用,本例使用系统自带VideoView和SurfaceView两种使用方法进行视频播放,VideoView对视频进行播放实际上内部还是用过SurfaceView和MediaPlayer来实现的,是android已经包装好可以直接使用的视频播放View而已。
这边只记录主要的和需要注意的事项:
SurfaceView方式关键代码:
public class SurfaceViewActivity extends Activity {
private EditText nameText;
private String path;
// MediaPlayer实际上主要用于音频输出,SurfaceView主要作用于显示MediaPlayer输出的界面
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private boolean pause;
private int position;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.surfaceview);
mediaPlayer = new MediaPlayer();
nameText = (EditText) this.findViewById(R.id.filename);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
// 把输送给surfaceView的视频画面,直接显示到屏幕上,不要维持它自身的缓冲区
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceView.getHolder().setFixedSize(176, 144);
surfaceView.getHolder().setKeepScreenOn(true);// 设置播放时打开屏幕
surfaceView.getHolder().addCallback(new SurfaceCallback());
}
private final class SurfaceCallback implements Callback {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
if (position > 0 && path != null) {
play(position);
position = 0;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mediaPlayer.isPlaying()) {
position = mediaPlayer.getCurrentPosition();
mediaPlayer.stop();
}
}
}
@Override
protected void onDestroy() {
mediaPlayer.release();
mediaPlayer = null;
super.onDestroy();
}
public void mediaplay(View v) {
switch (v.getId()) {
case R.id.playbutton:
String filename = nameText.getText().toString();
if (filename.startsWith("http")) {
// path = filename;
path = "http://baishi.baidu.com/watch/07744898579139394846.html?page=videoMultiNeed";
play(0);
} else {
File file = new File(Environment.getExternalStorageDirectory(), filename);
if (file.exists()) {
// path = file.getAbsolutePath();
path = "/mnt/sdcard/X1_Video/泊车辅助.mp4";
play(0);
} else {
path = null;
Toast.makeText(this, R.string.filenoexsit, 1).show();
}
}
break;
case R.id.pausebutton:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
pause = true;
} else {
if (pause) {
mediaPlayer.start();
pause = false;
}
}
break;
case R.id.resetbutton:
if (mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(0);
} else {
if (path != null) {
play(0);
}
}
break;
case R.id.stopbutton:
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
break;
}
}
private void play(int position) {
try {
mediaPlayer.reset();
// 设置资源路径
mediaPlayer.setDataSource(path);
// mediaPlayer.setDataSource(MainActivity.this, Uri.parse("/mnt/sdcard/X1_Video/泊车辅助.mp4"));
// 设置播放时的图像输出用surfaceView来显示
mediaPlayer.setDisplay(surfaceView.getHolder());
mediaPlayer.prepare();// 缓冲
// mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new PrepareListener(position));
} catch (Exception e) {
e.printStackTrace();
}
}
private final class PrepareListener implements OnPreparedListener {
private int position;
public PrepareListener(int position) {
this.position = position;
}
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();// 播放视频
if (position > 0)
mediaPlayer.seekTo(position);
}
}
}
注意事项:MediaPlayer实际上主要用于音频输出,SurfaceView主要作用于显示MediaPlayer输出的界面。
VideoView方式关键代码:
public class VideoActivity extends Activity implements OnClickListener {
private final String SDCARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/X1_Video";// /mnt/sdcard
private ImageView mBack, mHome, mPlay, mPause;
private MyVideoView mVideoView = null;
private MediaController mMediaController = null;
private TextView mFileName, mCurrTime, mTotalTime;
private SeekBar mVideoSeekBar;
private int mDuration, mCurrPosi;
private boolean isCompletion = false;
/**
* 作用 update SeekbarView 和 update time、
*/
private Handler mTimerHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
mCurrPosi = mVideoView.getCurrentPosition();
mCurrTime.setText(stringForTime(mCurrPosi));
mVideoSeekBar.setProgress((mCurrPosi * mVideoSeekBar.getMax()) / mDuration);
if (!isCompletion) {// 正在播放,还没播放完
mTimerHandler.sendEmptyMessageDelayed(0, 1000);
} else {// 播放完时
mPlay.setVisibility(View.VISIBLE);
mPause.setVisibility(View.GONE);
mVideoSeekBar.setProgress(0);
mCurrTime.setText("00:00");
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_video);
String fileName = getIntent().getStringExtra("file_name");
final String filePath = SDCARD_PATH + File.separator + fileName + ".mp4";
mBack = (ImageView) findViewById(R.id.im_back_to_activity);
mBack.setOnClickListener(VideoActivity.this);
mHome = (ImageView) findViewById(R.id.im_back_to_home);
mHome.setOnClickListener(VideoActivity.this);
mPlay = (ImageView) findViewById(R.id.im_play_icon);
mPlay.setOnClickListener(VideoActivity.this);
mPause = (ImageView) findViewById(R.id.im_pause_icon);
mPause.setOnClickListener(VideoActivity.this);
mFileName = (TextView) findViewById(R.id.tv_play_view_headtext);
mFileName.setText(fileName);
mCurrTime = (TextView) findViewById(R.id.tv_play_schedule_head);
mTotalTime = (TextView) findViewById(R.id.tv_play_schedule_end);
mVideoView = (MyVideoView) findViewById(R.id.videoview);
mMediaController = new MediaController(this);
// mVideoView.setMediaController(mMediaController);
// 显示控制栏
mMediaController.setPrevNextListeners(new OnClickListener() {
@Override
/**
* 下一曲
*/
public void onClick(View v) {
}
}, new OnClickListener() {
@Override
/**
* 上一曲
*/
public void onClick(View v) {
}
});
mMediaController.hide();
// mVideoView.setVideoPath(filePath);
mVideoView.setVideoURI(android.net.Uri.parse(filePath));
// mVideoView.setVideoURI(Uri.parse("http://10.0.2.2:8080/X1_Video/res/movie/泊车辅助.mp4"));
mVideoView.requestFocus();// 请求音频聚焦
try {
mCurrPosi = mVideoView.getCurrentPosition();
mCurrTime.setText(stringForTime(mCurrPosi));
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepare();
mDuration = mediaPlayer.getDuration();
mTotalTime.setText(stringForTime(mDuration));
} catch (IllegalArgumentException | SecurityException | IllegalStateException | IOException e) {
e.printStackTrace();
}
mVideoSeekBar = (SeekBar) findViewById(R.id.sb_play_seekbar);
mVideoSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
private int mProgress;
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mVideoView.seekTo(mProgress * (mDuration / seekBar.getMax()));
mTimerHandler.sendEmptyMessageDelayed(0, 1000);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mTimerHandler.removeMessages(0);
isCompletion = false;
}
@Override
/**
* 最大进度值100
*/
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mProgress = progress;
}
});
mVideoView.setOnCompletionListener(new OnCompletionListener() {
@Override
/**
* 播放完成时回调
*/
public void onCompletion(MediaPlayer mp) {
isCompletion = true;
// mTimerHandler.removeMessages(0);若是此处采用remove,
// mCurrTime进度会比总时长少一秒
}
});
mVideoView.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (null != mVideoView)
mVideoView.start();
}
});
mTimerHandler.sendEmptyMessageDelayed(0, 1000);
}
// 将长度positon转换为时间
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
StringBuilder mFormatBuilder = new StringBuilder();
mFormatBuilder.setLength(0);
Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.im_back_to_activity:
case R.id.im_back_to_home:
VideoActivity.this.finish();
break;
case R.id.im_play_icon:
if (mVideoView != null) {
mVideoView.start();
mTimerHandler.sendEmptyMessageDelayed(0, 1000);
mPlay.setVisibility(View.GONE);
mPause.setVisibility(View.VISIBLE);
isCompletion = false;
}
break;
case R.id.im_pause_icon:
if (mVideoView.isPlaying()) {
mVideoView.pause();
mTimerHandler.removeMessages(0);
mPlay.setVisibility(View.VISIBLE);
mPause.setVisibility(View.GONE);
}
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mVideoView.isPlaying()) {
mVideoView.stopPlayback();
mTimerHandler.removeMessages(0);
}
// 显示底部导航栏
Intent hide = new Intent();
hide.setAction("com.asir.appwidget.provider.show");
hide.putExtra("current_app_id", -1);
sendBroadcast(hide);
}
}
再使用中可能要获取视频文件当前播放时间 和 视频总时长,首先官方并没有提供获取当前播放时间的方法,只有个getCurrentPosition()方法,可以获取当前播放的进度。代码如下:
mVideoView.getCurrentPosition()
我们可以根据播放进度来获取当前播放时间,转化方法如下:
/**
* 将进度positon转换为时间
* @param position
* @return
*/
private String getTimebyCurrentPosition(int position) {
int totalSeconds = position / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
StringBuilder mFormatBuilder = new StringBuilder();
mFormatBuilder.setLength(0);
Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
至于获取总时长VideoView提供一个getDuration()方法,实际上内部也是使用了mediaPlayer.getDuration(),但是此方法一直都是返回-1,我们这边用另外一种方法来获取,代码如下:
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepare();
mDuration = mediaPlayer.getDuration();
另外VideoView一般跟自带的控制菜单MediaController一起使用,具体使用方式如下:
mMediaController = new MediaController(this);
mVideoView.setMediaController(mMediaController);
// 显示控制栏
mMediaController.setPrevNextListeners(new OnClickListener() {
@Override
/**
* 下一曲
*/
public void onClick(View v) {
}
}, new OnClickListener() {
@Override
/**
* 上一曲
*/
public void onClick(View v) {
}
});
mMediaController.hide();
不管是使用哪种方式,都要请求音频聚焦,这里涉及到车载导航系统中一个切源的概念。
最后就是若要播放网络视频,只需要将设置的路劲path换用成设置URI方式即可,具体代码如下:
// 播放本地视频
// mVideoView.setVideoPath(filePath);
mVideoView.setVideoURI(android.net.Uri.parse(filePath));
// 播放网络视频
// mVideoView.setVideoURI(Uri.parse("http://10.0.2.2:8080/X1_Video/res/movie/泊车辅助.mp4"));
实际上内部还是使用了mediaPlayer,如下:
// 设置资源路径
mediaPlayer.setDataSource(path);
// mediaPlayer.setDataSource(MainActivity.this, Uri.parse("/mnt/sdcard/X1_Video/泊车辅助.mp4"));