ExoPlayer 库提供了多途径加载多媒体资源 , 如记载在线视频文件、 加载本地音频文件、 读取本地视频缓存等 。 其核心围绕 DataSource
类提供多种途径加载媒体的默认实现类 。
DataSource
DataSource
是可读数据流的核心接口 , 暴露构建不同场景下读取数据流的 Factory
接口 。 如 FileDataSource
读取的是 File 文件数据 , HttpDataSource
则是从 HTTP 协议中读取服务器数据 。
DefaultDataSource
DefaultDataSource
及 DefaultDataSourceFactory
是 ExoPlayer 库默认实现处理媒体加载的类 , 其实现内部逻辑为
- 根据给定 uri , 识别 Scheme 来判断获取数据流的方式
- 根据不同的方式 , 构建不同的 DataSource 实例
- DataSource 实例代理
DefaultDataSource#read
及DefaultDataSource#open
的实现
DefaultDataSource
的逻辑非常简单明了 , 内部维护了真正读取数据的 DataSource
实例 。
#open
1 | /** |
该方法用于 “ 打开数据源来读取特定的数据 ” 。 其中 DataSpec
定义了操作数据的信息 , 包括是否压缩、 是否缓存、 请求源、 可读长度、 读取位置、 标志位等等 。
方法返回 long
数据源长度 。 下面以 FileDataSource
及 DefaultHttpDataSource
加载场景来进一步加深理解 (以下分析代码保留主逻辑略细节) 。
1 | from FileDataSource.java |
上述读取文件的实现简单明了 , 而通过 Http 请求则相对复杂些 :
1 |
|
无论是 FileDataSource
还是 DefaultHttpDataSource
, #open
都保留了可读数据的入口 , 分别是 file 和 inputStream ,为 #read
中做好了铺垫 。
#read
1 | /** |
该方法用于 “ 在 #open 方法暴露的数据入口中, 从 offset 位置读取长度为 readLength 的数据并保存在 buffer 缓存区中 ” , 返回读取的长度 。
同样我们以 FileDataSource
及 DefaultHttpDataSource
为例来进一步加深理解 (以下分析代码保留主逻辑略细节) 。
1 | @Override |
从上述方法可知 , File 可读数据越读越小 , 直到返回 C.RESULT_END_OF_INPUT
标志着文件已经读取完毕 。
1 |
|
上述 1 中为何会存在所需要跳过的字节数据呢 ? 在 #open
实现中 , 其实有一个代码段涉及到
1 | // If we requested a range starting from a non-zero position and received a 200 rather than a |
从注释中可知 , 如果我们请求的数据块不是从其实 0 位置开始且服务端不支持 HTTP 206 (常用于断点续传) , 则服务端会返回 200 且返回的数据会从 0 位置开始 。这时候我们接收的数据和本地 buffer 区中的数据存在 [0 , rangPosition) 区间重叠 , 故这部分需要剔除 。
dataSpec.position != 0
意味着本地请求并不是从 0 位置开始 , 故 responseCode == 200 && dataSpec.position != 0
条件满足时我们需要丢去 [0 , rangPosition) 区间数据 。 看看 #skipInternal
是否如我们所想 。
1 | private void skipInternal() throws IOException { |
了解为何跳过且如何跳过后 , 看看 #readInternal
是如何读取数据的 。
1 | private int readInternal(byte[] buffer, int offset, int readLength) throws IOException { |
实际上 #read
流程和 FileDataSource
基本是一致的 。
#close
1 | /** |
同常规 javaStream 流处理一样。 close() 用于资源加载完毕或者加载过程出现异常时调用 。
对于 FileDataSource
, 处理逻辑就是关闭 file :
1 | @Override |
对于 DefaultHttpDataSource , 处理逻辑就是关闭 inputStream :
1 |
|
扩展数据获取
如果仔细看上述 #open
, #read
和 #open
, 相信对如何自定义数据获取流程有一定的理解 。
官方提供了扩展 Okhttp
方式获取数据组件 extension-okhttp
, 内部实现实际上和 DefaultHttpDataSource
十分相识 。
对于常规数据获取 , 官方 library-core
实际上已经十分完善 , 扩展方面更是提供了 extension-okhttp
, extension-rtmp
等 。
业务不尽相同 , 如果上述组件都不满足业务需求的话 , 再考虑自行扩展组件即可 。