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