SurfaceView播放器篇三

实现功能:
1、下次进入,从上一次播放位置开始播放;
2、来电保存视频进度;
3、视频未看部分不能快进;
4、适应视频大小,防止播放画面变形;
5、播放与暂停按钮;
6、按home键、查看最新打开应用键,back返回键后,再次回到或打开播放页面,不报错,能正常播放。

解决问题:
1、避免MediaPlayer error(-38, 0),MediaPlayer error(-19, 0);
2、避免Mediaplayer setVideoSurfaceTexture failed: -22问题;
3、避免进入onError回调函数后,Mediaplayer的start(),pause()方面无效果;
4、Mediaplayer onCompletion后 再次start后 再调用pause无暂停效果,还是会继续播放。
解决思路:
Mediaplayer和SurfaceView都重新new一个,一切重新开始。
实现代码:


import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.*;
import cn.rainfo.surfaceviewplayer.util.DisplayUtil;
import cn.rainfo.surfaceviewplayer.util.TimeUtil;

import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SurfaceViewPlayerActivity extends AppCompatActivity implements MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener, SurfaceHolder.Callback,SeekBar.OnSeekBarChangeListener,View.OnClickListener {

    FrameLayout fLyt;
    SurfaceView surfaceView;
    TextView duration_mmss_tv,position_mmss_tv;
    SeekBar position_seek_bar;
    ProgressBar progressBar;
    private SurfaceHolder holder;
    private MediaPlayer mMediaPlayer;
    private String mediaPath = "http://pvqnrhdsk.bkt.clouddn.com/%E6%B8%94%E8%88%9F%E6%99%9A%E5%94%B1.mp4";
    int[] resetXY;
    private int currentPosition,durationPosition,maxCurrentPosition;
    ScheduledExecutorService executorService;
    private TelephonyManager manager;
    private boolean videoCompletion;
    ImageView mPlay_pause_iv;
    private Handler progressHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(currentPosition>maxCurrentPosition){

                maxCurrentPosition=currentPosition;

            }else if (currentPosition<maxCurrentPosition){
                if(videoCompletion){
                    mMediaPlayer.start();
                }
            }
            position_mmss_tv.setText(TimeUtil.formatMmSs(currentPosition));
            position_seek_bar.setProgress(currentPosition);

        }
    };

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

        manager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
        // 对listen_call_state状态监听
        manager.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);

        fLyt = findViewById(R.id.fLyt);
        position_mmss_tv = findViewById(R.id.position_mmss_tv);
        duration_mmss_tv = findViewById(R.id.duration_mmss_tv);
        position_seek_bar = findViewById(R.id.position_seek_bar);
        progressBar = findViewById(R.id.progressBar);
        mPlay_pause_iv = findViewById(R.id.play_pause_iv);
        position_seek_bar.setOnSeekBarChangeListener(this);
        mPlay_pause_iv.setOnClickListener(this);

    }

    private void getProgress() {

        executorService= new ScheduledThreadPoolExecutor(1);
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {

                currentPosition=mMediaPlayer.getCurrentPosition();
                progressHandler.sendEmptyMessage(1);

            }
        },1,1,TimeUnit.SECONDS);
    }

    @Override
    public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {

    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        videoCompletion=true;
        mMediaPlayer.pause();
        mPlay_pause_iv.setImageResource(R.drawable.ac_video_play);
    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
//        Log.i("SurfaceViewPlayer", "onPrepared");
        mPlay_pause_iv.setImageResource(R.drawable.ac_video_pause);
        mMediaPlayer.seekTo(getLastCurrentPosition());
        mMediaPlayer.start();
        durationPosition=mMediaPlayer.getDuration();
        position_seek_bar.setMax(durationPosition);
        duration_mmss_tv.setText(TimeUtil.formatMmSs(durationPosition));
        getProgress();
    }

    @Override
    public void onVideoSizeChanged(MediaPlayer mediaPlayer, int videoX, int videoY) {
//        Log.i("SurfaceViewPlayer", "onVideoSizeChanged");
        if (resetXY == null) {

            resetXY = DisplayUtil.resetVideoXY(DisplayUtil.getDisplayX(SurfaceViewPlayerActivity.this), videoX, videoY);
        }
        if (surfaceView == null) {

            surfaceView = new SurfaceView(SurfaceViewPlayerActivity.this);
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(resetXY[0], resetXY[1]);
            fLyt.addView(surfaceView,0, layoutParams);
            holder = surfaceView.getHolder();
            holder.addCallback(this);
        }
    }

    /**
     * 错误常数
     * MEDIA_ERROR_IO
     * 文件不存在或错误,或网络不可访问错误
     * 值: -1004 (0xfffffc14)
     * MEDIA_ERROR_MALFORMED
     * 流不符合有关标准或文件的编码规范
     * 值: -1007 (0xfffffc11)
     * MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK
     * 视频流及其容器不适用于连续播放视频的指标(例如:MOOV原子)不在文件的开始.
     * 值: 200 (0x000000c8)
     * MEDIA_ERROR_SERVER_DIED
     * 媒体服务器挂掉了。此时,程序必须释放MediaPlayer 对象,并重新new 一个新的。
     * 值: 100 (0x00000064)
     * MEDIA_ERROR_TIMED_OUT
     * 一些操作使用了过长的时间,也就是超时了,通常是超过了3-5秒
     * 值: -110 (0xffffff92)
     * MEDIA_ERROR_UNKNOWN
     * 未知错误
     * 值: 1 (0x00000001)
     * MEDIA_ERROR_UNSUPPORTED
     * 比特流符合相关编码标准或文件的规格,但媒体框架不支持此功能
     * 值: -1010 (0xfffffc0e)
     * what int: 标记的错误类型:
     * MEDIA_ERROR_UNKNOWN
     * MEDIA_ERROR_SERVER_DIED
     * extra int: 标记的错误类型.
     * MEDIA_ERROR_IO
     * MEDIA_ERROR_MALFORMED
     * MEDIA_ERROR_UNSUPPORTED
     * MEDIA_ERROR_TIMED_OUT
     * MEDIA_ERROR_SYSTEM (-2147483648) - low-level system error.
     *
     * @param mediaPlayer
     * @param what
     * @param extra
     * @return
     */
    @Override
    public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {

        saveLastCurrentPosition();

        if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
            //媒体服务器挂掉了。此时,程序必须释放MediaPlayer 对象,并重新new 一个新的。
            Toast.makeText(SurfaceViewPlayerActivity.this,
                    "网络服务错误",
                    Toast.LENGTH_SHORT).show();
        } else if (what == MediaPlayer.MEDIA_ERROR_UNKNOWN) {

            if (extra == MediaPlayer.MEDIA_ERROR_IO) {
                //文件不存在或错误,或网络不可访问错误
                Toast.makeText(SurfaceViewPlayerActivity.this,
                        "网络文件错误",
                        Toast.LENGTH_SHORT).show();
            } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
                //超时
                Toast.makeText(SurfaceViewPlayerActivity.this,
                        "网络超时",
                        Toast.LENGTH_SHORT).show();
            } else {

                Toast.makeText(SurfaceViewPlayerActivity.this,
                        "未知错误" + extra,
                        Toast.LENGTH_SHORT).show();
            }
        }

        finish();
        return false;
    }

    @Override
    public boolean onInfo(MediaPlayer mediaPlayer, int what, int extra) {
        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
            progressBar.setVisibility(View.GONE);
            return true;
        }else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
            progressBar.setVisibility(View.GONE);
            return true;
        }else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
            progressBar.setVisibility(View.VISIBLE);
            return true;
        }

        return false;
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
//        Log.i("SurfaceViewPlayer", "surfaceCreated");
        mMediaPlayer.setDisplay(holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }

    private void startPlayVideo() {
        progressBar.setVisibility(View.VISIBLE);

        // 创建一个MediaPlayer对象
        mMediaPlayer = new MediaPlayer();

        // 设置播放的视频数据源
        try {
            mMediaPlayer.setDataSource(mediaPath);
        } catch (IOException e) {
            Toast.makeText(this, "视频地址错误", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }

        mMediaPlayer.setOnBufferingUpdateListener(this);
        // 设置相关的监听器
        mMediaPlayer.setOnCompletionListener(this);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        mMediaPlayer.setOnErrorListener(this);

        mMediaPlayer.setOnInfoListener(this);

        //播放时屏幕保持唤醒
        mMediaPlayer.setScreenOnWhilePlaying(true);

        // 播放准备,使用异步方式,配合OnPreparedListener
        mMediaPlayer.prepareAsync();

    }

    @Override
    protected void onPause() {
        super.onPause();
        saveLastCurrentPosition();
        if (mMediaPlayer != null) {
            releaseVideoPlayer();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        startPlayVideo();
    }

    private void releaseVideoPlayer(){
        mMediaPlayer.stop();
        mMediaPlayer.release();
        mMediaPlayer = null;
        fLyt.removeView(surfaceView);
        holder = null;
        surfaceView = null;
    }

    private void saveLastCurrentPosition(){
        SharedPreferences.Editor editor = getSharedPreferences("SurfaceViewPlayer", MODE_PRIVATE).edit();
        editor.putInt("lastCurrentPosition", currentPosition);
        editor.commit();
    }

    private int getLastCurrentPosition(){
        SharedPreferences sharedPreferences = getSharedPreferences("SurfaceViewPlayer", MODE_PRIVATE);
        return sharedPreferences.getInt("lastCurrentPosition", 0);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

        //b代表是否TrackingTouch,i代表当前进度;判断滑到中进度小于滑动前的视频播放最大进度
        if (b&&i<maxCurrentPosition){
            mMediaPlayer.seekTo(i);
        }

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        if(!videoCompletion&&seekBar.getProgress()>maxCurrentPosition){
            mMediaPlayer.seekTo(maxCurrentPosition);
            Toast.makeText(SurfaceViewPlayerActivity.this,"只能快进到此,未学习过的内容不能快进",Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.play_pause_iv:
                if(mMediaPlayer!=null) {

                    if(videoCompletion){
                        videoCompletion=false;
                        //MediaPlay onCompletion后 再次start后 再调用pause无暂停效果,还是会继续播放;所以采用先释放播放器资源,一切重新开始。
                        releaseVideoPlayer();
                        startPlayVideo();
                    }else {
                        if (mMediaPlayer.isPlaying()) {

                            mMediaPlayer.pause();
                            mPlay_pause_iv.setImageResource(R.drawable.ac_video_play);
                        } else {

                            mMediaPlayer.start();
                            mPlay_pause_iv.setImageResource(R.drawable.ac_video_pause);
                        }
                    }

                }
                break;

        }
    }


    private class MyPhoneStateListener extends PhoneStateListener {

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                //空闲
                case TelephonyManager.CALL_STATE_IDLE:
                    break;
                //来电
                case TelephonyManager.CALL_STATE_RINGING:
                    saveLastCurrentPosition();
                    break;
                //挂断
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    break;
            }
            super.onCallStateChanged(state, incomingNumber);
        }
    }

    @Override
    protected void onDestroy() {
        executorService.shutdown();
        super.onDestroy();
    }
}
<?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">

    <include
            android:id="@+id/inHeader"
            layout="@layout/include_header"/>
    <FrameLayout
            android:id="@+id/fLyt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="200dp">

        <ProgressBar
                android:id="@+id/progressBar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center" />
    </FrameLayout>

    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:padding="3dp">
        <ImageView android:id="@+id/play_pause_iv" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/ac_video_pause"/>
        <TextView
                android:layout_toRightOf="@+id/play_pause_iv"
                android:id="@+id/position_mmss_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="00:00"
                android:layout_centerVertical="true"/>

        <SeekBar
                android:id="@+id/position_seek_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_toLeftOf="@+id/duration_mmss_tv"
                android:layout_toRightOf="@+id/position_mmss_tv"
                android:layout_centerVertical="true"/>

        <TextView
                android:id="@+id/duration_mmss_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="00:00"
                android:layout_centerVertical="true"
                android:layout_marginRight="3dp"
                android:layout_toLeftOf="@+id/ivFullscreen"/>

        <ImageView
                android:id="@+id/ivFullscreen"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:src="@drawable/ac_video_play_fullscreen"/>
    </RelativeLayout>
</LinearLayout>

效果图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值