ExoPlayer HLS流插入VAST广告异常问题求助
ExoPlayer HLS流注入VAST广告问题解决
问题分析
1. setMediaSource不展示广告
注入广告时自行创建了HlsMediaSource.Factory,但未使用初始化播放器时配置了广告加载组件的DefaultMediaSourceFactory,导致广告加载逻辑未被触发;同时直接调用setMediaSource未正确触发广告加载的生命周期流程。
2. setMediaItem抛出ArrayIndexOutOfBoundsException
异常中byteCount=-940等负数表明HTTP请求返回异常(如4xx/5xx状态码),原因是新建的DefaultHttpDataSource.Factory未携带初始化时设置的Referer请求头,HLS流请求被服务端拒绝,进而触发读取字节数异常;此外直接复用currentMediaItem的Builder存在状态残留问题。
解决方案
要实现无需重新初始化播放器、用replaceMediaSource(替代setMediaSource以保留播放状态)完成VAST广告注入,需做以下修改:
1. 复用初始化时的核心工厂实例
将初始化时的mediaSourceFactory和httpDataSourceFactory改为类成员变量,避免重复创建不带广告配置和请求头的实例:
// 改为类成员变量 private lateinit var mediaSourceFactory: MediaSource.Factory private lateinit var httpDataSourceFactory: DataSource.Factory private fun initPlayer() { adsLoader = ImaAdsLoader.Builder(requireContext()) .setAdEventListener { adEvent -> // SOME LISTENER } .build() val httpDataSource = DefaultHttpDataSource.Factory().createDataSource().apply { setRequestProperty("Referer", "*some referer*") } httpDataSourceFactory = DataSource.Factory { httpDataSource } mediaSourceFactory = DefaultMediaSourceFactory(httpDataSourceFactory) .setLocalAdInsertionComponents( { unusedAdTagUri: MediaItem.AdsConfiguration? -> adsLoader } ) { binding.unsafe.adView } // 其余缓冲设置和播放器初始化代码不变 }
2. 修改广告注入方法
使用类成员的mediaSourceFactory创建带广告的MediaSource,用replaceMediaSource替换当前播放源(保留播放状态),同时重新构建MediaItem避免状态残留:
private fun injectAdIntoExoPlayer(adLink: String) { val currentUri = playerInstance?.currentMediaItem?.uri ?: return // 重新构建MediaItem,避免旧实例状态残留 val adsConfig = MediaItem.AdsConfiguration.Builder(Uri.parse(adLink)).build() val mediaItemWithAd = MediaItem.Builder() .setUri(currentUri) .setAdsConfiguration(adsConfig) .build() // 使用初始化时配置好广告组件的mediaSourceFactory创建MediaSource val mediaSource = mediaSourceFactory.createMediaSource(mediaItemWithAd) playerInstance?.apply { // 替换当前播放源,保留播放进度等状态 replaceMediaSource(mediaSource) prepare() // 确保触发广告加载流程 } }
3. 统一初始播放器设置的MediaSource创建逻辑
在setupExoPlayer中也使用类成员的mediaSourceFactory创建MediaSource,保持配置一致性:
private fun setupExoPlayer(source: String, adLink: String? = null) { if (playerInstance == null) initPlayer() binding { player.playerView.player = playerInstance adsLoader?.setPlayer(playerInstance) player.playerView.subtitleView?.visibility = View.GONE } val adsConfig = adLink?.let { MediaItem.AdsConfiguration.Builder(Uri.parse(adLink)).build() } val mediaItem = MediaItem.Builder() .setUri(source) .setAdsConfiguration(adsConfig) .build() // 使用类成员的mediaSourceFactory创建MediaSource val mediaSource = mediaSourceFactory.createMediaSource(mediaItem) playerInstance?.apply { setMediaSource(mediaSource) prepare() addListener(playerErrorHandler) playWhenReady = true } }
关键注意事项
- 确保
ImaAdsLoader始终与播放器关联,且广告视图adView已正确布局。 - 禁止重复创建
DataSource.Factory和MediaSource.Factory,否则会丢失广告配置和请求头信息。 - 使用
replaceMediaSource而非setMediaSource,可在不中断播放状态的情况下替换源,更适合动态注入广告的场景。
内容的提问来源于stack exchange,提问作者iddqdidkfaidclip
相关产品推荐
相关产品推荐

