FFmpeg (libav) 需要花费大量时间在具有很少变体的 master 上执行 avformat_open_input

问题描述 投票:0回答:1

我正在尝试使用 libav 来解复用来自 youtube 的 HLS 流,到目前为止它可以工作,但我的问题是对 avformat_open_input 的调用需要很长时间,有时甚至需要 1 分钟。

对 Exoplayer 进行同样的操作(例如),它可以正常工作,没有任何问题..我感觉错过了使用 libav 进行 hls 解复用的一些重要内容..

我正在使用的主文件如下所示:

#EXTM3U
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:URI="http://localhost:35835/itag/233/mediadata.m3u8",TYPE=AUDIO,GROUP-ID="233",DEFAULT=YES,AUTOSELECT=YES,NAME="Default"
#EXT-X-MEDIA:URI="http://localhost:35835/itag/234/mediadata.m3u8",TYPE=AUDIO,GROUP-ID="234",DEFAULT=YES,AUTOSELECT=YES,NAME="Default"
#EXT-X-STREAM-INF:BANDWIDTH=1354423,CODECS="avc1.4D401F,mp4a.40.2",RESOLUTION=854x480,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/231/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=318204,CODECS="avc1.4D4015,mp4a.40.5",RESOLUTION=426x240,AUDIO="233",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/229/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=402807,CODECS="avc1.4D4015,mp4a.40.2",RESOLUTION=426x240,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/229/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=811077,CODECS="avc1.4D401E,mp4a.40.2",RESOLUTION=640x360,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/230/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=177519,CODECS="avc1.4D400C,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/269/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3851098,CODECS="avc1.4D4020,mp4a.40.2",RESOLUTION=1280x720,AUDIO="234",FRAME-RATE=60,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/311/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=6324518,CODECS="avc1.64002A,mp4a.40.2",RESOLUTION=1920x1080,AUDIO="234",FRAME-RATE=60,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/312/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=95792,CODECS="vp09.00.10.08,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",FRAME-RATE=15,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/602/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=170851,CODECS="vp09.00.11.08,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/603/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=292033,CODECS="vp09.00.20.08,mp4a.40.5",RESOLUTION=426x240,AUDIO="233",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/604/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=376636,CODECS="vp09.00.20.08,mp4a.40.2",RESOLUTION=426x240,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/604/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=716510,CODECS="vp09.00.21.08,mp4a.40.2",RESOLUTION=640x360,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/605/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1145989,CODECS="vp09.00.30.08,mp4a.40.2",RESOLUTION=854x480,AUDIO="234",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/606/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3217654,CODECS="vp09.00.40.08,mp4a.40.2",RESOLUTION=1280x720,AUDIO="234",FRAME-RATE=60,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/612/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=6078208,CODECS="vp09.00.41.08,mp4a.40.2",RESOLUTION=1920x1080,AUDIO="234",FRAME-RATE=60,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/617/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=14086878,CODECS="vp09.00.50.08,mp4a.40.2",RESOLUTION=2560x1440,AUDIO="234",FRAME-RATE=60,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
http://localhost:35835/itag/623/mediadata.m3u8

为了举例说明 EXT-STREAM-INF 链接内容的外观,这里有一个示例:

#EXTM3U
#YT-EXT-CONDENSED-URL:BASE-URI="https://rr6---sn-gqn-h5qs.googlevideo.com/videoplayback/id/189d2f25f6103cad/itag/233/source/youtube/cpn/2TC6rJxXeKRmE8UD/expire/1696647973/ei/xXYgZczEE-idp-oPltGVuAI/ip/2a0c:5a84:f209:1800:b842:b4cf:48d7:ca1c/requiressl/yes/ratebypass/yes/goi/133/sgoap/clen%3D4352437%3Bdur%3D713.642%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1677742885047413/hls_chunk_host/rr6---sn-gqn-h5qs.googlevideo.com/mh/M-/mm/31,29/mn/sn-gqn-h5qs,sn-h5qzen7y/ms/au,rdu/mv/m/mvi/6/pl/36/ctier/A/pfa/5/initcwndbps/1466250/hightc/yes/siu/1/vprv/1/playlist_type/DVR/txp/5532434/mt/1696626071/fvip/2/keepalive/yes/fexp/24007246/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,goi,sgoap,ctier,pfa,hightc,siu,vprv,playlist_type/sig/AGM4YrMwRQIhAIxbCFktH4mxamWq1_maS3YbKeNCpYaOd8JCh0fMaCblAiBFrvxzoCzIjaLpMkxvv6Aw0dSNpYDl_KI-P1aNw7McyQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AK1ks_kwRQIhAOKU632ETDnLoNcjq8c7oB8fd2bxioPtYANy3Bo7LKrvAiBfywDIDKfA4RY7c0CzqpRWOso0X0Gkbx6aLox0EG11KQ%3D%3D/playlist/index.m3u8",PARAMS="begin,len,goap,gosq",PREFIX="s/"
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:7
#EXTINF:3.1,
https://rr6---sn-gqn-h5qs.googlevideo.com/videoplayback/id/189d2f25f6103cad/itag/233/source/youtube/cpn/2TC6rJxXeKRmE8UD/expire/1696647973/ei/xXYgZczEE-idp-oPltGVuAI/ip/2a0c:5a84:f209:1800:b842:b4cf:48d7:ca1c/requiressl/yes/ratebypass/yes/goi/133/sgoap/clen%3D4352437%3Bdur%3D713.642%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1677742885047413/hls_chunk_host/rr6---sn-gqn-h5qs.googlevideo.com/mh/M-/mm/31,29/mn/sn-gqn-h5qs,sn-h5qzen7y/ms/au,rdu/mv/m/mvi/6/pl/36/ctier/A/pfa/5/initcwndbps/1466250/hightc/yes/siu/1/vprv/1/playlist_type/DVR/txp/5532434/mt/1696626071/fvip/2/keepalive/yes/fexp/24007246/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,goi,sgoap,ctier,pfa,hightc,siu,vprv,playlist_type/sig/AGM4YrMwRQIhAIxbCFktH4mxamWq1_maS3YbKeNCpYaOd8JCh0fMaCblAiBFrvxzoCzIjaLpMkxvv6Aw0dSNpYDl_KI-P1aNw7McyQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AK1ks_kwRQIhAOKU632ETDnLoNcjq8c7oB8fd2bxioPtYANy3Bo7LKrvAiBfywDIDKfA4RY7c0CzqpRWOso0X0Gkbx6aLox0EG11KQ%3D%3D/playlist/index.m3u8/s/0/3100/slices%3D0-62986/0
#EXTINF:4.266666,
https://rr6---sn-gqn-h5qs.googlevideo.com/videoplayback/id/189d2f25f6103cad/itag/233/source/youtube/cpn/2TC6rJxXeKRmE8UD/expire/1696647973/ei/xXYgZczEE-idp-oPltGVuAI/ip/2a0c:5a84:f209:1800:b842:b4cf:48d7:ca1c/requiressl/yes/ratebypass/yes/goi/133/sgoap/clen%3D4352437%3Bdur%3D713.642%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1677742885047413/hls_chunk_host/rr6---sn-gqn-h5qs.googlevideo.com/mh/M-/mm/31,29/mn/sn-gqn-h5qs,sn-h5qzen7y/ms/au,rdu/mv/m/mvi/6/pl/36/ctier/A/pfa/5/initcwndbps/1466250/hightc/yes/siu/1/vprv/1/playlist_type/DVR/txp/5532434/mt/1696626071/fvip/2/keepalive/yes/fexp/24007246/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,goi,sgoap,ctier,pfa,hightc,siu,vprv,playlist_type/sig/AGM4YrMwRQIhAIxbCFktH4mxamWq1_maS3YbKeNCpYaOd8JCh0fMaCblAiBFrvxzoCzIjaLpMkxvv6Aw0dSNpYDl_KI-P1aNw7McyQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AK1ks_kwRQIhAOKU632ETDnLoNcjq8c7oB8fd2bxioPtYANy3Bo7LKrvAiBfywDIDKfA4RY7c0CzqpRWOso0X0Gkbx6aLox0EG11KQ%3D%3D/playlist/index.m3u8/s/3100/4267/slices%3D0-62986/1
h5qs.googlevideo.com/videoplayback/id/189d2f25f6103cad/itag/233/source/youtube/cpn/2TC6rJxXeKRmE8UD/expire/1696647973/ei/xXYgZczEE-idp-oPltGVuAI/ip/2a0c:5a84:f209:1800:b842:b4cf:48d7:ca1c/requiressl/yes/ratebypass/yes/goi/133/sgoap/clen%3D4352437%3Bdur%3D713.642%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1677742885047413/hls_chunk_host/rr6---sn-gqn-h5qs.googlevideo.com/mh/M-/mm/31,29/mn/sn-gqn-h5qs,sn-h5qzen7y/ms/au,rdu/mv/m/mvi/6/pl/36/ctier/A/pfa/5/initcwndbps/1466250/hightc/yes/siu/1/vprv/1/playlist_type/DVR/txp/5532434/mt/1696626071/fvip/2/keepalive/yes/fexp/24007246/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,goi,sgoap,ctier,pfa,hightc,siu,vprv,playlist_type/sig/AGM4YrMwRQIhAIxbCFktH4mxamWq1_maS3YbKeNCpYaOd8JCh0fMaCblAiBFrvxzoCzIjaLpMkxvv6Aw0dSNpYDl_KI-P1aNw7McyQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AK1ks_kwRQIhAOKU632ETDnLoNcjq8c7oB8fd2bxioPtYANy3Bo7LKrvAiBfywDIDKfA4RY7c0CzqpRWOso0X0Gkbx6aLox0EG11KQ%3D%3D/playlist/index.m3u8/s/693833/5034/slices%3D0-640,4202012-4262932/148
#EXTINF:4.3,
https://rr6---sn-gqn-h5qs.googlevideo.com/videoplayback/id/189d2f25f6103cad/itag/233/source/youtube/cpn/2TC6rJxXeKRmE8UD/expire/1696647973/ei/xXYgZczEE-idp-oPltGVuAI/ip/2a0c:5a84:f209:1800:b842:b4cf:48d7:ca1c/requiressl/yes/ratebypass/yes/goi/133/sgoap/clen%3D4352437%3Bdur%3D713.642%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1677742885047413/hls_chunk_host/rr6---sn-gqn-h5qs.googlevideo.com/mh/M-/mm/31,29/mn/sn-gqn-h5qs,sn-h5qzen7y/ms/au,rdu/mv/m/mvi/6/pl/36/ctier/A/pfa/5/initcwndbps/1466250/hightc/yes/siu/1/vprv/1/playlist_type/DVR/txp/5532434/mt/1696626071/fvip/2/keepalive/yes/fexp/24007246/beids/24350018/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,goi,sgoap,ctier,pfa,hightc,siu,vprv,playlist_type/sig/AGM4YrMwRQIhAIxbCFktH4mxamWq1_maS3YbKeNCpYaOd8JCh0fMaCblAiBFrvxzoCzIjaLpMkxvv6Aw0dSNpYDl_KI-P1aNw7McyQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/AK1ks_kwRQIhAOKU632ETDnLoNcjq8c7oB8fd2bxioPtYANy3Bo7LKrvAiBfywDIDKfA4RY7c0CzqpRWOso0X0Gkbx6aLox0EG11KQ%3D%3D/playlist/index.m3u8/s/698867/4300/slices%3D0-640,4202012-4323843/149
[.. a lot more parts ...]
#EXT-X-ENDLIST

注:

  • 我已经尝试解析 master.m3u8 以过滤流并生成一个新的 master.m3u8,其中仅包含我想要的流(1 个音频 + 1 个视频)。计时有所改善,但仍然超过 15 秒!
  • 我感觉 avformat_open_input 正在将所有部分读取/打开/解析到每个 EXT-X-STREAM-INF 或类似的内容中,否则这是没有意义的。不知道如何解决这个问题。
  • 我已经尝试过一些选项,如 http_multiple 或 http_seekable,结果没有任何变化..

有什么想法吗?

video ffmpeg http-live-streaming m3u8 libav
1个回答
0
投票

我找到了一种方法可以大大减少 avformat_open_input 打开流、获取流信息等的时间..

诀窍是:

  • 解析m3u8文件以获取分段的uri。
  • 创建 avio 上下文来读取这些片段。
  • 在调用avformat_open_input之前执行av_probe_input_buffer。

现在,我花了 20 秒,有时甚至超过一分钟的相同视频,在不到 100 毫秒的时间内打开。

这里有一个快速但肮脏的示例来演示解决方案,使用 libav + hlparse:

    #define AVIO_CTX_BUFSIZE 1024 * 32

static int avioContextRead(void *context, uint8_t *buf, int bufSize)
{
    HlsSegmentsParser *demuxer = (HlsSegmentsParser *)context;
    if (demuxer == nullptr)
    {
        return 0;
    }
    return demuxer->readBuffer(buf, bufSize);
}

int HlsSegmentsParser::readBuffer(uint8_t *buf, int bufSize)
{
    while(true){
        if(cur_segment == nullptr){
            cur_segment = segments->data;
            qDebug()<<"AVFORMAT: Reading segment:"<<cur_segment->sequence_num;
            if (avio_open2(&segment_avio_context, cur_segment->uri, AVIO_FLAG_READ, NULL, NULL) < 0) {
                // Errol;
                return -1;
            }
        }

        int bytesRead = avio_read(segment_avio_context, buf, bufSize);
        if(bytesRead<0)
        {
            qDebug()<<"AVFORMAT: Finished segment:"<<cur_segment->sequence_num;
            avio_close(segment_avio_context);
            segment_avio_context = nullptr;

            cur_segment = nullptr;
            segments = segments->next;
            continue;
        }

        return bytesRead;
    }
}

HlsSegmentsParser::HlsSegmentsParser()
{
    // Inicializa libavformat
    avformat_network_init();
}

int HlsSegmentsParser::init(const std::string &filePath)
{

    m_formatContext = avformat_alloc_context();
    m_avioCtxBuffer = static_cast<uint8_t *>(av_malloc(AVIO_CTX_BUFSIZE));
    m_avioContext = avio_alloc_context(m_avioCtxBuffer, AVIO_CTX_BUFSIZE, 0, this, &avioContextRead, nullptr, nullptr);

    auto *formatContext = (AVFormatContext *)m_formatContext;
    formatContext->pb = static_cast<AVIOContext *>(m_avioContext);
    formatContext->flags |= AVFMT_FLAG_CUSTOM_IO;


    auto content = readURLContent(filePath.c_str());
    qDebug()<<"AVFORMAT: Content: " << content.c_str();

    master_t master_playlist;
    if (HLS_OK == hlsparse_master_init(&master_playlist)) {
         if (hlsparse_master(content.c_str(), content.length(), &master_playlist)) {

            auto videoUri = master_playlist.stream_infs.data->uri;
            auto stream_contnet = readURLContent(videoUri);

            qDebug()<<"AVFORMAT: StreamContent: " << stream_contnet.c_str();
            media_playlist_t media_playlist;
            if(HLS_OK == hlsparse_media_playlist_init(&media_playlist)){
                if(hlsparse_media_playlist(stream_contnet.c_str(), stream_contnet.length(), &media_playlist)){
                    segments = &media_playlist.segments;
                    qDebug() << "AVFORMAT: Opening Input (video): " << videoUri;
                }
            }

        }
    }

    int ret = 0;

    qDebug()<<"AVFORMAT: av_probe_input_buffer: ";
    const AVInputFormat * fmt = nullptr;
    ret = av_probe_input_buffer(m_avioContext, &fmt,  nullptr, nullptr, 0 , 0);
    if (ret < 0)
    {
        qDebug() << "AVFORMAT: Unable to av_probe_input_buffer: " << ret << av_err2str(ret);
    }
    qDebug()<<"AVFORMAT: av_probe_input_buffer done! ";

    qDebug()<<"AVFORMAT: avformat_open_input: ";
    ret = avformat_open_input(&formatContext, nullptr, fmt, nullptr);
    if (ret < 0)
    {
        qDebug() << "AVFORMAT: Unable to avformat_open_input: " << ret << av_err2str(ret);
    }

    qDebug()<<"AVFORMAT: avformat_open_input done! ";


    qDebug()<<"AVFORMAT: avformat_find_stream_info" ;
    ret = avformat_find_stream_info(formatContext, nullptr);
    if (ret < 0)
    {
        qDebug() << "AVFORMAT: Unable to avformat_find_stream_info: " << ret << av_err2str(ret);
    }

    qDebug()<<"AVFORMAT: avformat_find_stream_info done!";

    const AVCodec *vcodec = nullptr;
    auto m_vStreamIdx = av_find_best_stream(formatContext,
                                       AVMEDIA_TYPE_VIDEO,
                                       -1,
                                       -1,
                                       &vcodec,
                                            0);


    qDebug()<<"AVFORMAT: m_vStreamIdx"<<m_vStreamIdx;

    AVCodecParameters *codecParam = formatContext->streams[m_vStreamIdx]->codecpar;
    AVRational fpsRatio = formatContext->streams[m_vStreamIdx]->avg_frame_rate;

    m_width = codecParam->width;
    m_height = codecParam->height;
    m_vCodec = codecParam->codec_id;

    qDebug()<<"AVFORMAT: VCodec "<<avcodec_get_name(codecParam->codec_id)<<m_width<<"x"<<m_height<<fpsRatio.den<<fpsRatio.num;

    return -1;
}

std::string HlsSegmentsParser::readURLContent(const char* url) {
    AVIOContext *avioCtx = NULL;
    uint8_t buffer[8192];
    int bytesRead;
    std::string content;

    if (avio_open2(&avioCtx, url, AVIO_FLAG_READ, NULL, NULL) < 0) {
        fprintf(stderr, "No se pudo abrir la URL.\n");
        avformat_network_deinit();
        return "";
    }

    do {
        bytesRead = avio_read(avioCtx, buffer, sizeof(buffer));
        if (bytesRead > 0) {
            content.append(reinterpret_cast<char*>(buffer), bytesRead);
        }
    } while (bytesRead > 0);

    avio_close(avioCtx);
    return content;
}
© www.soinside.com 2019 - 2024. All rights reserved.