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 源码。