1、Android中视频播放方式

  1. surfaceView+MediaPlayer,通过MediaPlayer来控制视频的播放、暂停、进度等;
  2. 使用VideoView 来播放,这个类其实也是继承了SurfaceView 类,并且实现了MediaController。MediaPlayerController 这个用于控制媒体播放的接口,另外在VideoView上还有一个用于对媒体播放进行控制的面板,包括快进、快退、播放、暂停按钮以及一个进度条;
  3. 系统提供的播放器

2、surfaceView+MediaPlayer实现视频播放基本步骤

  1. 在布局文件内定义SurfaceView
  2. 在代码中实现SurfaceHolder.Callback接口,重写三个方法
  3. 通过方法getHolder();得到SurfaceHolder,然后设置SurfaceHolder
  4. 为MediaPlayer通过setDisplay()方法,设置SurfaceHolder
  5. 使用setDataSource()方法为MediaPlayer设置播放文件
  6. 进行播放
  • 优点:灵活性高,可以进行自定义;
  • 缺点:难度比较大;
 public class MainActivity extends Activity implements OnClickListener {
     private EditText et_path;
     private Button bt_play, bt_replay, bt_pause, bt_stop;
     private SurfaceView sv;

     private MediaPlayer mediaPlayer;

     private int currentPosition;
     private SeekBar sb;

     private boolean isplaying;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         et_path = (EditText) findViewById(R.id.et_path);
         bt_play = (Button) findViewById(R.id.bt_play);
         bt_replay = (Button) findViewById(R.id.bt_replay);
         bt_pause = (Button) findViewById(R.id.bt_pause);
         bt_stop = (Button) findViewById(R.id.bt_stop);
         sb = (SeekBar) findViewById(R.id.sb);

         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
             @Override
             public void onStopTrackingTouch(SeekBar seekBar) {
                 int process = seekBar.getProgress();
                 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                     mediaPlayer.seekTo(process);
                 }
             }

             @Override
             public void onStartTrackingTouch(SeekBar seekBar) {
             }

             @Override
             public void onProgressChanged(SeekBar seekBar, int progress,
                     boolean fromUser) {
             }
         });

         sv = (SurfaceView) findViewById(R.id.sv);
         // 低版本手机加上参数,指定自己不维护缓冲区
         /* 下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前 */
         sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

         sv.getHolder().addCallback(new Callback() {
             @Override
             public void surfaceDestroyed(SurfaceHolder holder) {
                 System.out.println("holder被销毁了");
                 if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                     currentPosition = mediaPlayer.getCurrentPosition();
                     stop();
                 }
             }

             @Override
             public void surfaceCreated(SurfaceHolder holder) {
                 System.out.println("holder被创建了");
                 if (currentPosition > 0) {
                     play(currentPosition);
                 }
             }

             @Override
             public void surfaceChanged(SurfaceHolder holder, int format,
                     int width, int height) {
                 System.out.println("holder大小变化了");
             }
         });

         bt_play.setOnClickListener(this);
         bt_replay.setOnClickListener(this);
         bt_pause.setOnClickListener(this);
         bt_stop.setOnClickListener(this);
     }

     @Override
     public void onClick(View v) {
         switch (v.getId()) {
         case R.id.bt_play:
             play(0);
             break;
         case R.id.bt_replay:
             replay();
             break;
         case R.id.bt_stop:
             stop();
             break;
         case R.id.bt_pause:
             pause();
             break;
         }
     }

     /**
      * 暂停播放
      */
     private void pause() {
         if ("继续".equals(bt_pause.getText().toString().trim())) {
             mediaPlayer.start();
             bt_pause.setText("暂停");
             return;
         }
         if (mediaPlayer != null && mediaPlayer.isPlaying()) {
             mediaPlayer.pause();
             bt_pause.setText("继续");
             return;
         }
     }

     /**
      * 重新播放
      */
     private void replay() {
         bt_pause.setText("暂停");
         if (mediaPlayer != null && mediaPlayer.isPlaying()) {
             mediaPlayer.seekTo(0);
             return;
         }
         play(0);
     }

     /**
      * 停止播放音乐
      */
     private void stop() {
         if (mediaPlayer != null && mediaPlayer.isPlaying()) {
             mediaPlayer.stop();
             mediaPlayer.release();
             mediaPlayer = null;
             bt_pause.setText("暂停");
             bt_play.setEnabled(true);
             isplaying = false;
         }
     }

     /**
      * 播放视频
      */
     private void play(final int currentPosition2) {
         String path = et_path.getText().toString().trim();
         try {
             mediaPlayer = new MediaPlayer();
             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
             /* 设置Video影片以SurfaceHolder播放 */
             mediaPlayer.setDisplay(sv.getHolder());
             mediaPlayer.setDataSource(path);
             mediaPlayer.prepareAsync();
             mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
                 @Override
                 public void onPrepared(MediaPlayer mp) {
                     mediaPlayer.start();
                     int max = mediaPlayer.getDuration();
                     sb.setMax(max);
                     mediaPlayer.seekTo(currentPosition2);

                     new Thread() {
                         public void run() {
                             isplaying = true;
                             while (isplaying) {
                                 int position = mediaPlayer.getCurrentPosition();
                                 sb.setProgress(position);
                                 try {
                                     Thread.sleep(500);
                                 } catch (InterruptedException e) {
                                     e.printStackTrace();
                                 }
                             }
                         }
                     }.start();
                 }
             });
             mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                 @Override
                 public void onCompletion(MediaPlayer mp) {
                     bt_play.setEnabled(true);
                 }
             });
             mediaPlayer.setOnErrorListener(new OnErrorListener() {
                 @Override
                 public boolean onError(MediaPlayer mp, int what, int extra) {
                     bt_play.setEnabled(true);
                     isplaying = false;
                     return false;
                 }
             });
             bt_play.setEnabled(false);
         } catch (Exception e) {
             Toast.makeText(this, "播放失败", 0).show();
             e.printStackTrace();
         }
     }

 }

3、VideoView实现视频播放基本步骤

  1. * 在onCreate方法中,首先获取布局管理器中添加的VideoView组件。并创建一个要播放视频所对应的对象。
  2. * 然后创建一个MediaController对象,用于控制视频的播放。
  3. *使用VideoView播放该视频
  • 优点:比较简单,可以直接进行使用;
  • 缺点:灵活性不高;
 public class MainActivity extends Activity {
     private VideoView vv;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         vv = (VideoView) findViewById(R.id.vv);

         MediaController mc = new MediaController(this);
         mc.setAnchorView(vv);

         vv.setMediaController(mc);
         vv.setVideoPath("mnt/sdcard/areyouok.3gp");
         vv.start();
     }
 }

4、View, surfaceView, GLSurfaceView区别

  • view是最基础的,必须在UI主线程内更新画面,速度较慢。
  • SurfaceView 是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快
  • GLSurfaceView 是SurfaceView的子类,opengl 专用的

  SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。

  那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。

  当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

  基于以上,根据游戏特点,一般分成两类。

  1. 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
  2. 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

  Android中的SurfaceView类就是双缓冲机制。因此,开发游戏时尽量使用SurfaceView而不要使用View,这样的话效率较高,而且SurfaceView的功能也更加完善。

5、SurfaceHolder

  可以通过SurfaceHolder接口访问这个Surface.用getHolder()方法可以得到这个接口。

  surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。

  如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。

  surfaceview的核心在于提供了两个线程:UI线程和渲染线程。

  这里应注意:

  1. 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
  2. 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

Android多媒体技术之视频播放的更多相关文章

  1. android实现控制视频播放次数

    android实现控制视频播放次数,实质就是每个视频片段播放完后,通过MediaPlayer设置监听器setOnCompletionListener监听视频播放完毕,用Handler发送消息再次激活视 ...

  2. Android几种视频播放方式,VideoView、SurfaceView+MediaPlayer、TextureView+MediaPlayer,以及主流视频播放器开源项目

    简单的说下一Android的几种视频播放功能: 1.VideoView:最简单的视频播放 <FrameLayout xmlns:android="http://schemas.andr ...

  3. android 自己定义视频播放器之2/1

    非常久没更新博客,相信大家年后都比較忙. 今天给大家带来了一款视频播放器,首先确认的得有几点. 1.首先得有个播放视频的view. 2.加点额外功能进去左边上下滑动调节亮度,右边上下滑动调节声量: 3 ...

  4. 开源安卓Android流媒体音视频播放器实现声音自动停止、恢复、一键静音功能源码

    本文转自EasyDarwin团队John的博客:http://blog.csdn.net/jyt0551/article/details/60802145 我们在开发安卓Android流媒体音视频播放 ...

  5. Android开发 VideoView视频播放详解

    前言 VideoView是Android主要的视频播放View,它其实是对MediaPlayer的再次封装.如果你已经了解过MediaPlayer在使用VideoView是十分简单的.如果你想先了解M ...

  6. android 全屏视频播放(SurfaceView + MediaPlayer)

    介绍个第三方: JieCaoVideoPlayer 实现Android的全屏视频播放,支持完全自定义UI.手势修改进度和音量.hls.rtsp,设置http头信息,也能在ListView.ViewPa ...

  7. 玩转Android之在线视频播放控件Vitamio的使用

    其实Android中自带的MediaPlayer本身就能播放在线视频,MediaPlayer结合SurfaceView播放在线视频也是不错的选择(如果你没有性能或者用户体验上的要求),关于MediaP ...

  8. 分享几个不错的Android开源音视频播放器

    整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习.   UniversalMusicPlayer https://github.com/googlesamp ...

  9. Android高级_视频播放控件

    一.Android系统自带VideoView控件 1. 创建步骤: (1)自带视频文件放入res/raw文件夹下: (2)声明初始化VideoView控件: (3)创建视频文件Uri路径,Uri调用p ...

随机推荐

  1. java并发编程实战:第十六章----Java内存模型

    一.什么是内存模型,为什么要使用它 如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果 编译器把变量保存在本地寄存器而不是内存中 编译器中生成的指令顺序,可以与源代码中的顺 ...

  2. 23 DesignPatterns学习笔记:C++语言实现 --- 2.4 Composite

    23 DesignPatterns学习笔记:C++语言实现 --- 2.4 Composite 2016-07-22 (www.cnblogs.com/icmzn) 模式理解

  3. 【树状DP】星象仪

    题目描述 在寂寞的夜里,星象仪是非常浪漫的东西.但是,你作为一个精神稍微有点不太正常的Geek,把原本正常的星象仪改造得像电报发送器一样.当然,你这个的构造还要更加奇葩一点.具体来说,你的星象仪是一棵 ...

  4. Cacti部署

    1>监控概述   通常运维人员在一个企业当中所需要管理一台或者多台服务器,或者甚至更多,特别是BAT公司或者门户级别的公司,一个人管理的服务器可能上百甚至上千台                  ...

  5. Spring 之 AOP

    面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化.AOP 的核心构造是方面, 它将那些影响多个类的行为封装到可重用的模块 ...

  6. How do I determine if I'm being run under the debugger?

    #include <assert.h>#include <stdbool.h>#include <sys/types.h>#include <unistd.h ...

  7. Java反射API研究(1)——注解Annotation

    注解在表面上的意思,只是标记一下这一部分,最好的注解就是代码自身.而在java上,由于注解的特殊性,可以通过反射API获取,这种特性使得注解被广泛应用于各大框架,用于配置内容,代替xml文件配置. 要 ...

  8. Android-应用安装/替换/卸载/广播监听

    在上一篇博客Android-开关机的广播,中介绍了,如何订阅接收者,去接收系统发送的开机/关机广播, 而这篇博客是订阅接收者 去接收应用的(安装/替换/卸载) 三种广播 订阅 接收者 去接收 应用的 ...

  9. TFS签入代码时,自动修改工作项的状态为“已解决”

    Visual Studio中有一个很酷的功能,就是签入代码到TFS库时,可以关联相应的工作项,实现代码与工作项(需求.任务.Bug等)的关联,从而实现代码的跟踪. 在关联工作项的过程中,如果工作项具备 ...

  10. openstack 的 lbaas 疑问

    1 为什么lbaas的haproxy实现没有将其放到vrouter中,而vpnaas/fwaas都放到vrouter中呢? 放在vrouter上,可以减少vrouter到haproxy的流量路径,是怕 ...