解决FFmpeg HLS断网判断错误问题

最近小伙伴遇到了直播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,其它错误码不处理。



作者:偶是星爷
链接:https://www.jianshu.com/p/6e450d02599c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值