Android开发Android技术知识Android知识

VideoView and TextureVideoView o

2016-12-16  本文已影响2140人  rivc

此文章你将会学会在android的视频播放功能

  • 使用VideoView视频播放
  • 使用TextureVideoView视频播放

Tips

此文章仅供学习使用,有些细节还没认真fix的,望大家不要太在意细节,主要列表视频播放功能已实现,thanks!


绪论(好多废话的~选择忽略)

公司上班没事干好无聊,自己又刚出来社会冒泡,就学习学习下技术啦。自己应用有个功能需求,需要在一个列表里面有多个视频播放,好了,第一时间百度“android 视频播放”,得出以下搜索结果心得:
一,时间过去太久,一般写的文章都是N年前的。
二,写的人太少,就像android 蓝牙那些,讲的人少++
三,网上的文章太过片面,比如大多数的网友们写的就是简简单单一个控件播放一个视频的例子,或者简单说下某个技术点的功能,而没有市面上存在的软件例子来做一个相对完整的Demo讲解的(或许网上的大神们高估了像我这些菜鸟的能力吧,所以写得那么简单)
四,找到一款继承视频开发的SDK(国内的喔)。Vitamio (https://www.vitamio.org/ )是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染。(我没试过好不好用)听起来挺不错啦,有基础的小伙伴可以去使用它,没基础的建议学点基础在去使用,这样事半功倍。
五,我想学习下,装逼下,哈哈哈哈哈哈哈哈哈

好吧,既然国内说得那么少,我只能转战国外的,像Google官网学习和StackOverflow等等的地方学习了。

对于我们开发者挺值得庆幸的事情是,Google Developers中国网站(https://developers.google.cn/)在近期正式发布了,从此中国小伙伴们不用翻墙也可以上谷歌官网学习了。以下谷歌官网的链接我都使用中国谷歌官网的,方便你们搜索。

使用VideoView播放视频 -Added in API level 1

Google VideoView 官方链接
https://developer.android.google.cn/reference/android/widget/VideoView.html
VideoView 继承 SurfaceView implement一个MediaController.MediaPlayerControl(用来显示进度条,播放,停止 等等的那个控制器)

xml布局

<VideoView    
    android:id="@+id/videoView"   
    android:layout_width="match_parent"  
    android:layout_height="match_parent" />

代码使用

@BindView(R.id.videoView) protected VideoView videoView;

Butterknife是一个插件来的,用于View的绑定,相当于 VideoView videoView = (VideoView)findViewById(R.id.videoView)

videoView.setMediaController(new MediaController(context));

用于控制条显示,可以不写

videoView.setVideoPath(getVideoUrl());

设置视频的路径。还有setViewURI(Uri uri),setVideoURI(Uri uri)和setVideoUri(Uri uri, Map<String, String> headers)都可以设置路径。

videoView.requestFocus();

将焦点放在View上面,如果写单单一个播放的控件,不写也是可以播放的。在列表中,使用它只是为了区分它是哪个VideoView的,方便控件的状态还原,你可以试着在我的Demo中注释它播放几个VideoView和不注释试试就知道了。

videoView.seekTo(0);

可以不写,查看到它的源码,原来是使用MediaPlayer.seetTo(x)去设置的,用来指定哪个时间位置开始播放,单位为毫秒,接着看就是NDK的东西了,详情如下:
/** * Seeks to specified time position. * *
@param msec the offset in milliseconds from the start to seek to *
@throws IllegalStateException if the internal player engine has not been * initialized */
public native void seekTo(int msec) throws IllegalStateException;

videoView.start();

这是启动VideoView,但这不意味视频就可以马上可以播放的,它只不过是开始播放准备状态中,真正可以播放视频是在回调`videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {})的时候。当然,如果你整个视频缓冲完点击按钮再次触动videoView.start()时候,就可以马上播放的,不会调用setOnPreparedListener的。

就这样,简简单单一个利用VideoView播放视频功能的小东西就完成了。

接下来,介绍下VideoView常用的一些方法,你不写也不会报错,此只不过增强对VideoView的控制罢了。

videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {    
     @Override   
      public void onPrepared(MediaPlayer mp) { 
               //Register a callback to be invoked when the media file* is loaded 
               //and ready to go.
        }
}); ```
当VideoView执行.start()后,视频可以播放时候就会执行onPrepared()函数里面的东西,看函数就可以知道它是利用MediaPlayer去具体实现这个准备播放监听事件的。虽然VideoView每次都使用MediaPlayer去实现其身的某些事件,但是在VideoView你是不可以直接控制MediaPlayer的,比如setOnPreparedListener()(监听可以播放时候的函数),setOnBufferingUpdateListener(用来监听缓冲流有多少的函数)等等,但是你可以巧妙的利用以上的onPrepared(MediaPlayer mp)里面的MediaPlayer去使用设置某些函数。

```java
videoView.setOnFocusChangeListener(new View.OnFocusChangeListener() {    
        @Override    
          public void onFocusChange(View v, boolean hasFocus) {  
             //  Called when the focus state of a view has changed.
           }
}); ```
当焦点改变的时候这个函数会被调用,如在一个列表里的多个VideoView中点击不同Item的VideoView,你就可以再此函数设置前一个VideoView Item的某些控件状态的回复。如:列表中,A_VideoView播放着,点击B_VideoView,那么B_VideoView就要进入准备播放视频的状态中,而A_VideoView就要还原未播放状态,将其播放状态停止下来,和隐藏其Item对应的VideoView,progressBar等等的吧。还是不懂的你可以运行我的Demo和看Log。
```java
 /** 
  * Interface definition of a callback to be invoked to communicate some 
  * info and/or warning about the media or its playback. 
  */   
videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {  
        @Override    
         public boolean onInfo(MediaPlayer mp, int what, int extra) { 
                /**   
                 * @return True if the method handled the info, false if it didn't.
                 * Returning false, or not having an OnErrorListener at all, will
                 * cause the info to be discarded.
                 */
                return false;  
    }
});

此方法在视频播放的时候去返回一些事件的信息或者发出一些警告信息,比如网络不好导致视频停止播放的时候,你可以设置ProgressBar加载视频按钮的出现,这样用户体验就更加好。

onInfo()函数的参数
mp : 此函数信息属于的MediaPlayer
what :信息的类型或者是警告。例如有: MediaPlayer.MEDIA_INFO_BUFFERING_START: 暂停播放等待缓冲更多数据,这里你可以设置ProgressBar的显示 。MediaPlayer.MEDIA_INFO_BUFFERING_END: 缓冲完后继续播放,这里你可以设置ProgressBar的消失等等。
extra: 指定的code. 通常依赖于实现

返回值: 当返回true的时候,这个方法会处理那些发生的异常或正常的事件,如果返回false的时候,而且没有OnErrorListener函数,那么这个事件将会被丢失。

/** 
 * Register a callback to be invoked when the end of a media file 
 * has been reached during playback. 
 * * @param l The callback that will be run
 */
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
     {                
          @Override               
           public void onCompletion(MediaPlayer mp) {                    
        
     }            
});

当时视频播放完成的时候此方法将会被调用。

/** 
 * Register a callback to be invoked when an error occurs 
 * during playback or setup.  If no listener is specified, 
 * or if the listener returned false, VideoView will inform
 * the user of any errors. 
 * * @param l The callback that will be run 
 */
videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { 
    @Override   
     public boolean onError(MediaPlayer mp, int what, int extra) {      
      
        return true;    
      }
});

在播放或则在配置的过程中出现错误,这个函数将被调用。它也是基于MediaPlayer实现的。

onError()函数的参数,
mp:该信息对应的MediaPlayer
what:错误类型
extra:错误码 有:
MEDIA_ERROR_IO 本地文件或网络相关错误
MEDIA_ERROR_MALFORMED 比特流不符合相关的编码标准和文件规范
MEDIA_ERROR_UNSUPPORTED 比特流符合相关编码标准和文件规范,但是media框架不被支持。
MEDIA_ERROR_TIMED_OUT 超时错误
MEDIA_ERROR_SYSTEM 应用系统比较旧的错误(不知道为什么官网找不到这个,但是AS查看源码有)

返回值:返回true,说明此方法处理了该错误。如:当设置不存在的链接时候,软件不会报错,但是会还原该Item的状态,你可以用我Demo玩下。返回false,或者完全没用OnErrorListener的时候,OnCompletionListener将会被调用,如果设置不存在链接的例子,就会弹出窗口直接报错。

总结:看完上面介绍的几个方法,聪明的你应该会察觉,虽说是使用VideoView,但大部分最终的实现却是利用MediaPlayer去实现的。所以还想弄懂原理的话,你们自行了解MediaPlayer去吧。


使用TextureVideoView播放视频

TextureView -Added in API level 14

开始我先简单介绍Google原生的TextureView控件,因为TextureVideoView是继承TextureView的。

Google TextureView官方链接
https://developer.android.google.cn/reference/android/view/TextureView.html

TextureView 直接继承View。 TextureView主要用来绘制流内存(比如:视频,openGL场景),TextureView仅仅在硬件加速窗口时候被使用,当软件已经渲染完成的时候,TextureView是不会绘画任何东西的。不像SurfaceView,TextureView不会创建单独的窗口,然而其表现出来的就像一个View。而SurfaceView和TextureView重要不同点是,因为VideoView继承SurfaceView,所以可以说VideoView和TextureView重要的不同点是,TextureView可以被移动,转换和动画等等。例如:你可以通过textureView.setAlpha(0.5f)去设置透明,而VideoView都是不可以的。

(额外简单说下硬件加速度这东西,它是API Level 11时候开始,绘制View才支持硬件加速的,而TextureView在API 14才有,硬件加速利用GPU特性,使绘制更加平滑,但内存消耗会多点。硬件加速可以被代码开启和关闭。更多的关于硬件加速可以查看此网友的http://www.cnblogs.com/frydsh/archive/2012/10/23/2733581.html

为什么不直接介绍Google原生的TextureView而是介绍一个第三方的TextureVideView。
一,TextureView封装得确实很好
二,(个人)原因是网上说得太少了,偷懒还没去了解
三,还有就是下面的原因:
无论继承是surfaceView的VideoView还是TextureView播放视频,都利用了MediaPlayer去实现。 以前用surfaceView(VideoView是继承surfaceView的)来播放视频的时候可以获得surfaceHolder对象,然后通过setDisplay方法就可以为mediaplayer指定显示的surfaceview。 但是textureview有点不同,他返回的是SurfaceTexture,这里我们需要先将SurfaceTexture转为Surface,然后通过setSurface方法为Mediaplayer指定显示的TextureView

好了!开始正式介绍TextureVideoView!

TextureVideoView是国外https://sprylab.com/en/home.html 网站的公司做出的第三方库。

添加依赖 :

compile 'com.sprylab.android.texturevideoview:texturevideoview:1.1.1'

TextureVideoView:

VideoVIew:

你应该发现了,它们都是implements了 MediaPlayerControl,是的,它们都是通过MediaPlayer去实现视频的播放。而且TextureVideoView封装的方法名和VideoView方法名基本都一样,而且功能也是一样,上面VideoView已经介绍过了常用几个方法,所以这就不再介绍了。 也就是说我们将下面蓝色的TextureVideoView换成VideoView也是可以播放的了。

播放格式的支持

用以上两种控件播放视频,以下格式亲测过。
OK: mp4,3gp,flv,webm
NO:avi,mov,wmv,mkv,gif

源码地址
https://git.coding.net/Riv-Android/VideoPlayer.git

转载请在开头注明作者详细信息和本文出处

上一篇 下一篇

猜你喜欢

热点阅读