ffmpeghttp-live-streaminglibavformat

FFmpeg HLS select streams and only retrieve their data


Opening a HLS stream using avformat_open_input retrieves data from all streams and I would like to only retrieve data from some of them. Is that possible?

Consider the following MWE:

#include <libavformat/avformat.h>
int main(int argc, char **argv)
{
    AVFormatContext *inFmtCtx = NULL;
    AVPacket packet;
    const char *inUrl;
    int ret;

    if (argc < 2) { return -1; }
    inUrl = argv[1];

    if ((ret = avformat_open_input(&inFmtCtx, inUrl, NULL, NULL)) < 0)
        goto end;
    if ((ret = avformat_find_stream_info(inFmtCtx, NULL)) < 0)
        goto end;

    while (1) {
        ret = av_read_frame(inFmtCtx, &packet);
        if (ret < 0) break;

        // # Placeholder: Do Something # //
        printf("%i, ", packet.stream_index);

        av_packet_unref(&packet);
    }
end:
    avformat_close_input(&inFmtCtx);
    if (ret < 0 && ret != AVERROR_EOF) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }
    return 0;
}

Using the example HLS url "http://mcdn.daserste.de/daserste/de/master.m3u8" (might be geolocked), the printf returns values between 0 and 9, indicating that all 10 streams (5 video, 5 audio) are retrieved.

Of course, one could discard all but the selected ones, after they have been read, e.g. using

    if(packet.stream_index != selectedVideoStreamId && packet.stream_index != selectedAudioStreamId) {
        av_packet_unref(&packet);
        continue;
    }

But can the input context / ffmpeg be configured to only retrieve the selected streams, i.e. not downloading all the data that is not needed (the unselected streams)?


Solution

  • You can disable a HLS variant by discarding all streams that belong to it:

    if ((ret = avformat_open_input(&inFmtCtx, inUrl, NULL, NULL)) < 0)
        goto end;
    
    // disable all but the last stream
    for (i = 0; i < inFmtCtx->nb_streams - 1; ++i) {
        AVStream *st = inFmtCtx->streams[i];
        st->discard = AVDISCARD_ALL;
    }
    
    if ((ret = avformat_find_stream_info(inFmtCtx, NULL)) < 0)
        goto end;
    

    Reading your stream for a few seconds yields:

    stream=0 pkt_count=0
    stream=1 pkt_count=0
    stream=2 pkt_count=0
    stream=3 pkt_count=0
    stream=4 pkt_count=0
    stream=5 pkt_count=0
    stream=6 pkt_count=0
    stream=7 pkt_count=0
    stream=8 pkt_count=998
    stream=9 pkt_count=937
    

    As you can see it reads two streams corresponding to the multiplexed audio/video streams in the last playlist, even if a single stream was enabled. If you need better granularity than that you'll have to modify the HLS demuxer.