Android支持采用第9章讨论的3种视频播放方法来播放HTTP和RTSP视频。无须改变原来的源代码,就可以通过意图触发内置的媒体播放器活动或者使用VideoView类来播放任何形式的网络视频。只需要将HTTP或RTSP URL作为视频Uri,那么只要是支持的格式,它就可以工作。
1.VideoView网络视频播放器
下面是来自第9章的ViewTheVideo活动示例,其使用一个VideoView及一个指向YouTube移动站点视频的RTSP URL。唯一的变化是构造videoUri所传入的字符串。
1 package com.nthm.androidtestActivity; 2 3 import com.nthm.androidtest.R; 4 import android.app.Activity; 5 import android.net.Uri; 6 import android.os.Bundle; 7 import android.widget.MediaController; 8 import android.widget.VideoView; 9 10 public class ViewTheVideo extends Activity { 11 private VideoView vv; 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.viewthevideo); 16 vv=(VideoView) findViewById(R.id.VedioView); 17 vv.setMediaController(new MediaController(ViewTheVideo.this)); 18 Uri videoUri=Uri.parse("");//此处的路径下的视频必须是能够渐进式累进式的视频 19 vv.setVideoURI(videoUri); 20 vv.start(); 21 } 22 }
2.MediaPlayer网络视频播放器
将MediaPlayer用于网络视频播放类似于在第9章中讨论过的MediaPlayer和MediaController代码。在下面的示例中会突出显示专门与网络播放相关的部分。关于MediaPlayer和MediaController的完整解释,请参阅第9章中的示例。
1 package com.nthm.androidtestActivity; 2 3 import java.io.IOException; 4 import com.nthm.androidtest.R; 5 import android.app.Activity; 6 import android.media.MediaPlayer; 7 import android.media.MediaPlayer.OnBufferingUpdateListener; 8 import android.media.MediaPlayer.OnCompletionListener; 9 import android.media.MediaPlayer.OnErrorListener; 10 import android.media.MediaPlayer.OnInfoListener; 11 import android.media.MediaPlayer.OnPreparedListener; 12 import android.media.MediaPlayer.OnSeekCompleteListener; 13 import android.media.MediaPlayer.OnVideoSizeChangedListener; 14 import android.os.Bundle; 15 import android.view.Display; 16 import android.view.MotionEvent; 17 import android.view.SurfaceHolder; 18 import android.view.SurfaceHolder.Callback; 19 import android.view.SurfaceView; 20 import android.view.View; 21 import android.view.View.OnTouchListener; 22 import android.widget.LinearLayout; 23 import android.widget.MediaController; 24 import android.widget.MediaController.MediaPlayerControl; 25 import android.widget.TextView;
StreamingVideoPlayer活动实现了许多可用的监听以及源自MediaPlayer、SurfaceHolder和MediaController的回调抽象类。OnBufferingUpdateListener在处理网络传输的媒体时特别的有用。这个类指定了一个onBufferingUpdate方法,当缓冲媒体时重复调用该方法,从而能够跟踪缓冲区的填充程度。
1 public class StreamingVideoPlayer extends Activity implements 2 OnCompletionListener, OnErrorListener, OnInfoListener, 3 OnBufferingUpdateListener, OnPreparedListener, OnSeekCompleteListener, 4 OnVideoSizeChangedListener, Callback, MediaPlayerControl,OnTouchListener { 5 private MediaController controller; 6 private Display currentDisplay; 7 private SurfaceView surfaceView; 8 private SurfaceHolder surfaceHolder; 9 private MediaPlayer mediaPlayer; 10 private View mainView;
在当前版本中将使用一个称为statusView的TextView对象,以将状态消息显示给用户。之所以这么做,是因为通过Internet加载待播放的视频会花费相当长的时间,如果没有一些状态消息,那么用户可能会认为应用程序已经挂起。
1 private TextView statusView; 2 private int videoWidth=0; 3 private int videoHeight=0; 4 private boolean readyToPlay=false; 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.streamimgvideoplayer); 9 mainView=findViewById(R.id.MainView); 10 statusView=(TextView) findViewById(R.id.StatusTextView); 11 12 surfaceView=(SurfaceView) findViewById(R.id.SurfaceView); 13 surfaceHolder=surfaceView.getHolder(); 14 surfaceHolder.addCallback(this); 15 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 16 17 mediaPlayer=new MediaPlayer(); 18 statusView.setText("MediaPlayer Created"); 19 mediaPlayer.setOnCompletionListener(this); 20 mediaPlayer.setOnErrorListener(this); 21 mediaPlayer.setOnInfoListener(this); 22 mediaPlayer.setOnPreparedListener(this); 23 mediaPlayer.setOnSeekCompleteListener(this); 24 mediaPlayer.setOnVideoSizeChangedListener(this);
在MediaPlayer事件监听器的列表中,活动实现并注册为OnBufferingUpdateListener。
1 mediaPlayer.setOnBufferingUpdateListener(this);
与播放SD卡上的文件不同,现在将播放一个来自RTSP服务器的文件。在字符串filePath中指定该文件的URL。然后,使用MediaPlayer的setDataSource方法,并传入filePath字符串。由于MediaPlayer知道如何处理从一个RTSP服务器加载并播放数据,因此不必做其他的改动来处理它。
1 String filePath="";//可用于渐进式加载的视频路径 2 try { 3 mediaPlayer.setDataSource(filePath); 4 } catch (IllegalArgumentException e) { 5 e.printStackTrace(); 6 } catch (SecurityException e) { 7 e.printStackTrace(); 8 } catch (IllegalStateException e) { 9 e.printStackTrace(); 10 } catch (IOException e) { 11 e.printStackTrace(); 12 } 13 statusView.setText("MediaPlayer setDataSource Set"); 14 currentDisplay=getWindowManager().getDefaultDisplay(); 15 controller=new MediaController(this); 16 } 17 18 19 @Override 20 public void surfaceCreated(SurfaceHolder holder) { 21 mediaPlayer.setDisplay(holder); 22 statusView.setText("MediaPlayer Display surface Set");
我们将使用MediaPlayer的prepareAsync方法而不是prepare方法。prepareAsync方法在后台中一个单独的线程上做准备工作,从而使得用户界面不会被挂起。因此,这将允许用户执行其他操作,或者允许开发人员显示一个加载动画或类似的内容。
1 try{ 2 mediaPlayer.prepareAsync(); 3 }catch(Exception e){ 4 finish(); 5 }
因此,通过更新由statusView显示的状态信息,用户将知道在运行prepareAsync时发生了什么。
1 statusView.setText("MediaPlayer preparing"); 2 } 3 @Override 4 public void surfaceChanged(SurfaceHolder holder, int format, int width, 5 int height) { 6 7 } 8 @Override 9 public void surfaceDestroyed(SurfaceHolder holder) { 10 11 } 12 @Override 13 public void onCompletion(MediaPlayer mp) { 14 statusView.setText("MediaPlayer Playback Completed"); 15 } 16 @Override 17 public boolean onInfo(MediaPlayer mp, int what, int extra) { 18 return false; 19 } 20 21 @Override 22 public boolean onError(MediaPlayer mp, int what, int extra) { 23 return false; 24 } 25 @Override 26 public void onPrepared(MediaPlayer mp) { 27 statusView.setText("MediaPlayer Prepared"); 28 videoWidth=mp.getVideoWidth(); 29 videoHeight=mp.getVideoHeight(); 30 31 if(videoWidth>currentDisplay.getWidth()||videoHeight>currentDisplay.getHeight()){ 32 float heightRatio=(float)videoHeight/(float)currentDisplay.getHeight(); 33 float widthRatio=(float)videoWidth/(float)currentDisplay.getWidth(); 34 if(heightRatio>1||widthRatio>1){ 35 if(heightRatio>widthRatio){ 36 videoHeight=(int)Math.ceil((float)videoHeight/(float)heightRatio); 37 videoWidth=(int)Math.ceil((float)videoWidth/(float)heightRatio); 38 }else{ 39 videoHeight=(int)Math.ceil((float)videoHeight/(float)widthRatio); 40 videoWidth=(int)Math.ceil((float)videoWidth/(float)widthRatio); 41 } 42 } 43 } 44 surfaceView.setLayoutParams(new LinearLayout.LayoutParams(videoWidth,videoHeight)); 45 controller.setMediaPlayer(this); 46 controller.setAnchorView(this.findViewById(R.id.MainView)); 47 controller.setEnabled(true); 48 controller.show(); 49 mp.start(); 50 statusView.setText("MediaPlayer Started"); 51 } 52 @Override 53 public void onSeekComplete(MediaPlayer mp) { 54 55 } 56 @Override 57 public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { 58 videoWidth=mp.getVideoWidth(); 59 videoHeight=mp.getVideoHeight(); 60 61 if(videoWidth>currentDisplay.getWidth()||videoHeight>currentDisplay.getHeight()){ 62 float heightRatio=(float)videoHeight/(float)currentDisplay.getHeight(); 63 float widthRatio=(float)videoWidth/(float)currentDisplay.getWidth(); 64 if(heightRatio>1||widthRatio>1){ 65 if(heightRatio>widthRatio){ 66 videoHeight=(int)Math.ceil((float)videoHeight/(float)heightRatio); 67 videoWidth=(int)Math.ceil((float)videoWidth/(float)heightRatio); 68 }else{ 69 videoHeight=(int)Math.ceil((float)videoHeight/(float)widthRatio); 70 videoWidth=(int)Math.ceil((float)videoWidth/(float)widthRatio); 71 } 72 } 73 } 74 surfaceView.setLayoutParams(new LinearLayout.LayoutParams(videoWidth,videoHeight)); 75 }
由于活动实现了OnBufferingUpdateListener,并注册为MediaPlayer的监听器,因此当下载缓冲媒体时,将定期调用以下方法。缓冲将发生在准备阶段(调用onPrepareAsync或onPrepare方法之后)。
1 @Override 2 public void onBufferingUpdate(MediaPlayer mp, int percent) { 3 statusView.setText("MediaPlayer Buffering:"+percent+"%"); 4 } 5 6 @Override 7 public void start() { 8 mediaPlayer.start(); 9 } 10 11 @Override 12 public void pause() { 13 if(mediaPlayer.isPlaying()){ 14 mediaPlayer.pause(); 15 } 16 } 17 18 @Override 19 public int getDuration() { 20 return mediaPlayer.getDuration(); 21 } 22 23 @Override 24 public int getCurrentPosition() { 25 return mediaPlayer.getCurrentPosition(); 26 } 27 28 @Override 29 public void seekTo(int pos) { 30 mediaPlayer.seekTo(pos); 31 } 32 33 @Override 34 public boolean isPlaying() { 35 return mediaPlayer.isPlaying(); 36 } 37 38 @Override 39 public int getBufferPercentage() { 40 return 0; 41 } 42 43 @Override 44 public boolean canPause() { 45 return false; 46 } 47 48 @Override 49 public boolean canSeekBackward() { 50 return false; 51 } 52 53 @Override 54 public boolean canSeekForward() { 55 return false; 56 } 57 58 @Override 59 public int getAudioSessionId() { 60 return 0; 61 } 62 @Override 63 public boolean onTouch(View v, MotionEvent event) { 64 if(controller.isShowing()){ 65 controller.hide(); 66 }else{ 67 controller.show(); 68 } 69 return false; 70 } 71 }