我正在使用 ExoPlayer 在基于 Kotlin 的 Android 应用程序中播放 HLS 流。我们的媒体服务器每分钟更改流链接,以避免视频链接泄漏。如果我根据过期的清单文件 (
.ts
) 请求 .m3u8
块,服务器会响应 HTTP 错误代码 483,指示我需要检索新的流链接。
情况:
在播放开始时,ExoPlayer 下载有效的清单,然后获取前 10 个 .ts 块(例如,
001.ts
到 010.ts
),这代表视频的前 5 分钟。HTTP 483
错误。HttpDataSource
或自定义 LoadErrorHandlingPolicy
检测错误,但挑战是如何在不中断播放的情况下无缝切换流 URL。目标是让播放器检索新清单并在 5 分钟后继续播放视频,而用户不会注意到任何中断。我尝试过的:
我有一个自定义
HttpDataSource
来捕获 483 错误,但我不确定如何更新 ExoPlayer 中的流 URL 而不需要 重置播放器或导致播放停止。问题:
当发生 HTTP 483 等错误时,如何动态更新 ExoPlayer 中的 HLS 清单 URL,而不需要中断播放或要求用户注意到任何转换?
使用 MediaSource.Factory 进行自定义处理 创建一个自定义 HlsMediaSource.Factory,它提供能够处理清单 URL 更改的 MediaSource。
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 清单中服务器强制的更改。