如何让Android的ExoPlayer忽略HLS空播放列表?
解决ExoPlayer播放HLS时忽略空播放列表的问题
问题背景
Android应用用ExoPlayer播放HLS视频,服务器偶尔会返回只有头部、没有音视频轨道的空播放列表,且无法修改服务器行为。此时ExoPlayer会停在最后一帧停止播放,但iOS的AVPlayer却能正常继续播。排查后发现:
- 收到空列表前,播放器会定期请求m3u8和ts文件;收到后只请求播放列表更新,
DefaultHlsPlaylistTracker的onLoadCompleted不再触发。 - 内部会触发
onPlaylistError(错误类型为HlsPlaylistTracker.PlaylistStuckException),但这个错误没传递到播放器的外部回调。
解决方案
方案1:自定义播放列表解析器,过滤空列表
通过重写HlsPlaylistParser,在解析时如果遇到空播放列表,就返回上一次的有效列表,避免播放器进入异常状态。
- 实现自定义解析器:
public class NonEmptyHlsPlaylistParser extends DefaultHlsPlaylistParser { private HlsPlaylist lastValidPlaylist; @Override public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException { HlsPlaylist playlist = super.parse(uri, inputStream); // 判断是否为空播放列表:媒体列表无分片或无音视频轨道 if (playlist instanceof MediaPlaylist) { MediaPlaylist mediaPlaylist = (MediaPlaylist) playlist; if (mediaPlaylist.segments.isEmpty() || mediaPlaylist.tracks.isEmpty()) { // 有有效历史列表就返回历史,没有就返回当前(避免首次加载出问题) return lastValidPlaylist != null ? lastValidPlaylist : playlist; } } // 更新有效历史列表 lastValidPlaylist = playlist; return playlist; } }
- 替换ExoPlayer的默认解析器:
HlsMediaSource.Factory hlsFactory = new HlsMediaSource.Factory(dataSourceFactory) .setPlaylistParserFactory(() -> new NonEmptyHlsPlaylistParser()); // 用这个factory创建HlsMediaSource,再设置给ExoPlayer
方案2:自定义播放列表追踪器,忽略空列表错误
重写DefaultHlsPlaylistTracker的错误处理逻辑,遇到PlaylistStuckException时直接跳过,继续请求新的播放列表。
- 实现自定义追踪器:
public class TolerantHlsPlaylistTracker extends DefaultHlsPlaylistTracker { public TolerantHlsPlaylistTracker(Factory factory) { super(factory); } @Override protected void handlePlaylistError(PlaylistError playlistError) { // 识别空列表导致的异常 if (playlistError.error instanceof PlaylistStuckException) { // 直接调度下一次播放列表更新,不触发错误停止 schedulePlaylistUpdate(true); return; } // 其他错误按原逻辑处理 super.handlePlaylistError(playlistError); } }
- 配置到ExoPlayer中:
DefaultHlsPlaylistTracker.Factory trackerFactory = new DefaultHlsPlaylistTracker.Factory(dataSourceFactory); TolerantHlsPlaylistTracker playlistTracker = new TolerantHlsPlaylistTracker(trackerFactory); HlsMediaSource.Factory hlsFactory = new HlsMediaSource.Factory(dataSourceFactory) .setPlaylistTracker(playlistTracker);
方案3:监听播放器错误,主动恢复播放
如果前两个方案不好实现,可以尝试监听播放器的错误回调,捕获到PlaylistStuckException时主动恢复播放。
注意:需要确保错误能传递到外部回调,可通过Player.Listener实现:
player.addListener(new Player.Listener() { @Override public void onPlayerError(PlaybackException error) { if (error.getCause() instanceof HlsPlaylistTracker.PlaylistStuckException) { // 尝试恢复播放,比如重新准备或者继续播放 player.play(); // 或者重新加载媒体源 if (player.getMediaItem() != null) { player.prepare(); } } } });
内容的提问来源于stack exchange,提问作者BArtWell
相关产品推荐
相关产品推荐

