[FFmpeg-devel] [PATCH v4 2/2] lavf/hls: add option to defer parsing of variants
Rainer Hochecker
fernetmenta at online.de
Sun Dec 3 16:54:36 EET 2017
---
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.
+
+ at 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
More information about the ffmpeg-devel
mailing list