我在 Android 应用程序中使用 ExoPlayer 通过 HLS 播放视频。有时,服务器在播放期间返回空播放列表(只有标题,没有曲目)。这种情况很少发生,随后的重新启动将返回更新的播放列表。我无法改变这种行为。
问题是ExoPlayer在收到空播放列表后停止播放视频并显示最后一帧的静态图像。其他播放器(例如iOS中的AvPlayer)没有这个问题,视频继续播放。
我尝试稍微调查一下这个问题。据我所知,在收到空播放列表之前,播放器首先请求 m3u8 文件,然后请求 ts 文件。并且这种情况会定期重复。但是,当播放器收到空播放列表时,对 ts 文件的请求就会消失,只发送播放列表更新的请求。 当我使用调试器进行查看时,我发现在收到空播放列表后,
onLoadCompleted
类中的 DefaultHlsPlaylistTracker
方法停止被调用。这似乎指出了问题的根源,但我不太明白。
如何让 ExoPlayer 忽略空播放列表?
更新:onPlaylistError 也会引发错误 HlsPlaylistTracker.PlaylistStuckException。但没有触发任何玩家回调。
您可以尝试实现自定义
HlsPlaylistTracker
,忽略空播放列表并重试获取,直到收到有效的播放列表。这意味着扩展 DefaultHlsPlaylistTracker
,并覆盖其播放列表解析行为。
首先,创建一个自定义
HlsPlaylistTracker
:
public class CustomHlsPlaylistTracker extends DefaultHlsPlaylistTracker {
public CustomHlsPlaylistTracker(
DataSource.Factory dataSourceFactory,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
HlsPlaylistParserFactory playlistParserFactory) {
super(dataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory);
}
@Override
protected void onPlaylistUpdated(HlsUrl url, HlsMediaPlaylist newPlaylist) {
if (newPlaylist.segments.isEmpty()) {
// Detected an empty playlist, do not propagate this update.
Log.w("CustomHlsPlaylistTracker", "Empty playlist detected, ignoring and retrying.");
// Force a refresh
refreshPlaylist(url);
} else {
// Propagate the update if the playlist is not empty.
super.onPlaylistUpdated(url, newPlaylist);
}
}
}
然后,您需要在构建
ExoPlayer
实例时设置此自定义跟踪器:
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, "yourApplicationName"));
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
HlsPlaylistParserFactory playlistParserFactory = new DefaultHlsPlaylistParserFactory();
CustomHlsPlaylistTracker customHlsPlaylistTracker = new CustomHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory);
// Use the custom HlsPlaylistTracker when building the HlsMediaSource.
HlsMediaSource.Factory mediaSourceFactory = new HlsMediaSource.Factory(dataSourceFactory)
.setPlaylistTracker(customHlsPlaylistTracker);
MediaSource mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(hlsVideoUri));
player.setMediaSource(mediaSource);
player.prepare();