ExoPlayer是Google开源的播放器,可以算是MediaPlayer的扩展版本吧,因为跟一些小伙伴发起了一个播放器开发的项目,所以接下来打算把学到的东西都写下来。一来,可以方便和其他小伙伴交流,二来,也方便以后自己复习。碍于自己Android知识水平有限,如有错误还请大家多多指教。 - 注:ExoPlayer版本 r1.5.9,单独分离出来的ExoPlayerDemo

在Demo中可以看到,流媒体播放的请求都是以Intent的形式,封装一个Sample去拉起一个播放页面的Activity。

Intent mpdIntent = new Intent(mContext, PlayerActivity.class)
                .setData(Uri.parse(sample.uri))
                .putExtra(PlayerActivity.CONTENT_ID_EXTRA, sample.contentId)
                .putExtra(PlayerActivity.CONTENT_TYPE_EXTRA, sample.type)
                .putExtra(PlayerActivity.PROVIDER_EXTRA, sample.provider);
        mContext.startActivity(mpdIntent);

而在Sample的构造方法中,需要传入媒体的类型:

public static final Sample[] CNTV_DASH = new Sample[]{
            new Sample("CCTV-1HD",
            "http://vod.cdn4.cmvideo.cn/envivo_v/HD/cctv1/450/01.m3u8", Util.TYPE_HLS),
    };

同时在PlayerActivity中可以看到,根据不同的流媒体类型会回调不同的接口:

private RendererBuilder getRendererBuilder() {
        String userAgent = Util.getUserAgent(this, "PlayerActivity");
        switch (contentType) {
            case Util.TYPE_SS:
                return new SmoothStreamingRendererBuilder(this, userAgent,
                        contentUri.toString(),
                        new SmoothStreamingTestMediaDrmCallback());
            case Util.TYPE_DASH:
                return new DashRendererBuilder(this, userAgent, contentUri.toString(),
                        new WidevineTestMediaDrmCallback(contentId, provider));
            case Util.TYPE_HLS:
                return new HlsRendererBuilder(this, userAgent, contentUri.toString());
            case Util.TYPE_OTHER:
                return new ExtractorRendererBuilder(this, userAgent, contentUri);
            default:
                throw new IllegalStateException("Unsupported type: " + contentType);
        }
    }

接着前面的线索,查找Util类中的类型参数,如下:

/**
   * Value returned by {@link #inferContentType(String)} for DASH manifests.
   */
  public static final int TYPE_DASH = 0;

  /**
   * Value returned by {@link #inferContentType(String)} for Smooth Streaming manifests.
   */
  public static final int TYPE_SS = 1;

  /**
   * Value returned by {@link #inferContentType(String)} for HLS manifests.
   */
  public static final int TYPE_HLS = 2;

  /**
   * Value returned by {@link #inferContentType(String)} for files other than DASH,
   * HLS or Smooth Streaming manifests.
   */
  public static final int TYPE_OTHER = 3;

看到这里,直观的反应是这四个参数代表不同的流媒体协议。之前都流媒体的协议并不是很了解,于是开始搜索。找到了部分跟协议相关的知识,比如自适性串流。阅读后对流媒体协议有了初步的了解,但感觉还是跟给出的四个类型对不上。终于注意到 inferContentType(String) 这个方法:

/**
   * Makes a best guess to infer the type from a file name.
   *
   * @param fileName Name of the file. It can include the path of the file.
   * @return One of {@link #TYPE_DASH}, {@link #TYPE_SS}, {@link #TYPE_HLS} or
   * {@link #TYPE_OTHER}.
   */
  public static int inferContentType(String fileName) {
    if (fileName == null) {
      return TYPE_OTHER;
    } else if (fileName.endsWith(".mpd")) {
      return TYPE_DASH;
    } else if (fileName.endsWith(".ism")) {
      return TYPE_SS;
    } else if (fileName.endsWith(".m3u8")) {
      return TYPE_HLS;
    } else {
      return TYPE_OTHER;
    }
  }

查看inferContentType的调用会发现,在传入的Sample没有设置类型的时候,会调用该方法检测Uri的后缀,然后根据后缀进行如上分类,并不是代表某一个协议的意思。我用一个MP4格式的本地视频,以TYPE_SS的类型传入,发现也是能播放的。猜测这个类型参数可能在播放时会进行某些优化。只好在接下来的学习中再去解决自己的疑惑了。