美文网首页音视频开发集锦
解决FFmpeg HLS断网判断错误问题

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

作者: 偶是星爷 | 来源:发表于2018-09-27 17:18 被阅读280次

    最近小伙伴遇到了直播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.

    相关文章

      网友评论

        本文标题:解决FFmpeg HLS断网判断错误问题

        本文链接:https://www.haomeiwen.com/subject/soceoftx.html