最近小伙伴遇到了直播hls,网络断开获取不到错误码的问题。网上也有类似问题讨论,比如:
现在播放hls,rtmp视频的时候,网络变化断开,播放器执行的不是error方法,而是complete
这可能是FFmpeg的一个bug。同事写了个临时方案:判断当前是否播放到结尾,若不在结尾则认为是网络断开。
这个方案对于点播有效,对于直播hls则无效,因为直播没有duration。这就迫使我们要寻找更准确的方法。
即便是点播hls,想要通过播放时间点判断是否结束也不准确,最终的结束时间可能和显示的相差很大
问题原因
FFmpeg在打开每个文件时会返回AVFormatContext
对象,context对象中有一个pb,这是内部用到的IO上下文
/**
* I/O context.
*
* - demuxing: either set by the user before avformat_open_input() (then
* the user must close it manually) or set by avformat_open_input().
* - muxing: set by the user before avformat_write_header(). The caller must
* take care of closing / freeing the IO context.
*
* Do NOT set this field if AVFMT_NOFILE flag is set in
* iformat/oformat.flags. In such a case, the (de)muxer will handle
* I/O in some other way and this field will be NULL.
*/
AVIOContext *pb;
通过pb->error,就能获取到IO上发生的错误。
HLS是一种多文件格式,内部有多个m3u8+ts组成。解析完头部或,内部为每个playlist单独创建了AVIOContext
static int hls_read_header(AVFormatContext *s, AVDictionary **options)
{
// ....
/* Open the demuxer for each playlist */
for (i = 0; i < c->n_playlists; i++) {
struct playlist *pls = c->playlists[i];
AVInputFormat *in_fmt = NULL;
if (!(pls->ctx = avformat_alloc_context())) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (pls->n_segments == 0)
continue;
pls->index = i;
pls->needed = 1;
pls->parent = s;
ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
read_data, NULL, NULL);
pls->pb.seekable = 0;
ret = av_probe_input_buffer(&pls->pb, &in_fmt, pls->segments[0]->url,
NULL, 0, 0);
if (ret < 0) {
/* Free the ctx - it isn't initialized properly at this point,
* so avformat_close_input shouldn't be called. If
* avformat_open_input fails below, it frees and zeros the
* context, so it doesn't need any special treatment like this. */
av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", pls->segments[0]->url);
avformat_free_context(pls->ctx);
pls->ctx = NULL;
goto fail;
}
pls->ctx->pb = &pls->pb;
ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, NULL);
}
}
pls->pb
才是实际IO,在hls_read_packet
方法中,pls->pb
读取失败,返回了错误码,但是没有更新最先创建的context。简单的改法即将pls->pb->error透传给s->pb->error.
点播特殊处理
点播HLS在播放中,所有的TS分片url已知。需要在open_url
中设置error。由于内部http有重试机制,如果重试成功还需要把error改回来。
修改点
暂且只处理EIO,其它错误码不处理。
广告时间:
还在为选择播放器烦恼,究竟是用原生的AVPlayer,还是简单基于FFmpeg的kxmovie,又或者是巨复杂的ijkplayer?如果你不想花时间处理各种奇奇怪怪的bug,也不想再界面交互上花太多心思,试一试SuperPlayer.
网友评论