之前用Android系统提供的视频播放组件VideoView实现了播放视频的功能,当然,Android播放视频不只有一种实现方式,今天就记录另一种播放视频的demo。
1、效果图:
这里简单一点,就放一张图片吧,视频资源前两天不小心删掉了,也就显示不出来,不过功能是没问题的。
2、分析代码:
主界面SurfaceViewVideoActivity.java:
/**
* SurfaceViewVideoActivity 2016-10-13
*/
public class SurfaceViewVideoActivity extends Activity implements
OnClickListener {
private Button startplay_btn;// 播放
private Button pauseplay_btn;// 暂停
private Button restartplay_btn;// 继续播放
private SurfaceView surfaceview_video;// 播放显示
private ImageView imageplay;
private SeekBar seekbar;
/**
* 播放路径
*/
private String pathString;
/**
* surfaceView播放控制
*/
private SurfaceHolder surfaceHolder;
/**
* 播放视频
*/
private MediaPlayer mediaPlayer = null;
/**
* 屏幕的宽度和高度
*/
private int screenWidth, screenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_surfaceviewvideo_activity);
initView();
loadData();
}
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
switch (view.getId()) {
case R.id.startplay_btn:
playVideo(pathString);
break;
case R.id.pauseplay_btn:
mediaPalyPause();
break;
case R.id.restartplay_btn:
mediaPlayRestart();
break;
}
}
// 加载数据
@SuppressLint("NewApi")
private void loadData() {
imageplay.setVisibility(View.VISIBLE);
surfaceview_video.setVisibility(View.GONE);
String path = null;
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// 存在获取外部文件路径
path = Environment.getExternalStorageDirectory().getPath();
} else {
// 不存在获取内部存储
path = Environment.getDataDirectory().getPath();
}
pathString = path + "/test.mp4";
Bitmap bitmap = null;
// 获取视频缩略图
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(pathString);
bitmap = retriever.getFrameAtTime();
if (bitmap != null) {
imageplay.setImageBitmap(bitmap);
}
} catch (Exception e) {
// TODO: handle exception
}
}
// 初始化视图
@SuppressWarnings("deprecation")
private void initView() {
startplay_btn = (Button) findViewById(R.id.startplay_btn);
pauseplay_btn = (Button) findViewById(R.id.pauseplay_btn);
startplay_btn.setOnClickListener(this);
pauseplay_btn.setOnClickListener(this);
surfaceview_video = (SurfaceView) findViewById(R.id.surfaceview_video);
imageplay = (ImageView) findViewById(R.id.imageplay);
restartplay_btn = (Button) findViewById(R.id.restartplay_btn);
restartplay_btn.setOnClickListener(this);
seekbar = (SeekBar) findViewById(R.id.seekbar);
// 设置surfaceHolder
surfaceHolder = surfaceview_video.getHolder();
// 设置Holder类型,该类型表示surfaceView自己不管理缓存区
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// 设置surface回调
surfaceHolder.addCallback(new SurfaceCallBack());
// 设置拖动事件
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
if (mediaPlayer != null) {
if (progress >= 0) {
// 如果是用户手动拖动控件,则设置视频跳转。
if (fromUser) {
// 设置进度
mediaPlayer.seekTo(progress);
}
}
}
}
});
}
// 设置SurfaceView的回调监听
private class SurfaceCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// surfaceView被创建
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// surfaceView销毁,同时销毁mediaPlayer
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
/**
* 播放视频
*/
public void playVideo(String path) {
imageplay.setVisibility(View.GONE);
surfaceview_video.setVisibility(View.VISIBLE);
if (mediaPlayer == null) {
// 初始化MediaPaly
mediaPlayer = new MediaPlayer();
// 开始时设置为0
seekbar.setProgress(0);
mediaPlayer.seekTo(0);
// 重置MediaPlay
mediaPlayer.reset();
try {
// 设置视频资源路径
mediaPlayer.setDataSource(path);
// 设置媒体加载完成监听
mediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 设置声音效果
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 设置播放完成监听
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer arg0) {
// 播放完成
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
// mediaPlayer.seekTo(0);
}
imageplay.setVisibility(View.VISIBLE);
surfaceview_video.setVisibility(View.GONE);
}
});
// 设置媒体加载完成监听
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer arg0) {
// 开始播放
mediaPlayer.start();
// 视频显示到屏幕上SurfaceView
mediaPlayer.setDisplay(surfaceHolder);
// 设置视频播放的大小
setDisplayMetrics();
// 设置SeekBar的最大进度
seekbar.setMax(mediaPlayer.getDuration());
// 开启线程 刷新进度条
new Thread(runnable).start();
}
});
}
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
seekbar.setProgress(mediaPlayer.getCurrentPosition());
}
}
};
/**
* 暂停
*/
private void mediaPalyPause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
/**
* 继续播放
*/
public void mediaPlayRestart() {
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
// 销毁时,如果正在播放,则停止。
mediaPlayer.stop();
mediaPlayer = null;
}
}
// 设置video surface窗口的大小
public void setDisplayMetrics() {
// 获取屏幕的宽度和高度
DisplayMetrics displayMetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
screenWidth = displayMetrics.widthPixels;
screenHeight = displayMetrics.heightPixels;
// 获取视频的大小
// 获取视频的宽度和高度
int width = mediaPlayer.getVideoWidth();
int height = mediaPlayer.getVideoHeight();
if (width > screenWidth || height > screenHeight) {
// 计算出宽高的倍数
float vWidth = (float) width / (float) screenWidth;
float vHeight = (float) height / (float) screenHeight;
// 获取最大的倍数值,按大数值进行缩放
float max = Math.max(vWidth, vHeight);
// 计算出缩放大小,取接近的正值
width = (int) Math.ceil((float) width / max);
height = (int) Math.ceil((float) height / max);
}
// 设置SurfaceView的大小并居中显示
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
width, height);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
surfaceview_video.setLayoutParams(layoutParams);
// 全屏
// RelativeLayout.LayoutParams layoutParams = new
// RelativeLayout.LayoutParams(screenWidth,
// screenHeight);
// layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
// surfaceview_video.setLayoutParams(layoutParams);
}
}
上面的代码中有几行是可以作为新知识学习的:
Bitmap bitmap = null;
// 获取视频缩略图
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(pathString);
bitmap = retriever.getFrameAtTime();
if (bitmap != null) {
imageplay.setImageBitmap(bitmap);
}
} catch (Exception e) {
// TODO: handle exception
}
这里的功能是截取到视频的第一帧,展示到图片控件上,我们也可以试着获取视频的中间某一部分,指定那一刻时间点,方法为retriever.getFrameAtTime(timeUs)
和retriever.getFrameAtTime(timeUs, option)
,
看源码参数介绍:
意思是指该时间点的播放帧会被截取到,系统不会保证指定的这个位置position
正好是一帧,如果这个位置不正好是播放帧,那么会截取距离此位置最近的一帧用来返回bitmap
。时间格式不正确的话,这个参数就会被忽略。这个功能很有用哦,像progressbar
拖拽时在其上面出现的每一帧都是对应的progressbar
的位置,有很多播放器像搜狐都是用的这个思路吧。
另外,其实MediaPlayer还可以设置声音的效果:
// 设置声音效果
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
这里还有其他效果是可以获取的:
警告、音乐、通知、铃声、系统声音等等。
最后有一个记录播放位置的变量,封装到Constants.java
里边了:
public class Constants {
/**
* 记录播放位置
*/
public static int playPosition=-1;
}
布局文件 layout_surfaceviewvideo_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/startplay_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<Button
android:id="@+id/pauseplay_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停" />
<Button
android:id="@+id/restartplay_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="继续播放" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp" >
<ImageView
android:id="@+id/imageplay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:visibility="gone" />
<SurfaceView
android:id="@+id/surfaceview_video"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</RelativeLayout>
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true" />
</LinearLayout>
</LinearLayout>
上述代码就是实现视频播放的全过程了,简单易学