• Android视频播放-SurfaceView和Mediaplayer


    好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用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);
    	}
  • 相关阅读:
    JavaScript实现简单轮播图动画
    洛谷2151 [SDOI2009]HH去散步(矩阵快速幂,边点互换)
    洛谷P2579 [ZJOI2005]沼泽鳄鱼(矩阵快速幂,周期)
    洛谷4159 [SCOI2009] 迷路(矩阵快速幂,拆点)
    洛谷5789 [TJOI2017]可乐(矩阵快速幂,Floyd思想)
    【封装】二维BIT
    【封装】替罪羊树
    【封装】Splay
    洛谷3521 [POI2011]ROT-Tree Rotations(线段树合并)
    数据结构:树的链式递归实现
  • 原文地址:https://www.cnblogs.com/xiaofeixiang/p/4099433.html
Copyright © 2020-2023  润新知