Exoplayer 介绍

本文记录了一些关于 ExoPlayer 库的一些入门知识。

我们的 Android 项目中可能要使用到视频播放功能。于是提前进行预研,在此记录。

  • WebView - 性能较差,用于H5页面的视频尚能接受,但作为原生播放组件体验差
  • VideoView - 性能尚可,但它无缓存,无缓冲动画。要自行实现这些功能有较大工作量

官方推荐使用 ExoPlayer 库用于音视频播放。

ExoPlayer

ExoPlayer 提供目前 MediaPlayer API 接口不支持的功能,例如 DASH 和 SmoothStreaming 自适应播放。相比 MediaPlayer,ExoPlayer 易于自定义扩展。

库概览

整个 ExoPlayer 库的核心是 ExoPlayer 接口。一个 ExoPlayer 实例对外暴露传统上常见的高级媒体播放器功能,比如媒体缓冲、播放、暂停及seek。ExoPlayer 的实现被设计成对媒体类型,如何存储,在哪里存储,如何渲染等问题有很少的假定和限制。ExoPlayer 并不是直接实现媒体加载和渲染功能,而是将这些工作代理给专门的组件。这些组件在播放器被创建时或准备播放时被注入进来。ExoPlayer 的常用组件包括:

  • MediaSource - 用于定义将被播放的媒体,用于加载媒体,用于指定可以从哪里读取媒体。在开始播放媒体时通过 ExoPlayer.prepare() 方法将 MediaSource 注入进来
  • Renderers - 用于渲染 media 中的单个组件。创建 player 时注入进来
  • TrackSelector - 用于选择 MediaSource 中的 tracks 给每个可用的 Renderer 来消费。创建 player 时注入进来
  • LoadControl - 用于控制 MediaSource 何时缓冲更多媒体数据,以及如何缓冲。创建 player 时注入进来

ExoPlayer 提供了各种缺省的实现。如果仍然不能满足使用需求,也可以进行自定义。比如,自定义 LoadControl 来修改 player 的缓冲策略。或者自定义 Renderer 来使用 Android 并不能原生支持的视频解码器。

优缺点

ExoPlayer 对比 Android MediaPlayer API 的优缺点:

  • DASH(Dynamic Adaptive Streaming over HTTP) 和 SmoothStreaming 支持
  • 支持更多的媒体格式
  • 设备和 Android 版本兼容性更好
  • 自定义和扩展能力更好
  • 使用官方扩展跟第三方库交互的能力更好

配置

简单介绍 ExoPlayer 的用法。先保证在 build.gradle 中包含 Google 和 JCenter 库:

1
2
3
4
repositories {
google()
jcenter()
}

然后在 build.gradle 中添加依赖:

1
implementation 'com.google.android.exoplayer:exoplayer:2.9.0'

另外注意 ExoPlayer 使用 Java 8 编译,所以你的 app 也必须使用 Java 8 编译。

1
2
3
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
}

也可以按需要添加对特定 ExoPlayer API 的依赖,而不是整个 ExoPlayer 库。比如,只依赖 Core,DASH 和 UI:

1
2
3
implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'

ExoPlayer 可用的模块如下:

  • exoplayer-core - 核心功能(必须)
  • exoplayer-dash - 支持 DASH
  • exoplayer-hls - 支持 HLS
  • exoplayer-smoothstreaming - 支持 SmoothStreaming
  • exoplayer-ui - 提供 ExoPlayer 使用的 UI 组件及资源

除了以上库模块,ExoPlayer 还提供若干依赖外部库的扩展模块用于额外功能。具体见扩展库列表

用法

对于简单场景,按照以下几个步骤使用 ExoPlayer

  • 添加 ExoPlayer 依赖
  • 创建 SimpleExoPlayer 实例
  • 将 player 跟 view 关联(用于视频输出和用户输入)
  • 使用完毕后释放 player

创建 ExoPlayer

  • 使用 ExoPlayerFactory 创建 SimpleExoPlayer
  • ExoPlayerFactory 中有不同的方法用于创建 SimpleExoPlayer
  • 最常用的创建方法是 ExoPlayerFactory.newSimpleInstance
1
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(context);

关于线程安全问题:

  • 必须从同一个线程来访问 ExoPlayer 实例(通常来说就是主线程)
  • 访问 ExoPlayer 的线程必须要有 Looper

关联 view

ExoPlayer 库提供 PlayerView,它封装了 PlayerControlView 和用于渲染 video 的 Surface。可以在布局中使用 PlayerView,然后像下面这样进行绑定:

1
2
// Bind the player to the view.
playerView.setPlayer(player);

SimpleExoPlayer 提供接口用于设置自定义的组件以便更精细地控制 player controls 以及 Surface

  • setVideoSurfaceView
  • setVideoTextureView
  • setVideoSurfaceHolder
  • setVideoSurface

准备播放

ExoPlayer 库中 每个媒体片断由 MediaSource 表示。首先创建媒体片断 MediaSource,然后将其传给 ExoPlayer.prepare() 方法。ExoPlayer 库提供不同的 MediaSource 实现:

  • DashMediaSource - DASH
  • SsMediaSource - SmoothStreaming
  • HlsMediaSource - HLS
  • ExtractorMediaSource - regular media files

这段代码用于播放 MP4 文件:

1
2
3
4
5
6
7
8
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"));
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(mp4VideoUri);
// Prepare the player with the source.
player.prepare(videoSource);

播放控制

有两种途径进行播放控制:

  • 直接调用 player 相应的方法。例如:
    • setPlayWhenReady - 播放或暂停
    • seekTo - seek
    • setRepeatMode - 控制是否循环以及如何循环
    • setShuffleModeEnabled - 控制是否乱序播放
    • setPlaybackParameters - 调整播放速度
  • player 绑定到 view 的情况下,用户操作也会导致调用 player 相应的方法

事件监听

使用 Player.EventListener 监听播放状态及可能出现的错误。

1
2
3
// Add a listener to receive events from the player.
// 有时使用 Player.DefaultEventListener 更方便
player.addListener(eventListener);

SimpleExoPlayer 还提供一些额外的监听器:

  • addVideoListener - 监听视频渲染相关事件,可以用于调整 UI
  • addAnalyticsListener - 监听详细事件,可以用于问题诊断分析

释放

使用 ExoPlayer.release 释放资源。

ExoPlayer demo

demo展示了 ExoPlayer 的基本用法。

从 [github](git clone https://github.com/google/ExoPlayer.git) 下载代码后导入 Android Studio 编译成功后如下图所示。

demo 可能在模拟器上无法运行。我在 Genemotion 上的现象是卡在播放状态,不报错也不 crash,原因是某些模拟器并没有正确地实现 Android media stack。不过官方模拟器是支持 ExoPlayer 的(SDK level 23及以上)。

建议在真机上运行 demo。

demo 最终都是通过 PlayerActivity 来播放视频。使用如下命令在 logcat 中查看日志:

1
adb logcat EventLogger:V *:E

可以调整 demo 的 build variants 为 withExtensions 以使用各种扩展库

编辑 assets/media.exolist.json 文件可以向 demo 中添加自己的视频 sample。

关于代码混淆问题:不用特殊配置,直接混淆即可。

ExoPlayer tutorial

这里提供了不错的入门指导。