Manifest 过期(HTTP 483)时如何无缝更新 ExoPlayer 中的 HLS 流 URL,而不中断播放?

问题描述 投票:0回答:1

我正在使用 ExoPlayer 在基于 Kotlin 的 Android 应用程序中播放 HLS 流。我们的媒体服务器每分钟更改流链接,以避免视频链接泄漏。如果我根据过期的清单文件 (

.ts
) 请求
.m3u8
块,服务器会响应 HTTP 错误代码 483,指示我需要检索新的流链接。

情况:
在播放开始时,ExoPlayer 下载有效的清单,然后获取前 10 个 .ts 块(例如,

001.ts
010.ts
),这代表视频的前 5 分钟。
在第 4 分钟,玩家提前请求另外 10 个块来缓冲接下来的 5 分钟。然而,到那时,清单文件已经过期,服务器返回一个
HTTP 483
错误。
我可以使用自定义
HttpDataSource
或自定义
LoadErrorHandlingPolicy
检测错误,但挑战是如何在不中断播放的情况下无缝切换流 URL。目标是让播放器检索新清单并在 5 分钟后继续播放视频,而用户不会注意到任何中断。

我尝试过的:
我有一个自定义

HttpDataSource
来捕获 483 错误,但我不确定如何更新 ExoPlayer 中的流 URL 而不需要 重置播放器或导致播放停止。

问题:
当发生 HTTP 483 等错误时,如何动态更新 ExoPlayer 中的 HLS 清单 URL,而不需要中断播放或要求用户注意到任何转换?

android kotlin http-live-streaming exoplayer android-media3
1个回答
0
投票

使用 MediaSource.Factory 进行自定义处理 创建一个自定义 HlsMediaSource.Factory,它提供能够处理清单 URL 更改的 MediaSource。
  1. 使用自定义 HttpDataSource 拦截 HTTP 483 错误 使用自定义 HttpDataSource.Factory 检测 HTTP 483 错误。检测到后,获取更新的清单 URL 并通知 ExoPlayer 更新媒体源。
  2. 动态媒体源替换 使用 ExoPlayer 的 setMediaSource() 与 ExoPlayer.addMediaSource() 或 ExoPlayer.prepare() 无缝替换当前媒体源,而无需重置播放位置。 实施步骤 自定义 HttpDataSource 创建自定义 HttpDataSource 来检测和处理 HTTP 483 错误。
class CustomHttpDataSourceFactory( private val baseFactory: HttpDataSource.Factory, private val onStreamExpired: () -> Unit ) : HttpDataSource.Factory { override fun createDataSource(): HttpDataSource { val dataSource = baseFactory.createDataSource() return object : HttpDataSource by dataSource { override fun open(dataSpec: DataSpec): Long { try { return dataSource.open(dataSpec) } catch (e: HttpDataSource.InvalidResponseCodeException) { if (e.responseCode == 483) { onStreamExpired() // Trigger URL update logic } throw e } } } } }
动态更新清单 URL
创建逻辑来动态更新 HLS 清单 URL 并重新加载 HlsMediaSource。

class DynamicHlsMediaSource( private val initialUrl: String, private val player: ExoPlayer, private val dataSourceFactory: HttpDataSource.Factory ) { private var currentUrl = initialUrl fun updateStreamUrl(newUrl: String) { currentUrl = newUrl val newMediaSource = buildMediaSource() player.setMediaSource(newMediaSource, true) player.prepare() } fun buildMediaSource(): HlsMediaSource { return HlsMediaSource.Factory(dataSourceFactory) .createMediaSource(MediaItem.fromUri(currentUrl)) } }

加载错误处理策略
集成 LoadErrorHandlingPolicy 优雅地处理错误并触发 URL 刷新。

class CustomLoadErrorHandlingPolicy( private val onStreamExpired: () -> Unit ) : LoadErrorHandlingPolicy { override fun getRetryDelayMsFor( dataType: Int, loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo, loadDurationMs: Int ): Long { if (loadErrorInfo.exception is HttpDataSource.InvalidResponseCodeException && (loadErrorInfo.exception as HttpDataSource.InvalidResponseCodeException).responseCode == 483 ) { onStreamExpired() // Trigger URL update return C.TIME_UNSET // Don't retry automatically } return DefaultLoadErrorHandlingPolicy().getRetryDelayMsFor(dataType, loadErrorInfo, loadDurationMs) } }

使用组件
使用自定义组件初始化 ExoPlayer

val player = ExoPlayer.Builder(context) .setLoadErrorHandlingPolicy( CustomLoadErrorHandlingPolicy { fetchNewStreamUrl { newUrl -> dynamicHlsMediaSource.updateStreamUrl(newUrl) } } ) .build() val dataSourceFactory = CustomHttpDataSourceFactory(DefaultHttpDataSource.Factory()) { fetchNewStreamUrl { newUrl -> dynamicHlsMediaSource.updateStreamUrl(newUrl) } } val dynamicHlsMediaSource = DynamicHlsMediaSource(initialUrl, player, dataSourceFactory) val initialMediaSource = dynamicHlsMediaSource.buildMediaSource() player.setMediaSource(initialMediaSource) player.prepare() player.play()

获取新的流 URL 实现逻辑以获取新的清单 URL(例如,使用 API 或任何服务器通信)。

fun fetchNewStreamUrl(onUrlFetched: (String) -> Unit) { // Example: Replace this with your server communication logic. val newUrl = "https://new-stream-url.com/manifest.m3u8" onUrlFetched(newUrl) }

解决方案的主要特点
错误检测和恢复:自定义 HttpDataSource 捕获 483 错误并触发恢复。
无缝过渡:ExoPlayer 的 setMediaSource() 更新媒体源,同时保留播放状态。
优雅的处理:自定义 LoadErrorHandlingPolicy 避免不必要的重试并确保播放器保持响应。
这种方法可确保最大限度地减少用户中断,同时动态适应 HLS 清单中服务器强制的更改。

© www.soinside.com 2019 - 2024. All rights reserved.