• 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"/> 

     
     

     

     

  • 相关阅读:
    Linux 进程间通信(包含一个经典的生产者消费者实例代码)
    Linux多进程编程实例
    web开发中的mysql使用
    Linux 网络编程中的read和write函数正确的使用方式
    Linux 使用tcpdump观察arp通信过程
    Linux 开启echo等服务
    Linux send和recv
    自己动手写http服务器——主程序(三)
    自己动手写http服务器——线程池(一)
    http请求报文格式和响应报文格式
  • 原文地址:https://www.cnblogs.com/SongYongQian/p/7852382.html
Copyright © 2020-2023  润新知