看下直播m3u8结构:

  1. #EXTM3U

  2. #EXT-X-MEDIA-SEQUENCE:3918

  3. #EXT-X-TARGETDURATION:10

  4. #EXTINF:10,

  5. 2017071806/1500358480.ts?type=hls_live_slice

  6. #EXTINF:10,

  7. 2017071806/1500358490.ts?type=hls_live_slice

  8. #EXTINF:10,

  9. 2017071806/1500358500.ts?type=hls_live_slice

  10. #EXTINF:10,

  11. 2017071806/1500358510.ts?type=hls_live_slice

  12. #EXTINF:10,

  13. 2017071806/1500358520.ts?type=hls_live_slice

  14. #EXTINF:10,

  15. 2017071806/1500358535.ts?type=hls_live_slice

  16. #EXTINF:10,

  17. 2017071806/1500358545.ts?type=hls_live_slice

  18. #EXTINF:10,

  19. 2017071806/1500358555.ts?type=hls_live_slice

  20. #EXTINF:10,

  21. 2017071806/1500358565.ts?type=hls_live_slice

  22. #EXTINF:10,

  23. 2017071806/1500358575.ts?type=hls_live_slice

看下点播m3u8结构:

  1. #EXTM3U

  2. #EXT-X-VERSION:3

  3. #EXT-X-MEDIA-SEQUENCE:0

  4. #EXT-X-TARGETDURATION:19

  5. #EXT-X-PLAYLIST-TYPE:VOD

  6. #EXTINF:9.800,

  7. 00_d0024036a7j.320086.1.ts?index=0&start=0&end=9800&brs=0&bre=1509827&ver=4

  8. #EXTINF:10.480,

  9. 01_d0024036a7j.320086.1.ts?index=1&start=9800&end=20280&brs=1509828&bre=3828055&ver=4

  10. #EXTINF:9.960,

  11. 02_d0024036a7j.320086.1.ts?index=2&start=20280&end=30240&brs=3828056&bre=6248743&ver=4

  12. #EXTINF:9.520,

  13. 03_d0024036a7j.320086.1.ts?index=3&start=30240&end=39760&brs=6248744&bre=8234399&ver=4

  14. #EXTINF:9.920,

  15. 04_d0024036a7j.320086.1.ts?index=4&start=39760&end=49680&brs=8234400&bre=10619931&ver=4

  16. #EXTINF:9.960,

  17. 05_d0024036a7j.320086.1.ts?index=5&start=49680&end=59640&brs=10619932&bre=13400639&ver=4

  18. #EXTINF:9.720,

  19. 06_d0024036a7j.320086.1.ts?index=6&start=59640&end=69360&brs=13400640&bre=16777683&ver=4

  20. #EXTINF:10.880,

  21. 07_d0024036a7j.320086.1.ts?index=7&start=69360&end=80240&brs=16777684&bre=19713867&ver=4

  22. ...省略

  23. #EXTINF:9.960,

  24. 029_d0024036a7j.320086.2.ts?index=29&start=295520&end=305480&brs=0&bre=1287611&ver=4

  25. #EXTINF:9.040,

  26. ...省略

  27. #EXTINF:7.240,

  28. 0315_d0024036a7j.320086.11.ts?index=315&start=3181360&end=3188600&brs=30080752&bre=31204615&ver=4

  29. #EXT-X-ENDLIST

区别:

  • 1、 每个TS分片时间,通过标签EXTINF,后面有的是float型,有的是int型,一般为10s(也有不会10s的),最后如果没有10s,就取对应时间。

  • 2、不是所有点播m3u8中都有EXT-X-PLAYLIST-TYPE,表明类型。最直接区分就是直播m3u8没有EXT-X-ENDLIST标签,因为是实时流,自然不会有结束,否则就是点播流了。

FFmpeg是如何解析直播,点播的HLS?在\libavformat\hlsproto.c中,就是实现步骤,先打开m3u8文件,然后parse。

  1. static int hls_open(URLContext *h, const char *uri, int flags)

  2. {

  3.    HLSContext *s = h->priv_data;

  4.    int ret, i;

  5.    const char *nested_url;

  6.    if (flags & AVIO_FLAG_WRITE)

  7.        return AVERROR(ENOSYS);

  8.    h->is_streamed = 1;

  9.    if (av_strstart(uri, "hls+", &nested_url)) {

  10.        av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));

  11.    } else if (av_strstart(uri, "hls://", &nested_url)) {

  12.        av_log(h, AV_LOG_ERROR,

  13.               "No nested protocol specified. Specify e.g. hls+http://%s\n",

  14.               nested_url);

  15.        ret = AVERROR(EINVAL);

  16.        goto fail;

  17.    } else {

  18.        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);

  19.        ret = AVERROR(EINVAL);

  20.        goto fail;

  21.    }

  22.    av_log(h, AV_LOG_WARNING,

  23.           "Using the hls protocol is discouraged, please try using the "

  24.           "hls demuxer instead. The hls demuxer should be more complete "

  25.           "and work as well as the protocol implementation. (If not, "

  26.           "please report it.) To use the demuxer, simply use %s as url.\n",

  27.           s->playlisturl);

  28.    if ((ret = parse_playlist(h, s->playlisturl)) < 0)

  29.        goto fail;

  30.    if (s->n_segments == 0 && s->n_variants > 0) {

  31.        int max_bandwidth = 0, maxvar = -1;

  32.        for (i = 0; i < s->n_variants; i++) {

  33.            if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {

  34.                max_bandwidth = s->variants[i]->bandwidth;

  35.                maxvar = i;

  36.            }

  37.        }

  38.        av_strlcpy(s->playlisturl, s->variants[maxvar]->url,

  39.                   sizeof(s->playlisturl));

  40.        if ((ret = parse_playlist(h, s->playlisturl)) < 0)

  41.            goto fail;

  42.    }

  43.    if (s->n_segments == 0) {

  44.        av_log(h, AV_LOG_WARNING, "Empty playlist\n");

  45.        ret = AVERROR(EIO);

  46.        goto fail;

  47.    }

  48.    s->cur_seq_no = s->start_seq_no;

  49.    if (!s->finished && s->n_segments >= 3)

  50.        s->cur_seq_no = s->start_seq_no + s->n_segments - 3;

  51.    return 0;

  52. fail:

  53.    hls_close(h);

  54.    return ret;

  55. }

解析playlist中的ts流

  1. static int parse_playlist(URLContext *h, const char *url)

  2. {

  3.    HLSContext *s = h->priv_data;

  4.    AVIOContext *in;

  5.    int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;

  6.    int64_t duration = 0;

  7.    char line[1024];

  8.    const char *ptr;

  9.    if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,

  10.                                   &h->interrupt_callback, NULL,

  11.                                   h->protocol_whitelist, h->protocol_blacklist)) < 0)

  12.        return ret;

  13.    read_chomp_line(in, line, sizeof(line));

  14.    if (strcmp(line, "#EXTM3U")) {

  15.        ret = AVERROR_INVALIDDATA;

  16.        goto fail;

  17.    }

  18.    free_segment_list(s);

  19.    s->finished = 0;

  20.    while (!avio_feof(in)) {

  21.        read_chomp_line(in, line, sizeof(line));

  22.        if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {

  23.            struct variant_info info = {{0}};

  24.            is_variant = 1;

  25.            ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,

  26.                               &info);

  27.            bandwidth = atoi(info.bandwidth);

  28.        } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {

  29.            s->target_duration = atoi(ptr) * AV_TIME_BASE;

  30.        } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {

  31.            s->start_seq_no = atoi(ptr);

  32.        } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {

  33.            s->finished = 1;

  34.        } else if (av_strstart(line, "#EXTINF:", &ptr)) {

  35.            is_segment = 1;

  36.            duration = atof(ptr) * AV_TIME_BASE;

  37.        } else if (av_strstart(line, "#", NULL)) {

  38.            continue;

  39.        } else if (line[0]) {

  40.            if (is_segment) {

  41.                struct segment *seg = av_malloc(sizeof(struct segment));

  42.                if (!seg) {

  43.                    ret = AVERROR(ENOMEM);

  44.                    goto fail;

  45.                }

  46.                seg->duration = duration;

  47.                ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);

  48.                dynarray_add(&s->segments, &s->n_segments, seg);

  49.                is_segment = 0;

  50.            } else if (is_variant) {

  51.                struct variant *var = av_malloc(sizeof(struct variant));

  52.                if (!var) {

  53.                    ret = AVERROR(ENOMEM);

  54.                    goto fail;

  55.                }

  56.                var->bandwidth = bandwidth;

  57.                ff_make_absolute_url(var->url, sizeof(var->url), url, line);

  58.                dynarray_add(&s->variants, &s->n_variants, var);

  59.                is_variant = 0;

  60.            }

  61.        }

  62.    }

  63.    s->last_load_time = av_gettime_relative();

  64. fail:

  65.    avio_close(in);

  66.    return ret;

  67. }

  68. https://mp.weixin.qq.com/s/xCMXp46VXZ2jRGuRGAKu8Q