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

Android Media3:HLS流拆分多MediaItem后播放异常及监听问题

问题解决方案

一、解决HLS章节裁剪无效、不切换MediaItem的问题

1. 修复时长变量不一致问题

代码中计算章节结束位置使用chapter.duration,但累加偏移量时用了chapter.runtime,如果这两个字段值不同,会导致裁剪位置完全错误。统一使用同一个时长字段:

return chapters.map { chapter ->
    // ... 其他代码
    val clippingConfiguration = ClippingConfiguration.Builder()
        .setStartPositionMs(offsetInMs)
        .setEndPositionMs(offsetInMs + chapter.duration)
        .setRelativeToDefaultPosition(true)
        .build()
    offsetInMs += chapter.duration // 统一使用duration字段累加偏移量
    // ... 构建MediaItem
}

2. 为HLS MediaSource添加裁剪包装

ExoPlayer对HLS流的裁剪需要通过ClippingMediaSource显式包装,直接给MediaItem设置ClippingConfiguration无法生效。修改PlaybackService中的自定义MediaSource Factory:

val customFactory = object : MediaSource.Factory {
    override fun createMediaSource(mediaItem: MediaItem): MediaSource {
        val token = mediaItem.requestMetadata.extras?.getString("TOKEN") ?: ""
        val dataSourceFactory = DataSource.Factory {
            val dataSource = DefaultHttpDataSource.Factory().createDataSource()
            dataSource.setRequestProperty("Authorization", token)
            dataSource
        }
        // 创建基础HLS MediaSource
        val hlsSource = HlsMediaSource.Factory(dataSourceFactory)
            .createMediaSource(mediaItem)
        // 应用裁剪配置(如果存在)
        return mediaItem.clippingConfiguration?.let { clippingConfig ->
            ClippingMediaSource.Factory()
                .createClippingMediaSource(hlsSource, clippingConfig)
        } ?: hlsSource
    }
}

3. 验证播放列表加载状态

在ViewModel中添加日志验证MediaItems是否正确添加:

controller.addMediaItems(mediaItems)
Log.d("PlaybackDebug", "已添加章节数量: ${mediaItems.size}")
controller.prepare()

二、解决媒体项切换监听不触发的问题

1. 注册正确的MediaController监听器

之前错误地将Runnable作为监听器添加,无法触发onMediaItemTransition。需要创建MediaController.Listener实例并正确注册:

fun prepare(context: Context, mediaItems: List<MediaItem>) {
    val sessionToken = SessionToken(
        context,
        ComponentName(context, PlaybackService::class.java),
    )
    MediaController.Builder(context, sessionToken)
        .buildAsync()
        .also { future ->
            future.addListener({
                val controller = future.get()
                // 创建并注册正确的监听器
                val controllerListener = object : MediaController.Listener {
                    override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
                        super.onMediaItemTransition(mediaItem, reason)
                        // 处理章节切换逻辑,比如更新UI显示当前章节
                        mediaItem?.let { currentChapter ->
                            // 发送状态到UI层,例如更新标题
                        }
                    }
                }
                controller.addListener(controllerListener)
                controller.addMediaItems(mediaItems)
                controller.prepare()
                controller.play()
            }, MoreExecutors.directExecutor())
            mediaControllerFuture.value = future
        }
}

2. 设置播放器顺序播放模式

确保播放器禁用循环,避免章节重复播放不切换:
在PlaybackService的Player初始化中添加:

val player = ExoPlayer.Builder(this)
    .setMediaSourceFactory(customFactory)
    .build()
    .apply {
        playWhenReady = true
        repeatMode = Player.REPEAT_MODE_OFF // 禁用循环,保证播放完章节自动切换
    }

额外排查点

  • 检查章节总时长是否与HLS流总时长匹配,避免最后一个章节超出流长度导致播放异常。
  • 可在PlaybackService中直接监听Player事件,验证章节切换是否触发:
player.addListener(object : Player.Listener {
    override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
        Log.d("ServiceDebug", "已切换到章节: ${mediaItem?.mediaId}")
    }
})

内容的提问来源于stack exchange,提问作者rysv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:09:58