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
相关产品推荐
相关产品推荐

