Android视频播放-SurfaceView和Mediaplayer

简介:

好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用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 );
}
本文转自Fly_Elephant博客园博客,原文链接:http://www.cnblogs.com/xiaofeixiang/p/4099433.html,如需转载请自行联系原作者
相关文章
|
前端开发 开发工具 Android开发
Android播放器之SurfaceView与GLSurfaceView
Surface的官方介绍:Handle onto a raw buffer that is being managed by the screen compositor,Surface是一个raw buffer的句柄,通过它在raw buffer上进行绘制,可以通过Surface获得一个Canvas。
297 0
|
7月前
|
XML Java Android开发
Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)
Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)
302 0
|
3月前
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
190 8
|
3月前
|
Android开发
Android中SurfaceView的双缓冲机制和普通View叠加问题解决办法
本文介绍了 Android 平台上的 SurfaceView,这是一种高效的图形渲染控件,尤其适用于视频播放、游戏和图形动画等场景。文章详细解释了其双缓冲机制,该机制通过前后缓冲区交换来减少图像闪烁,提升视觉体验。然而,SurfaceView 与普通 View 叠加时可能存在 Z-Order 不一致、同步问题及混合渲染难题。文中提供了使用 TextureView、调整 Z-Order 和创建自定义组合控件等多种解决方案。
176 9
|
4月前
|
Android开发
Android 利用MediaPlayer实现音乐播放
本文提供了一个简单的Android MediaPlayer音乐播放示例,包括创建PlayerActivity、配置AndroidManifest.xml和activity_player.xml布局,以及实现播放和暂停功能的代码。
39 0
Android 利用MediaPlayer实现音乐播放
|
5月前
|
Android开发 开发者
Android经典面试题之SurfaceView和TextureView有什么区别?
分享了`SurfaceView`和`TextureView`在Android中的角色。`SurfaceView`适于视频/游戏,独立窗口低延迟,但变换受限;`TextureView`支持复杂变换,视图层级中渲染,适合动画/视频特效,但性能略低。两者在性能、变换、使用和层级上有差异,开发者需按需选择。
107 1
|
6月前
|
存储 Android开发 Kotlin
开发安卓app OKhttp下载后使用MediaPlayer播放
在Android Jetpack Compose应用程序中,要使用OkHttp下载远程音频文件并在本地播放,你需要完成以下几个步骤: 1. **添加依赖**:确保`build.gradle`文件包含OkHttp和Jetpack Compose的相关依赖。 2. **下载逻辑**:创建一个`suspend`函数,使用OkHttp发起网络请求下载音频文件到本地。 3. **播放逻辑**:利用`MediaPlayer`管理音频播放状态。 4. **Compose UI**:构建用户界面,包含下载和播放音频的按钮。
|
6月前
|
存储 Android开发
安卓app,MediaPlayer播放本地音频 | 按钮控制播放和停止
在Jetpack Compose中,不直接操作原生Android组件如`Button`和`MediaPlayer`,而是使用Compose UI构建器定义界面并结合ViewModel管理音频播放逻辑。以下示例展示如何播放本地音频并用按钮控制播放/停止:创建一个`AudioPlayerViewModel`管理`MediaPlayer`实例和播放状态,然后在Compose UI中使用`Button`根据`isPlaying`状态控制播放。记得在`MainActivity`设置Compose UI,并处理相关依赖和权限。
|
6月前
|
存储 Android开发 Kotlin
Kotlin开发安卓app,在使用 MediaPlayer 播放 res/raw 中的音乐时遇到突然中断的问题,而 onErrorListener 没有接收到任何报错
在使用 Android MediaPlayer 播放 res/raw 中的音乐时遇到中断问题,可能的原因包括资源问题、媒体文件编码格式、生命周期管理和设备资源配置。要排查问题,检查音频文件是否正确包含,格式编码是否支持,MediaPlayer 是否正确管理及释放,以及设备是否有足够存储和配置。通过设置 onErrorListener 日志和确保在 onDestroy 中释放资源来调试。如果文件过大,考虑使用 AssetManager。遵循这些步骤可帮助诊断并解决播放中断的问题。
|
6月前
|
API Android开发 UED
56. 【Android教程】媒体播放器:MediaPlayer
56. 【Android教程】媒体播放器:MediaPlayer
148 0