ExoPlayer 漫谈之DataSource

3,120 阅读2分钟

大多数人多ExoPlayer的了解,仅限于ExoPlayer是一个高效的播放器,目前在播放器之中,ExoPlayer也算是独树一帜了,ExoPlayer在Github上非常火,使用的人非常多,可见ExoPlayer的功能、性能还是优化的相当不错的。

我们使用一个开源库,会用当然是好的,如果在第一步会用之后,还能深入分析其中的原理方法,将开源的东西消化一下,还在原有的基础上扩展原有的功能,修复原有项目的问题。最终也能在开源项目的基础上优化开源项目。得到符合我们业务场景的功能。

1.DataSource分析

DataSource在ExoPlayer中就是请求url的接口类,当然要看你播放的url是什么schema的,代码流程就不分析了,感兴趣可以自己看一下. 你输入一个url,这个url是什么schema,然后用对应schema的子DataSource去请求. 在DefaultDataSource.java中:

  public long open(DataSpec dataSpec) throws IOException {
    Assertions.checkState(dataSource == null);
    // Choose the correct source for the scheme.
    String scheme = dataSpec.uri.getScheme();
    if (Util.isLocalFileUri(dataSpec.uri)) {
      String uriPath = dataSpec.uri.getPath();
      if (uriPath != null && uriPath.startsWith("/android_asset/")) {
        dataSource = getAssetDataSource();
      } else {
        dataSource = getFileDataSource();
      }
    } else if (SCHEME_ASSET.equals(scheme)) {
      dataSource = getAssetDataSource();
    } else if (SCHEME_CONTENT.equals(scheme)) {
      dataSource = getContentDataSource();
    } else if (SCHEME_RTMP.equals(scheme)) {
      dataSource = getRtmpDataSource();
    } else if (SCHEME_UDP.equals(scheme)) {
      dataSource = getUdpDataSource();
    } else if (DataSchemeDataSource.SCHEME_DATA.equals(scheme)) {
      dataSource = getDataSchemeDataSource();
    } else if (SCHEME_RAW.equals(scheme)) {
      dataSource = getRawResourceDataSource();
    } else {
      dataSource = baseDataSource;
    }
    // Open the source and return.
    return dataSource.open(dataSpec);
  }

DataSource接口中的方法如下:

/**
 * A component from which streams of data can be read.
 */
public interface DataSource {

  void addTransferListener(TransferListener transferListener);

  long open(DataSpec dataSpec) throws IOException;

  int read(byte[] buffer, int offset, int readLength) throws IOException;

  @Nullable Uri getUri();

  default Map<String, List<String>> getResponseHeaders() {
    return Collections.emptyMap();
  }
  void close() throws IOException;
}
  • addTransferListener : 增加数据传输的监听
  • open : 发起请求的地方,可以是网络请求,也可以是请求本地文件
  • read : 读流数据
  • getUri : 获取请求的uri
  • getResponseHeaders : 获取请求的response header
  • close : 关掉流

这几个操作是必须的操作,当然你可以根据自己的业务需求增加额外的接口.

主要看一下TransferListener中的传入监听接口

public interface TransferListener {

  void onTransferInitializing(DataSource source, DataSpec dataSpec, boolean isNetwork);

  void onTransferStart(DataSource source, DataSpec dataSpec, boolean isNetwork);

  void onBytesTransferred(
      DataSource source, DataSpec dataSpec, boolean isNetwork, int bytesTransferred);

  void onTransferEnd(DataSource source, DataSpec dataSpec, boolean isNetwork);
}

其中的onBytesTransferred( DataSource source, DataSpec dataSpec, boolean isNetwork, int bytesTransferred)函数回调告诉开发者当前已经加载了多少流数据,方便做流量控制. ExoPlayer 这方面还是很贴心的.

DataSource之间的类关系如下: DataSource类结构图

2.okhttp替换源生的网络请求

原生的网络请求核心处理在DefaultHttpDataSource.java中,okhttp在链接复用、多线程控制、定制化方面做得比较好,我们在ExoPlayer中也想接入okhttp库。如何使用?

  • 需要引入两个类,如上,OkHttpDataSourceFactory.java和OkHttpDataSource.java
  • 在buildDataSource的时候用OkHttpDataSourceFactory替换DefaultHtttpDataSourceFactory就可以了。

具体可以参考github.com/JeffMony/Pl…