--- doc/demuxers.texi | 6 ++++ libavformat/hls.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 73dc0feec1..33643f966a 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -316,6 +316,12 @@ segment index to start live streams at (negative values are from the end). @item max_reload Maximum number of times a insufficient list is attempted to be reloaded. Default value is 1000. + +@item load_all_variants +If 0, only the first variant/playlist is loaded on open. All other variants +get disabled and can be enabled by setting discard option in program. +Default value is 1. + @end table @section image2 diff --git a/libavformat/hls.c b/libavformat/hls.c index 3c2c720abe..500e3c15de 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -112,6 +112,7 @@ struct playlist { int n_segments; struct segment **segments; int needed; + int parsed; int cur_seq_no; int64_t cur_seg_offset; int64_t last_load_time; @@ -206,6 +207,7 @@ typedef struct HLSContext { int strict_std_compliance; char *allowed_extensions; int max_reload; + int load_all_variants; } HLSContext; static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) @@ -315,6 +317,7 @@ static struct playlist *new_playlist(HLSContext *c, const char *url, pls->id3_mpegts_timestamp = AV_NOPTS_VALUE; pls->index = c->n_playlists; + pls->parsed = 0; pls->needed = 0; dynarray_add(&c->playlists, &c->n_playlists, pls); return pls; @@ -867,6 +870,10 @@ fail: av_free(new_url); if (close_in) ff_format_io_close(c->ctx, &in); + + if (pls) + pls->parsed = 1; + return ret; } @@ -1260,17 +1267,30 @@ static int64_t default_reload_interval(struct playlist *pls) pls->target_duration; } -static int playlist_needed(struct playlist *pls) +static int playlist_needed(AVFormatContext *s, struct playlist *pls, int check_parsed) { - AVFormatContext *s = pls->parent; + HLSContext *c = s->priv_data; int i, j; int stream_needed = 0; int first_st; /* If there is no context or streams yet, the playlist is needed */ - if (!pls->ctx || !pls->n_main_streams) + if (check_parsed && (!pls->ctx || !pls->n_main_streams)) return 1; + /* If the playlist belongs to a non discarded variant and is not parsed, + * we need to parse and activate it later */ + for (i = 0; i < s->nb_programs; i++) { + AVProgram *program = s->programs[i]; + struct variant *var = c->variants[i]; + if (program->discard < AVDISCARD_ALL) { + for (j = 0; j < var->n_playlists; j++) { + if (var->playlists[j] == pls && !var->playlists[j]->parsed) + return 1; + } + } + } + /* check if any of the streams in the playlist are needed */ for (i = 0; i < pls->n_main_streams; i++) { if (pls->main_streams[i]->discard < AVDISCARD_ALL) { @@ -1324,7 +1344,7 @@ restart: /* Check that the playlist is still needed before opening a new * segment. */ - v->needed = playlist_needed(v); + v->needed = playlist_needed(v->parent, v, 1); if (!v->needed) { av_log(v->parent, AV_LOG_INFO, "No longer receiving playlist %d\n", @@ -1418,23 +1438,41 @@ reload: static void add_renditions_to_variant(HLSContext *c, struct variant *var, enum AVMediaType type, const char *group_id) { - int i; + int i, j; + int found; for (i = 0; i < c->n_renditions; i++) { struct rendition *rend = c->renditions[i]; if (rend->type == type && !strcmp(rend->group_id, group_id)) { - if (rend->playlist) + if (rend->playlist) { /* rendition is an external playlist * => add the playlist to the variant */ - dynarray_add(&var->playlists, &var->n_playlists, rend->playlist); - else + found = 0; + for (j = 0; j < var->n_playlists; j++) { + if (var->playlists[j] == rend->playlist) { + found = 1; + break; + } + } + if (!found) + dynarray_add(&var->playlists, &var->n_playlists, rend->playlist); + } else { /* rendition is part of the variant main Media Playlist * => add the rendition to the main Media Playlist */ - dynarray_add(&var->playlists[0]->renditions, - &var->playlists[0]->n_renditions, - rend); + found = 0; + for (j = 0; j < var->playlists[0]->n_renditions; j++) { + if (var->playlists[0]->renditions[j] == rend) { + found = 1; + break; + } + } + if (!found) + dynarray_add(&var->playlists[0]->renditions, + &var->playlists[0]->n_renditions, + rend); + } } } } @@ -1831,8 +1869,15 @@ static int hls_read_header(AVFormatContext *s) goto fail; } /* If the playlist only contained playlists (Master Playlist), - * parse each individual playlist. */ - if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) { + * parse all individual playlists. + If option load_all_variants is false, load only first variant */ + if (!c->load_all_variants && c->n_variants > 1) { + for (i = 0; i < c->variants[0]->n_playlists; i++) { + struct playlist *pls = c->variants[0]->playlists[i]; + if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) + goto fail; + } + } else if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) { for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) @@ -1876,6 +1921,10 @@ static int hls_read_header(AVFormatContext *s) if (!program) goto fail; av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0); + + /* start with the first variant and disable all others */ + if (i > 0 && !c->load_all_variants) + program->discard = AVDISCARD_ALL; } /* Select the starting segments */ @@ -1893,6 +1942,9 @@ static int hls_read_header(AVFormatContext *s) for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; + if (!pls->parsed) + continue; + if ((ret = init_playlist(c, pls)) < 0) goto fail; @@ -1906,6 +1958,26 @@ fail: return ret; } +static void activate_playlist(AVFormatContext *s, struct playlist *pls) { + + HLSContext *c = s->priv_data; + + if (pls->index < c->n_variants) { + + struct variant *var = c->variants[pls->index]; + + if (parse_playlist(c, pls->url, pls, NULL) < 0) + return; + if (var->audio_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_AUDIO, var->audio_group); + if (var->video_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_VIDEO, var->video_group); + if (var->subtitles_group[0]) + add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group); + init_playlist(c, pls); + } +} + static int recheck_discard_flags(AVFormatContext *s, int first) { HLSContext *c = s->priv_data; @@ -1916,9 +1988,11 @@ static int recheck_discard_flags(AVFormatContext *s, int first) for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; - cur_needed = playlist_needed(c->playlists[i]); + cur_needed = playlist_needed(s, c->playlists[i], 0); if (cur_needed && !pls->needed) { + if (!pls->parsed) + activate_playlist(s, pls); pls->needed = 1; changed = 1; pls->cur_seq_no = select_cur_seq_no(c, pls); @@ -2165,6 +2239,8 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, for (i = 0; i < c->n_playlists; i++) { /* Reset reading */ struct playlist *pls = c->playlists[i]; + if (!pls->parsed) + continue; if (pls->input) ff_format_io_close(pls->parent, &pls->input); av_packet_unref(&pls->pkt); @@ -2221,6 +2297,8 @@ static const AVOption hls_options[] = { INT_MIN, INT_MAX, FLAGS}, {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded", OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS}, + {"load_all_variants", "parse all playlists of all variants at startup", + OFFSET(load_all_variants), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS}, {NULL} }; -- 2.14.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel