Android视频开发基础(三)

        接着之前两篇文章继续来讲Android视频开发这个主题。今天这篇文章,我们来聊一聊Android中MediaPlayer的DataSource。也许有的同学会觉得奇怪,DataSource有什么好讲的,我们直接调用MediaPlayer的setDataSource方法不就可以了。其实我们要讲的是更深层次的问题:怎么来干预视频播放器获取数据的过程。要解释这个问题,首先我们先来看看原生播放器的组成。下面是原生播放器的结构图。

Android视频开发基础(三)_第1张图片

 

       从图中我们可以看到,播放器的工作流程是:拉流线程通过播放器内的网络库去请求视频数据,然后交给解码线程进行解码,解码线程解析出的视频数据帧交给播放器的渲染线程去绘制到屏幕上。也就是说,播放器是自己去发送网络请求拉取数据的(如果是本地视频直接从本地地址读取),这个过程是完全封闭在播放器内部,用户只需要提供一个播放地址,播放器就帮我们把其他工作做掉了,这样的设计虽然简化了用户的操作,但是灵活性是比较差的,不利于功能的扩展。

       为了能够给用户提供更多的灵活性,Android新增了MediaDataSource类,通过给播放器设置MediaDataSource,我们就可以通过这个类来给播放器提供数据源。那么播放器的工作过程就变成了下图所示的样子。播放器每次读取数据都是直接从DataSource获取,至于DataSource的数据是从哪里来,是来自网络还是本地缓存,或者其他什么来源,对播放器来说都是透明的,播放器只管读数据就可以了。就比如说我要吃饭,原先需要自己去买菜、洗菜、做菜,现在请了个阿姨,我要吃饭的时候只需要告诉阿姨我要吃饭,她就把饭做好送过来,其他的一律不需要我关心。

 

Android视频开发基础(三)_第2张图片

 

      但遗憾的是,Android从  6.0版本才开始提供这个类,所以目前要给播放器设置DataSource需要自定义播放器。比如,可以用开源的播放库ijkplayer来实现自定义播放器,或者其他的一些播放器解决方案一般也会提供这个接口。

      我们来看一下MediaDataSource类,它其实非常简单,提供了两个接口:readAt() 和 getSize()。     

Android视频开发基础(三)_第3张图片

       

       我们解释一下每个参数的意义。position表示数据读取的起始位置,buffer是一段数据缓存,我们需要把数据塞到这段缓存里,然后播放器就从这段缓存去拿数据,offsset表示我们要向缓存buffer中第几个位置开始写入数据,size表示要读取的数据大小。正常来讲,还需要提供一个seek接口,用来移动数据读取的位置,但是这里做了简单处理:如果size为0,那么就表示这次readAt接口的调用是一个seek操作,我们就应该做一些相应的处理来维护状态的正确性。播放器想要seek到的位置,就是position的值所指示的位置。

       getSize方法顾名思义就是拿到视频文件的大小。

       说了这么多,DataSource到底有什么实际的作用呢?

       首先,我们可以进行文件缓存,从而实现边播边下载的目的。我们可以在readAt接口中去读取网络数据,然后将数据塞给播放器的同时,将数据写入文件缓存,这样的话,当播放器播放完视频,完整的视频文件也就写入了文件。我们只需要将文件移动到特定目录就完成了文件的下载。这样不仅可以起到节省带宽的目的,下一次用户再播放同一个视频的时候,就可以直接读取本地缓存,会大大提高视频首帧的渲染速度。

       另外,由于视频的请求可以走我们自己的网络库,我们就可以对请求进行定制,比如可以走我们的代理服务器来实现某些需求。

       这里只是列举了两种应用场景,更多的场景可以待我们慢慢去发掘,因为主动权已经完全在我们手里啦。

       文章从理论上讲解了DataSource的原理,具体的代码怎么写可以参考这篇文章:Implementing your own Android MediaDataSource 。

       下一篇文章我会来讲下直播的相关技术。

       欢迎关注我的微信公众号,收到最新的推送文章

      Android视频开发基础(三)_第4张图片

 

 

你可能感兴趣的:(【Android】)