[FFmpeg-devel] [PATCH 13/18] avformat/hls: demux each subtitle segment separately

Anssi Hannula anssi.hannula at iki.fi
Mon Dec 30 13:38:55 CET 2013


30.12.2013 13:14, Anssi Hannula kirjoitti:
> 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.

... and set prefer_hls_mpegts_pts=1 for WebVTT demuxer to get the
special timestamps we want. Splitted this to a separate patch locally.

> 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;
>  }
>  
> 


-- 
Anssi Hannula


More information about the ffmpeg-devel mailing list