Android视频播放-SurfaceView和Mediaplayer

十度 Android 2015年12月01日 收藏

好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用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);
	}