好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用Mediaplayer来播放音乐,这次就讲讲使用Mediaplayer来和SurfaceView配合播放一个视频流媒体。MediaPlayer不仅可以播放视频,还可以与SurfaceView的配合,SurfaceView主要用于显示MediaPlayer播放的视频流媒体的画面渲染,两者可以一起协同播放视频。
基础维护
先来看下要实现的界面:
如果你看过上篇文章,就发现其实很简单的就是多了一个进度条,还有一个就是SurfaceView,就是下面那块黑色区域;
布局文件代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <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即可。
先准备一段能播放的视频:
播放视频效果图:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //设置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中获取:
1 2 3 4 | surfaceView = (SurfaceView) findViewById(R.id.surface_video); holder = surfaceView.getHolder(); //兼容4.0以后的手机版本,本身是不维护的 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
如果正在播放视频,最小化的时候是会有声音的,需要在回调函数中处理一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 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的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 } }); |
暂停事件:
1 2 3 4 5 6 7 8 9 10 11 | 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( "继续" ); } } |
停止事件:
1 2 3 4 5 6 7 8 9 10 11 | 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 ); } |
重播事件:
1 2 3 4 5 6 7 8 9 10 | public void replayEvent(View view) { surfaceView.setVisibility(View.VISIBLE); if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.seekTo( 0 ); } else { playEvent(view); } // 重播的时候应该设置播放的状态 btn_PlayButton.setEnabled( true ); } |