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、实例:

主布局:

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:app="http://schemas.android.com/apk/res-auto"
 5     xmlns:tools="http://schemas.android.com/tools"
 6     android:orientation="vertical"
 7     android:layout_width="match_parent"
 8     android:layout_height="match_parent"
 9     tools:context="net.bwie.surfaceview.MainActivity">
10 
11     <Button
12         android:id="@+id/buffer_btn"
13         android:layout_width="wrap_content"
14         android:layout_height="wrap_content"
15         android:text="SurfaceVie使用缓冲刷新UI几面"/>
16 
17     <Button
18         android:id="@+id/video_btn"
19         android:layout_width="wrap_content"
20         android:layout_height="wrap_content"
21         android:text="SurfaceView播放视频"/>
22 
23 </LinearLayout>

 

主布局的Activity:

 1 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 2 
 3     protected Button mBufferBtn;
 4     protected Button mVideoBtn;
 5 
 6     @Override
 7     protected void onCreate(Bundle savedInstanceState) {
 8         super.onCreate(savedInstanceState);
 9         super.setContentView(R.layout.activity_main);
10         initView();
11     }
12 
13     @Override
14     public void onClick(View view) {
15         Intent intent = new Intent();
16         if (view.getId() == R.id.buffer_btn) {
17             intent.setClass(this, BufferActivity.class);
18         } else if (view.getId() == R.id.video_btn) {
19             intent.setClass(this, VideoActivity.class);
20         }
21         startActivity(intent);
22     }
23 
24     private void initView() {
25         mBufferBtn = (Button) findViewById(R.id.buffer_btn);
26         mBufferBtn.setOnClickListener(MainActivity.this);
27         mVideoBtn = (Button) findViewById(R.id.video_btn);
28         mVideoBtn.setOnClickListener(MainActivity.this);
29     }
30 }
SurfaceVie使用缓冲刷新UI几面:
布局
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:app="http://schemas.android.com/apk/res-auto"
 5     xmlns:tools="http://schemas.android.com/tools"
 6     android:layout_width="match_parent"
 7     android:layout_height="match_parent"
 8     tools:context="net.bwie.surfaceview.activity.BufferActivity">
 9 
10     <SurfaceView
11         android:id="@+id/surface_view"
12         android:layout_width="match_parent"
13         android:layout_height="match_parent"/>
14 
15 </RelativeLayout>

Activity

  1 // SurfaceView配合子线程,使用双缓冲刷新UI界面
  2 // SurfaceHolder:用于管理缓冲区Surface的类(生命周期)
  3 public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback {
  4 
  5     protected SurfaceView mSurfaceView;
  6     private SurfaceHolder mHolder;
  7 
  8     @Override
  9     protected void onCreate(Bundle savedInstanceState) {
 10         super.onCreate(savedInstanceState);
 11         super.setContentView(R.layout.activity_buffer);
 12         initView();
 13         initSurfaceHolder();
 14     }
 15 
 16     // 初始化Surface的管理者
 17     private void initSurfaceHolder() {
 18         mHolder = mSurfaceView.getHolder();
 19         // 添加管理生命周期的接口回调
 20         mHolder.addCallback(this);
 21     }
 22 
 23     private void initView() {
 24         mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
 25     }
 26 
 27     // 缓冲区创建
 28     @Override
 29     public void surfaceCreated(SurfaceHolder holder) {
 30         Log.d("1507", "surfaceCreated");
 31         new DrawThread(this).start();
 32     }
 33 
 34     // 缓冲区内容改变(子线程渲染UI的过程)
 35     @Override
 36     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
 37         Log.d("1507", "surfaceChanged");
 38     }
 39 
 40     // 缓冲区销毁
 41     @Override
 42     public void surfaceDestroyed(SurfaceHolder holder) {
 43         Log.d("1507", "surfaceDestroyed");
 44     }
 45 
 46     // 绘制UI的子线程
 47     private static class DrawThread extends Thread {
 48 
 49 //        private BufferActivity mActivity;
 50 
 51         // 使用弱引用持有Activity的实例,
 52         // 当Activity销毁时,当前线程会释放Activity,避免Activity无法释放导致的内存泄漏
 53         private WeakReference<BufferActivity> mWeakReference;
 54 
 55         public DrawThread(BufferActivity activity) {
 56 //            mActivity = activity;
 57             mWeakReference = new WeakReference<BufferActivity>(activity);
 58         }
 59 
 60         @Override
 61         public void run() {
 62             super.run();
 63 
 64             // 通过弱引用获取持有的Activity实例
 65             BufferActivity activity = mWeakReference.get();
 66 
 67             if (activity == null) {
 68                 return;
 69             }
 70 
 71             // 获取SurfaceView的盖度
 72             int height = activity.mSurfaceView.getHeight();
 73 
 74             // 创建画笔
 75             Paint paint = new Paint();
 76             paint.setColor(Color.GREEN);// 画笔颜色
 77             paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px
 78             paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心
 79             paint.setAntiAlias(true);// 设置是否抗锯齿
 80 
 81             Canvas canvas = null;
 82             for (int i = 0; i < height; i += 5) {
 83 
 84                 // 获取Surface中的画布
 85                 canvas = activity.mHolder.lockCanvas();// 锁定画布
 86 
 87                 // 当SurfaceView随着Activity销毁时,缓冲区Surface也会随着销毁,无法获取缓冲区的画布
 88                 if (canvas == null) {
 89                     return;
 90                 }
 91 
 92                 canvas.drawColor(Color.RED);// 设置画布背景覆盖之前的残影
 93                 // 使用画笔在画布上绘制指定形状
 94                 canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔
 95 
 96                 // 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示
 97                 activity.mHolder.unlockCanvasAndPost(canvas);
 98 
 99 
100             }
101 
102 
103         }
104     }
105 
106 }
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类:

 1 public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
 2 
 3     private SurfaceHolder mHolder;
 4     private MediaPlayer mMediaPlayer;
 5 
 6     public MyVideoSurfaceView(Context context, AttributeSet attrs) {
 7         super(context, attrs);
 8 
 9         init();
10     }
11 
12     private void init() {
13         // 获取Surface换朝哪个区的持有者
14         mHolder = getHolder();
15         mHolder.addCallback(this);
16     }
17 
18 
19     // 设置播放源
20     public void playVideo(String path) {
21         if (mMediaPlayer == null) {
22             mMediaPlayer = new MediaPlayer();
23         }
24 
25         try {
26         // 设置播放源
27             mMediaPlayer.setDataSource(path);
28             // 设置多媒体的显示部分:使用SurfaceHolder渲染画面
29             mMediaPlayer.setDisplay(mHolder);
30             mMediaPlayer.prepare();
31             mMediaPlayer.start();
32         } catch (IOException e) {
33             e.printStackTrace();
34         }
35 
36     }
37 
38     @Override
39     public void surfaceCreated(SurfaceHolder holder) {
40 
41     }
42 
43     @Override
44     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
45 
46     }
47 
48     @Override
49     public void surfaceDestroyed(SurfaceHolder holder) {
50         mMediaPlayer.release();
51         mMediaPlayer = null;
52     }
53 
54 }

别忘了加权限:

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

 
 

 

 

原文地址:https://www.cnblogs.com/SongYongQian/p/7852382.html