SurfaceView获取本地视频播放

时间:2023-03-09 15:21:34
SurfaceView获取本地视频播放

1、定义

可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。

它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。

2、实现

首先继承SurfaceView并实现SurfaceHolder.Callback接口
使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始(Surface—表面,这个概念在 图形编程中常常被提到。基本上我们可以把它当作显存的一个映射,写入到Surface 的内容
                   
  可以被直接复制到显存从而显示出来,这使得显示速度会非常快),而在Surface 被销毁之前必须结束。所以Callback
中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

需要重写的方法

 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

     //在surface的大小发生改变时激发

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在创建时激发,一般在这里调用画图的线程。

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //销毁时激发,一般在这里将画图的线程停止、释放。

整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口
----> SurfaceView.getHolder()获得SurfaceHolder对象
---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布---->
Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas
canvas)结束锁定画图,并提交改变,将图形显示。

3、SurfaceHolder
这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。
4、实例:

主布局:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.MainActivity"> <Button
android:id="@+id/buffer_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceVie使用缓冲刷新UI几面"/> <Button
android:id="@+id/video_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceView播放视频"/> </LinearLayout>

主布局的Activity:

 public class MainActivity extends AppCompatActivity implements View.OnClickListener {

     protected Button mBufferBtn;
protected Button mVideoBtn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
initView();
} @Override
public void onClick(View view) {
Intent intent = new Intent();
if (view.getId() == R.id.buffer_btn) {
intent.setClass(this, BufferActivity.class);
} else if (view.getId() == R.id.video_btn) {
intent.setClass(this, VideoActivity.class);
}
startActivity(intent);
} private void initView() {
mBufferBtn = (Button) findViewById(R.id.buffer_btn);
mBufferBtn.setOnClickListener(MainActivity.this);
mVideoBtn = (Button) findViewById(R.id.video_btn);
mVideoBtn.setOnClickListener(MainActivity.this);
}
}
SurfaceVie使用缓冲刷新UI几面:
布局
 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="net.bwie.surfaceview.activity.BufferActivity"> <SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </RelativeLayout>

Activity

 // SurfaceView配合子线程,使用双缓冲刷新UI界面
// SurfaceHolder:用于管理缓冲区Surface的类(生命周期)
public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback { protected SurfaceView mSurfaceView;
private SurfaceHolder mHolder; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_buffer);
initView();
initSurfaceHolder();
} // 初始化Surface的管理者
private void initSurfaceHolder() {
mHolder = mSurfaceView.getHolder();
// 添加管理生命周期的接口回调
mHolder.addCallback(this);
} private void initView() {
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
} // 缓冲区创建
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d("1507", "surfaceCreated");
new DrawThread(this).start();
} // 缓冲区内容改变(子线程渲染UI的过程)
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("1507", "surfaceChanged");
} // 缓冲区销毁
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("1507", "surfaceDestroyed");
} // 绘制UI的子线程
private static class DrawThread extends Thread { // private BufferActivity mActivity; // 使用弱引用持有Activity的实例,
// 当Activity销毁时,当前线程会释放Activity,避免Activity无法释放导致的内存泄漏
private WeakReference<BufferActivity> mWeakReference; public DrawThread(BufferActivity activity) {
// mActivity = activity;
mWeakReference = new WeakReference<BufferActivity>(activity);
} @Override
public void run() {
super.run(); // 通过弱引用获取持有的Activity实例
BufferActivity activity = mWeakReference.get(); if (activity == null) {
return;
} // 获取SurfaceView的盖度
int height = activity.mSurfaceView.getHeight(); // 创建画笔
Paint paint = new Paint();
paint.setColor(Color.GREEN);// 画笔颜色
paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px
paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心
paint.setAntiAlias(true);// 设置是否抗锯齿 Canvas canvas = null;
for (int i = 0; i < height; i += 5) { // 获取Surface中的画布
canvas = activity.mHolder.lockCanvas();// 锁定画布 // 当SurfaceView随着Activity销毁时,缓冲区Surface也会随着销毁,无法获取缓冲区的画布
if (canvas == null) {
return;
} canvas.drawColor(Color.RED);// 设置画布背景覆盖之前的残影
// 使用画笔在画布上绘制指定形状
canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔 // 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示
activity.mHolder.unlockCanvasAndPost(canvas); } }
} }
SurfaceView播放视频:
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
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="net.bwie.surfaceview.activity.VideoActivity"> <Button
android:id="@+id/play_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/> <net.bwie.surfaceview.widget.MyVideoSurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>

Activity

/**
* 1、获取播放源
* 2、准备SurfaceView
* 3、多媒体交给MediaPlayer处理
*/
public class VideoActivity extends AppCompatActivity implements View.OnClickListener { protected MyVideoSurfaceView mSurfaceView;
protected Button mPlayBtn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_video);
initView(); } // 运行、可见
@Override
protected void onStart() {
super.onStart();
} // 可交互
@Override
protected void onResume() {
super.onResume();
} private void play() {
String videoPath = Environment.getExternalStorageDirectory().getPath() +
"/VID_20171117_144736.3gp";// 外部存储根路径 mSurfaceView.playVideo(videoPath);
} private void initView() {
mSurfaceView = (MyVideoSurfaceView) findViewById(R.id.surface_view);
mPlayBtn = (Button) findViewById(R.id.play_btn);
mPlayBtn.setOnClickListener(VideoActivity.this);
} @Override
public void onClick(View view) {
if (view.getId() == R.id.play_btn) {
play();
}
}
}

MyVideoSurfaceView类:

 public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

     private SurfaceHolder mHolder;
private MediaPlayer mMediaPlayer; public MyVideoSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs); init();
} private void init() {
// 获取Surface换朝哪个区的持有者
mHolder = getHolder();
mHolder.addCallback(this);
} // 设置播放源
public void playVideo(String path) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
} try {
// 设置播放源
mMediaPlayer.setDataSource(path);
// 设置多媒体的显示部分:使用SurfaceHolder渲染画面
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
} } @Override
public void surfaceCreated(SurfaceHolder holder) { } @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaPlayer.release();
mMediaPlayer = null;
} }

别忘了加权限:

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>