Android --- 多媒体应用(使用VideoView、TextureView播放视频)

时间:2022-05-10 03:18:33

在Android中提供了一个VideoView播放视频,用于播放视频文件,要想使用VideoView组件播放视频,首先需要在布局文件中创建该组件,然后在Activity中获取该组件,并用 setVideoPath() 方法或 setVideoURI() 方法加载要播放的视频,最后调用VideoView组件的 start() 方法来播放视频。


在android中,还提供了一个可以与VideoView组件结合使用的MediaContraller组件。

MediaContraller组件用于通过图形控制界面来控制视频的播放.

/**
 * //初始化
 * playVideo=(VideoView)findViewById(R.id.video_view);
 * MediaController mc=new MediaController(this);// 媒体控制器
 * // MediaController与VideoView关联
 * mc.setAnchorView(playVideo);
 * // VideoView与MediaController关联
 * playVideo.setMediaController(mc);
 * //调用播放
 * String path=new File(Environment.getExternalStorageDirectory(),
 *      "bhhd.mp4").getAbsolutePath();
 * System.out.println("path:"+path);
 * playVideo.setVideoPath(path);
 * playVideo.start();
 */

public class VideoViewActivity extends AppCompatActivity implements View.OnClickListener {
    VideoView videoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_view);
        videoView = (VideoView) findViewById(R.id.video_view);
        //设置视频播放路径
        videoView.setVideoPath("/mnt/sdcard/Movies/an_xiang.mp4");
        //设置播放控制器
        videoView.setMediaController(new MediaController(this));
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_stop).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_play:
                videoView.start();
                break;
            case R.id.btn_pause:
                videoView.pause();
                break;
            case R.id.btn_stop:
                videoView.stopPlayback();
                break;
        }
    }
}


结合 TextureView 定义视频播放器

需要监听 Surface 创建完毕时才可以播放

private void init() {
        setSurfaceTextureListener(new SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                mSurface = new Surface(surface);
               start();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                stop();
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

MediaPlayer 需要使用 setSurface(Surface) 方法来设置画面显示的位置 , 如果时 SurfaceView 需要用 setDisplay(SurfaceHolder)

private void initPlayer() {
        if (null == mPlayer) {
            mPlayer = new MediaPlayer();
            //设置准备监听
            mPlayer.setOnPreparedListener(onPreparedListener);
            //设置错误监听
            mPlayer.setOnErrorListener(onErrorListener);
            //设置播放完毕监听
            mPlayer.setOnCompletionListener(onCompletionListener);
            mPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            //设置视频画面显示位置
            mPlayer.setSurface(mSurface);
        } else {
            mPlayer.reset();
        }
        //1、让MediaPlayer进入idle状态
    }

    //开始解析文件
    private void setDataAndPlay(String path) {
        isStop = false;
        //设置播放的数据源
        try {
            mPlayer.setDataSource(path);
            //2、进入initialized状态
            //准备
            mPlayer.prepareAsync();
        } catch (Exception e) {
            isStop = true;
        }
    }

视频播放在准备完毕时可以获取视频宽高

int videoWidth = mp.getVideoWidth();
int videoHeight = mp.getVideoHeight();

测试代码Demo

View包下新建一view类

public class XVideoView extends TextureView {
    private static final int MSG_UPDATE_POSITION = 1;
    private MediaPlayer mPlayer;
    //视频画面显示的载体
    private Surface mSurface;
    private int mVideoWidth;
    private int mVideoHeight;
    //更新的进度条
    private SeekBar seekBar;
    private boolean isStop = true;
    private int playPos;
    private Uri uri; // http: file:

    public XVideoView(Context context) {
        super(context);
        init();
    }

    public XVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setSurfaceTextureListener(new SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                mSurface = new Surface(surface);
               start();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                stop();
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

    private void initPlayer() {
        if (null == mPlayer) {
            mPlayer = new MediaPlayer();
            //设置准备监听
            mPlayer.setOnPreparedListener(onPreparedListener);
            //设置错误监听
            mPlayer.setOnErrorListener(onErrorListener);
            //设置播放完毕监听
            mPlayer.setOnCompletionListener(onCompletionListener);
            mPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            //设置视频画面显示位置
            mPlayer.setSurface(mSurface);
        } else {
            mPlayer.reset();
        }
        //1、让MediaPlayer进入idle状态
    }

    //开始解析文件
    private void setDataAndPlay(String path) {
        isStop = false;
        //设置播放的数据源
        try {
            mPlayer.setDataSource(path);
            //2、进入initialized状态
            //准备
            mPlayer.prepareAsync();
        } catch (Exception e) {
            isStop = true;
        }
    }

    private MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            isStop = false;
            //3、进入onPrepared状态(准备完毕),可以去start、pause、seekTo、stop
            //获取总时间
            seekBar.setMax(100);
            if (playPos > 0) {
                mp.seekTo(playPos);
            }
            mp.start();
            //更新播放时间
            mHandler.sendEmptyMessageDelayed(MSG_UPDATE_POSITION, 200);
        }
    };

    //(解码)错误监听
    private MediaPlayer.OnErrorListener onErrorListener = new MediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            mHandler.removeMessages(MSG_UPDATE_POSITION);
            playPos = mp.getCurrentPosition();
            mp.stop();
            mp.reset(); //进入idle状态
            isStop = true;
            return true;
        }
    };

    //播放完毕监听
    private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            mHandler.removeMessages(MSG_UPDATE_POSITION);
        }
    };

    private MediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
            mVideoWidth = mp.getVideoWidth();
            mVideoHeight = mp.getVideoHeight();
            if (mVideoWidth != 0 && mVideoHeight != 0) {
                requestLayout();
            }
        }
    };

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_UPDATE_POSITION) {
                if (mPlayer != null) {
                    int pos = mPlayer.getCurrentPosition(); //获取当前的播放时间
                    int duration = mPlayer.getDuration();
                    int position = 100 * pos / duration;
                    seekBar.setProgress(position);
                    sendEmptyMessageDelayed(MSG_UPDATE_POSITION, 200);
                }
            }
        }
    };

    public void start() {
        if (mPlayer == null) {
            initPlayer();
            String sc = uri.getScheme();
            if ("http".equalsIgnoreCase(sc) || "https".equalsIgnoreCase(sc)) {
                new DownloadTack().execute(uri.toString(), "/mnt/sdcard/" + System.currentTimeMillis() + ".mp3");
            }else{
                setDataAndPlay(uri.toString());
            }
        } else {
            if (!mPlayer.isPlaying()) {
                mPlayer.start();
                mHandler.sendEmptyMessageDelayed(MSG_UPDATE_POSITION, 200);
            }
        }
    }

    public void pause() {
        if (null != mPlayer && mPlayer.isPlaying()) {
            mHandler.removeMessages(MSG_UPDATE_POSITION);
            mPlayer.pause();
        }
    }

    public void stop() {
        if (null != mPlayer) {
            mHandler.removeMessages(MSG_UPDATE_POSITION);
            mPlayer.stop();
            mPlayer.release();
            mPlayer = null;
            seekBar.setProgress(0);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
        //        + MeasureSpec.toString(heightMeasureSpec) + ")");

        int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
        int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
        if (mVideoWidth > 0 && mVideoHeight > 0) {

            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
                // the size is fixed
                width = widthSpecSize;
                height = heightSpecSize;

                // for compatibility, we adjust size based on aspect ratio
                if (mVideoWidth * height < width * mVideoHeight) {
                    //Log.i("@@@", "image too wide, correcting");
                    width = height * mVideoWidth / mVideoHeight;
                } else if (mVideoWidth * height > width * mVideoHeight) {
                    //Log.i("@@@", "image too tall, correcting");
                    height = width * mVideoHeight / mVideoWidth;
                }
            } else if (widthSpecMode == MeasureSpec.EXACTLY) {
                // only the width is fixed, adjust the height to match aspect ratio if possible
                width = widthSpecSize;
                height = width * mVideoHeight / mVideoWidth;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    height = heightSpecSize;
                }
            } else if (heightSpecMode == MeasureSpec.EXACTLY) {
                // only the height is fixed, adjust the width to match aspect ratio if possible
                height = heightSpecSize;
                width = height * mVideoWidth / mVideoHeight;
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    width = widthSpecSize;
                }
            } else {
                // neither the width nor the height are fixed, try to use actual video size
                width = mVideoWidth;
                height = mVideoHeight;
                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // too tall, decrease both width and height
                    height = heightSpecSize;
                    width = height * mVideoWidth / mVideoHeight;
                }
                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // too wide, decrease both width and height
                    width = widthSpecSize;
                    height = width * mVideoHeight / mVideoWidth;
                }
            }
        } else {
            // no size yet, just adopt the given spec sizes
        }
        setMeasuredDimension(width, height);
    }

    public SeekBar getSeekBar() {
        return seekBar;
    }

    public void setSeekBar(SeekBar seekBar) {
        this.seekBar = seekBar;
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                if (mPlayer != null && mPlayer.isPlaying()) {
                    mHandler.removeMessages(MSG_UPDATE_POSITION);
                }
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (mPlayer != null) {
                    int pos = seekBar.getProgress(); //获取拖动到的位置
                    //计算该进度对应的时间轴
                    int currentPos = pos * mPlayer.getDuration() / 100;
                    mPlayer.seekTo(currentPos); //设置播放位置
                    if (mPlayer.isPlaying()) {
                        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_POSITION, 200);
                    }
                }
            }
        });
    }

    class DownloadTack extends AsyncTask<String, Integer, Object> implements MediaDownloader.OnDownloadListener {
        int lenght; //记录总大小
        String savePath;

        @Override
        protected Object doInBackground(String... params) {
            new MediaDownloader(this).download(params[0], params[1]);
            return null;
        }

        @Override
        public void onStart(int size, String path) {
            lenght = size;
            this.savePath = path;
        }

        @Override
        public void onDownloading(int currentSize) {
            //更新进度条
            if (currentSize >= 1024 && isStop) {
                //开始解析
                setDataAndPlay(savePath);
            }
            int currentRate = 100 * currentSize / lenght;
            publishProgress(currentRate);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            seekBar.setSecondaryProgress(values[0]);
        }
    }

    public Uri getUri() {
        return uri;
    }

    public void setUri(Uri uri) {
        this.uri = uri;
    }
}

 注册TextureViewPlayVideoActivity类

public class TextureViewPlayVideoActivity extends AppCompatActivity implements View.OnClickListener {
    private XVideoView xVideoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_texture_view_play_video);
        xVideoView = (XVideoView) findViewById(R.id.player_view);
        SeekBar seekBar = (SeekBar) findViewById(R.id.seek_bar);
        xVideoView.setSeekBar(seekBar);
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_stop).setOnClickListener(this);
//        xVideoView.setUri(Uri.parse("/mnt/sdcard/Movies/an_xiang.mp4"));
        xVideoView.setUri(Uri.parse("http://192.168.18.251:8080/MediaServer/video/gee.mp4"));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_play:
                xVideoView.start();
                break;
            case R.id.btn_pause:
                xVideoView.pause();
                break;
            case R.id.btn_stop:
                xVideoView.stop();
                break;
        }
    }
}