You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 00:31:03