ExoPlayer-IMA怎么实现无缝播放广告的

  Experience Matters, 是 Tubi 一次客户端技术沙龙的主题,客户端工程师应该追求丝滑般顺畅的用户体验,把做到极致作为自己的目标。开发者通过产品在和用户对话,开发者做的每一个优化和提升,都会有用户感受的到。

  上篇文章对 ExoPlayer-IMA 扩展做了简单的介绍,了解到它能让视频和广告做到无缝切换,广告和视频内容的转换不需要等待,就像在播同一段视频一样。那么它是怎么做到的呢?

  一般情况下,使用 ExoPlayer 播放内容视频,需要创建一个MediaSource, 并把它传给ExoPlayer实例,代码是这样的:

1
private lateinit var mPlayer: SimpleExoPlayer
2
fun play(uri: Uri){
3
  val mediaSource = createMediaSource(uri)
4
  mPlayer.prepare(mediaSource)
5
  mPlayer.playWhenReady = true
6
}
7
8
fun createMediaSource(contentUri:Uri): MediaSource{
9
	...
10
}

  使用了 IMA-Extension 后,要播放一段内容视频,代码是这样的:

1
private lateinit var mPlayer: SimpleExoPlayer
2
private lateinit var mAdsLoader: ImaAdsLoader
3
private lateinit var mAdViewProvider: AdViewProvider
4
5
fun play(uri: Uri){
6
  val contentMediaSource = createMedisSource(uri)
7
  val adsMediaSource = createAdsMediaSource(contentMediaSource, mAdsLoader, mAdViewProvider)
8
  mPlayer.prepare(mediaSource)
9
  mPlayer.playWhenReady = true
10
}
11
12
fun createAdsMediaSource(contentMediaSource:MediaSource, adsLoader: AdsLoader, 	                 adViewProvider: AdViewProvider): AdsMediaSource{
13
  val adMediaSourceFactory: MediaSourceFactory = ...
14
  return AdsMediaSource(mediaSource, adMediaSourceFactory, mAdsLoader, adViewProvider)
15
}
16
17
fun createMediaSource(contentUri:Uri): MediaSource{
18
	...
19
}

  二者主要区别是创建的 MedisSource 不一样,如果要让ExoPlayer 实现电影和广告的无缝切换,需要用到AdsMediaSoource 这个类,这里介绍下 MediaSource 和 AdsMediaSource.

MediaSource: 定义并提供ExoPlayer播放的媒体资源。MediaSource 有两个主要职责:

  1. 为播放器提供定义其媒体结构的时间轴(TimeLine),只要媒体结构发生变化,就提供新的时间轴。 MediaSource 通过调用 MediaSourceCaller.onSourceInfoRefreshed 来提供这些时间轴。
  2. 在其时间轴(TimeLine)的时段内提供媒体片段(MediaPeriod)实例。 MediaPeriods 通过调用 createPeriod(MediaPeriodId,Allocator,long) 获得,并为播放器提供一种加载和读取媒体的方式。

AdsMediaSource: 使用提供的源内容媒体插入线性广告,它必须是准备播放器的顶级资源。

  到这里我们知道 AdsMediaSource 包含了要播放的内容媒体和广告的信息,创建 AdsMedaiSource 的一种方法是传入以下几种类型作为构造函数的参数:

  contentMedisSource:内容媒体的 MediaSource;

  adMediaSourceFactory:加工广告 uri 成一个广告的 MediaSource,并提供支持的资源类型(DASH, HLS等);

  adsLoder:广告加载器;

  AdsLoader.AdsViewProvider:广告 UI 视图提供者。

  那么 AdsMediaSource 怎么动态播放实时广告的呢, 看了 AdsLoader 的实现后一目了然,AdsLoader 做为一个接口,很重要的一个方法是:

1
void start(EventListener eventListener, AdViewProvider adViewProvider);

  AdsMediaSource 会调用 start() 方法做加载广告的操作,AdsLoader 请求广告服务器,获取到广告信息后,包括每组广告的插入位置, 一组广告里有几个广告,创建一个成员变量 adPlaybackState,通过eventListener.onAdPlaybackState(AdPlaybackState adPlaybackState) 通知 AdsMediaSource 更新广告数据,当下一个广告点(cuePoint)来之前,AdsLoader 继续去广告服务器请求广告,更新 adPlaybackState。

  如果想进一步了解 AdsMediaSource 怎么在不重新创建 MediaSource 的情况下实现广告和内容视频的无缝切换,请探索 AdsMediaSource 源码。