好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用Mediaplayer来播放音乐,这次就讲讲使用Mediaplayer来和SurfaceView配合播放一个视频流媒体。MediaPlayer不仅可以播放视频,还可以与SurfaceView的配合,SurfaceView主要用于显示MediaPlayer播放的视频流媒体的画面渲染,两者可以一起协同播放视频。
基础维护
先来看下要实现的界面:
如果你看过上篇文章,就发现其实很简单的就是多了一个进度条,还有一个就是SurfaceView,就是下面那块黑色区域;
布局文件代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.googlevideo.MainActivity" > <EditText android:id="@+id/edit_musicPath" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="输入MV的路径" /> <SeekBar android:id="@+id/seekBar_video" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btn_Play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="playEvent" android:text="播放" /> <Button android:id="@+id/btn_Pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="pauseEvent" android:text="暂停" /> <Button android:id="@+id/btn_Stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="stopEvent" android:text="停止" /> <Button android:id="@+id/btn_Replay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="replayEvent" android:text="重播" /> </LinearLayout> <SurfaceView android:id="@+id/surface_video" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Demo实现
实现Demo之前应该讲讲视频播放的原理,先确定视频的格式,这个和解码相关,不同的格式视频编码不同,然后通过编码格式进行解码,最后得到一帧一帧的图像,并把这些图像快速的显示在界面上,即为播放一段视频。SurfaceView在Android中就是完成这个功能的。SurfaceView是配合MediaPlayer使用的,MediaPlayer也提供了相应的方法设置SurfaceView显示图片,只需要为MediaPlayer指定SurfaceView显示图像即可。它的完整签名:void setDisplay(SurfaceHolder sh)。它需要传递一个SurfaceHolder对象,SurfaceHolder可以理解为SurfaceView装载需要显示的一帧帧图像的容器,它可以通过SurfaceHolder.getHolder()方法获得。使用MediaPlayer配合SurfaceView播放视频的步骤与播放使用MediaPlayer播放MP3大体一致,只需要额外设置显示的SurfaceView即可。
先准备一段能播放的视频:
播放视频效果图:
代码如下:
editText = (EditText) findViewById(R.id.edit_musicPath); pathString = editText.getText().toString().trim(); File file = new File(pathString); if (file.exists()) { try { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(pathString); //通过SurfaceView获取的Holder mediaPlayer.setDisplay(holder); mediaPlayer.prepare(); mediaPlayer.start(); btn_PlayButton.setEnabled(false); //设置Bar的最大值 int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定时器更新进度条 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500); //视频播放完之后重新设置显示 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub btn_PlayButton.setEnabled(true); } }); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { Toast.makeText(this, "Sorry,你输入的路径有问题,请仔细检查", Toast.LENGTH_SHORT) .show(); }
SeekBar的使用:
//设置Bar的最大值 int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定时器更新进度条 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500);
其中holder是SurfaceHolder,在onCreate中获取:
surfaceView = (SurfaceView) findViewById(R.id.surface_video); holder = surfaceView.getHolder(); //兼容4.0以后的手机版本,本身是不维护的 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
如果正在播放视频,最小化的时候是会有声音的,需要在回调函数中处理一下:
holder.addCallback(new Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub Log.i(tag, "销毁了Holder"); if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { currentPosition=mediaPlayer.getCurrentPosition(); mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; timer.cancel(); timeTask.cancel(); timer=null; timeTask=null; } } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub Log.i(tag,"创建了Holder"); if (currentPosition>0) { try { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(pathString); //通过SurfaceView获取的Holder mediaPlayer.setDisplay(holder); mediaPlayer.prepare(); mediaPlayer.start(); mediaPlayer.seekTo(currentPosition); btn_PlayButton.setEnabled(false); //视频播放完之后重新设置显示 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub btn_PlayButton.setEnabled(true); } }); int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定时器更新进度条 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500); } catch (Exception e) { // TODO: handle exception } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub Log.i(tag,"改变了Holder"); } });
进度条是SeekBar,如果需要快进或者后退的时候是需要将焦点赋值给mediaPlayer的:
seekBarVideo=(SeekBar) findViewById(R.id.seekBar_video); seekBarVideo.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub int position=seekBar.getProgress(); if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { mediaPlayer.seekTo(position); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub } });
暂停事件:
public void pauseEvent(View view) { if (btn_PauseButton.getText().equals("继续")) { mediaPlayer.start(); btn_PauseButton.setText("暂停"); return; } if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); btn_PauseButton.setText("继续"); } }
停止事件:
public void stopEvent(View view) { if (mediaPlayer != null && mediaPlayer.isPlaying()) { btn_PlayButton.setEnabled(true); mediaPlayer.stop(); // 释放mediaplayer否则的话会占用内存 mediaPlayer.release(); mediaPlayer = null; } btn_PauseButton.setText("暂停"); btn_PlayButton.setEnabled(true); }
重播事件:
public void replayEvent(View view) { surfaceView.setVisibility(View.VISIBLE); if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.seekTo(0); } else { playEvent(view); } // 重播的时候应该设置播放的状态 btn_PlayButton.setEnabled(true); }