[FFmpeg-devel] [PATCH 13/18] avformat/hls: demux each subtitle segment separately
Anssi Hannula
anssi.hannula at iki.fi
Mon Dec 30 12:14:27 CET 2013
Subtitle demuxers (specifically WebVTT) read the entire file in
read_header(), so only provide them a single segment at a time, and open
the demuxer again for a following segment.
Signed-off-by: Anssi Hannula <anssi.hannula at iki.fi>
---
Again a bit hacky, but seems to be the norm for HLS...
libavformat/hls.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/libavformat/hls.c b/libavformat/hls.c
index f4eb1a1..80a8546 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -101,6 +101,9 @@ struct playlist {
char key_url[MAX_URL_SIZE];
uint8_t key[16];
+ int is_subtitle;
+ int reopen_subtitle;
+
int is_id3_timestamped; /* -1: not yet known */
int64_t id3_mpegts_timestamp; /* in mpegts tb */
int64_t id3_offset; /* in stream original tb */
@@ -155,6 +158,7 @@ typedef struct HLSContext {
int64_t seek_timestamp;
int seek_flags;
AVIOInterruptCB *interrupt_callback;
+ AVDictionary *demuxer_options;
char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context
@@ -373,9 +377,12 @@ static struct rendition *new_rendition(HLSContext *c, struct rendition_info *inf
/* add the playlist if this is an external rendition */
if (info->uri[0]) {
rend->playlist = new_playlist(c, info->uri, url_base);
- if (rend->playlist)
+ if (rend->playlist) {
dynarray_add(&rend->playlist->renditions,
&rend->playlist->n_renditions, rend);
+ if (type == AVMEDIA_TYPE_SUBTITLE)
+ rend->playlist->is_subtitle = 1;
+ }
}
if (info->assoc_language[0]) {
@@ -727,6 +734,14 @@ cleanup:
return ret;
}
+static void after_segment_switch(struct playlist *pls)
+{
+ /* subtitle demuxers may try to consume the entire stream, so report
+ * EOF to them on segment switches and reopen on next request */
+ if (pls->is_subtitle && pls->ctx)
+ pls->reopen_subtitle = 1;
+}
+
static int read_data(void *opaque, uint8_t *buf, int buf_size)
{
struct playlist *v = opaque;
@@ -736,7 +751,7 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size)
int just_opened = 0;
struct segment *seg;
- if (!v->needed)
+ if (!v->needed || v->reopen_subtitle)
return AVERROR_EOF;
restart:
@@ -822,6 +837,7 @@ reload:
c->end_of_segment = 1;
c->cur_seq_no = v->cur_seq_no;
+ after_segment_switch(v);
if (v->ctx && v->ctx->nb_streams &&
v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) {
@@ -987,10 +1003,13 @@ static int hls_read_header(AVFormatContext *s)
add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group);
}
+ av_dict_set(&c->demuxer_options, "prefer_hls_mpegts_pts", "1", 0);
+
/* Open the demuxer for each playlist */
for (i = 0; i < c->n_playlists; i++) {
struct playlist *pls = c->playlists[i];
AVInputFormat *in_fmt = NULL;
+ AVDictionary *opts = NULL;
if (pls->n_segments == 0)
continue;
@@ -1026,9 +1045,12 @@ static int hls_read_header(AVFormatContext *s)
pls->ctx = NULL;
goto fail;
}
+
pls->ctx->pb = &pls->pb;
pls->stream_offset = stream_offset;
- ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, NULL);
+ av_dict_copy(&opts, c->demuxer_options, 0);
+ ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &opts);
+ av_dict_free(&opts);
if (ret < 0)
goto fail;
@@ -1106,6 +1128,7 @@ fail:
free_playlist_list(c);
free_variant_list(c);
free_rendition_list(c);
+ av_dict_free(&c->demuxer_options);
return ret;
}
@@ -1131,6 +1154,7 @@ static int recheck_discard_flags(AVFormatContext *s, int first)
changed = 1;
pls->cur_seq_no = c->cur_seq_no;
pls->pb.eof_reached = 0;
+ after_segment_switch(pls);
av_log(s, AV_LOG_INFO, "Now receiving playlist %d\n", i);
} else if (first && !pls->cur_needed && pls->needed) {
if (pls->input)
@@ -1174,10 +1198,30 @@ start:
int64_t ts_diff;
AVRational tb;
ret = av_read_frame(pls->ctx, &pls->pkt);
+
if (ret < 0) {
if (!url_feof(&pls->pb) && ret != AVERROR_EOF)
return ret;
reset_packet(&pls->pkt);
+
+ if (pls->reopen_subtitle) {
+ /* each subtitle segment is demuxed separately */
+ struct AVInputFormat *ifmt = pls->ctx->iformat;
+ AVDictionary *opts = NULL;
+
+ pls->reopen_subtitle = 0;
+ pls->ctx->pb = NULL;
+ avformat_close_input(&pls->ctx);
+ if (!(pls->ctx = avformat_alloc_context()))
+ return AVERROR(ENOMEM);
+
+ pls->ctx->pb = &pls->pb;
+ av_dict_copy(&opts, c->demuxer_options, 0);
+ avformat_open_input(&pls->ctx, NULL, ifmt, &opts);
+ av_dict_free(&opts);
+ continue;
+ }
+
break;
} else {
if (pls->is_id3_timestamped) {
@@ -1279,6 +1323,7 @@ static int hls_close(AVFormatContext *s)
free_playlist_list(c);
free_variant_list(c);
free_rendition_list(c);
+ av_dict_free(&c->demuxer_options);
return 0;
}
--
1.8.1.5
More information about the ffmpeg-devel
mailing list