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 有两个主要职责:
- 为播放器提供定义其媒体结构的时间轴(TimeLine),只要媒体结构发生变化,就提供新的时间轴。 MediaSource 通过调用 MediaSourceCaller.onSourceInfoRefreshed 来提供这些时间轴。
 - 在其时间轴(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 源码。