3.使用MediaPlayer+SurfaceView播放视频文件

使用MediaPlayer+SurfaceView播放视频文件

MediaPlayer 主要用于播放音频,没有提供图像输出界面,所以需要借助其他的组件来显示 MediaPlayer 播放的图像,可以使用 SurfaceView 来显示。

Surface 和 SurfaceHolder | Android 开源项目 | Android Open Source Project (google.cn)

Surface 、SurfaceHolder和SurfaceView

Surface 对象使应用能够渲染要在屏幕上显示的图像。通过 SurfaceHolder 接口,应用可以编辑和控制 Surface。

Surface

Surface 是一个接口,供生产方与消耗方交换缓冲区。

SurfaceHolder

SurfaceHolder 是系统用于与应用共享 Surface 所有权的接口。与 Surface 配合使用的一些客户端需要 SurfaceHolder,因为用于获取和设置 Surface 参数的 API 是通过 SurfaceHolder 实现的。一个 SurfaceView 包含一个 SurfaceHolder。

与 View 交互的大多数组件都涉及到 SurfaceHolder。

SurfaceView

SurfaceView 是一个组件,可以像对待其他任何 View 一样对其进行操作,但 SurfaceView 的内容是透明的。

当 SurfaceView 的 View 组件即将变得可见时,框架会要求 SurfaceControl 从 SurfaceFlinger 请求新的 surface。如需在创建或销毁 Surface 时收到回调,请使用 SurfaceHolder 接口。默认情况下,新创建的 Surface 放置在应用界面 Surface 的后面。您可以替换默认的 Z 轴顺序,将新的 Surface 放在前面。

当 Activity 启动时,您将按以下顺序获得回调:

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

如果点击“返回”,您会获得:

  1. onPause()
  2. surfaceDestroyed()(在 Surface 消失前调用)

这句话照应了前面的 获取和设置 Surface 参数的 API 是通过 SurfaceHolder 实现

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().

通过SurfaceHolder.Callback让Surface可

三者的理解

什么是surfaceview?和surface有区别吗?SurfaceHolder和他们的关系是怎样? - 知乎 (zhihu.com)

Canvas是Java层构建的数据结构,是给View用的画布。ViewGroup会把自己的Canvas拆分给子View。View会在onDraw方法里将图形数据绘制在它获得的Canvas上。

而Surface是Native层构建的数据结构,是给SurfaceFlinger用的画布。它是直接被用来绘制到屏幕上的数据结构。

开发者一般所用的View都是在Canvas进行绘制,然后最顶层的View(通常是DecorView)的Canvas的数据信息会转换到一个Surface上。SurfaceFlinger会将各个应用窗口的Surface进行合成,然后绘制到屏幕上。

那为什么会有一个SurfaceView呢?

这是因为View的测量(Measure),布局(Layout)以及绘制(Draw)的计算量比较大。计算完以后再从Canvas转换成Surface中数据,然后再绘制屏幕,这个流程比较耗时。对于常规的UI绘制不会有什么问题,但是像Camera的预览以及视频的播放这样的应用场景来说就不可接受了。

SurfaceView就是为了解决这个问题。SurfaceView内容不再是绘制在Canvas上,而是直接绘制在其持有的一个Surface上。由于省去了很多步骤,其绘制性能大大提高。而SurfaceView本身只是用来控制这个Surface的大小和位置而已。

surfaceview 和surfaceholder的理解和使用-CSDN博客

通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未创建调用该函数会返回null,在
unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。

在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。

当在Canvas中绘制完成后,调用函数unlockCanvasAndPost(Canvas canvas)来通知系统Surface已经绘制完成,这样系统会把绘制完的内容显示出来。

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系_surfaceholder callback2 callback-CSDN博客

引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。

通过SurfaceView就可以看到Surface的部分或者全部的内容。

SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器。提供访问和控制SurfaceView背后的Surface 相关的方法。

Coding

public class MediaPlayerVideoActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback {
    /**
     * MediaPlayer + SurfaceView 播放视频
     */
    private static final String TAG = "MediaPlayerVideoActivity";

    private Button mStartButton;
    private Button mPauseButton;
    private Button mStopButton;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private MediaPlayer mPlayer = null;
    private boolean isRelease = false;   //判断是否MediaPlayer是否释放的标志

    public static Intent newIntent(Context packageContext) {
        Intent intent = new Intent(packageContext, MediaPlayerVideoActivity.class);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_player_vedio);

        bindViews();
    }

    private void bindViews() {
        mStartButton = (Button) findViewById(R.id.btn_media_player_video_start);
        mPauseButton = (Button) findViewById(R.id.btn_media_player_video_pause);
        mStopButton = (Button) findViewById(R.id.btn_media_player_video_stop);

        mStartButton.setOnClickListener(this);
        mPauseButton.setOnClickListener(this);
        mStopButton.setOnClickListener(this);

        mSurfaceView = (SurfaceView) findViewById(R.id.sfv_media_player_video);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setFixedSize(320, 220);   //显示的分辨率,不设置为视频默认
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_media_player_video_start:
                if (isRelease) {
                    loadVideo();
                    isRelease = false;
                }
                mPlayer.start();
                mStartButton.setEnabled(false);
                mPauseButton.setEnabled(true);
                mStopButton.setEnabled(true);
                break;
            case R.id.btn_media_player_video_pause:
                mPlayer.pause();
                mStartButton.setEnabled(true);
                mPauseButton.setEnabled(false);
                mStopButton.setEnabled(false);
                break;
            case R.id.btn_media_player_video_stop:
                mPlayer.reset();
                isRelease = true;
                // stop() 之后如果想再次播放,就得再次prepare()
                mStartButton.setEnabled(true);
                mPauseButton.setEnabled(false);
                mStopButton.setEnabled(false);
                break;
        }
    }

    @SuppressLint("LongLogTag")
    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        mPlayer = new MediaPlayer();
        loadVideo();
    }

    private void loadVideo() {
        // 播放raw目录下文件
//        mPlayer = MediaPlayer.create(MediaPlayerVideoActivity.this, R.raw.big_buck_bunny);

        // 播放 本地资源

        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        File file = new File("//sdcard/Movies/lesson.mp4");
        try {
            mPlayer.setDataSource(getApplicationContext(), Uri.parse(file.getAbsolutePath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            mPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mPlayer.setDisplay(mSurfaceHolder);    //设置显示视频显示在SurfaceView上
    }


    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        if (mPlayer.isPlaying()) {
            mPlayer.stop();
        }
        mPlayer.release();
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值