[FFmpeg-devel] [PATCH] hlsenc: single_file, support HLS ver 4 byteranges

Michael Niedermayer michaelni at gmx.at
Mon Sep 15 00:50:03 CEST 2014


On Sun, Sep 14, 2014 at 03:37:00PM +0300, Mika Raento wrote:
> This adds a new option -hls_flags single_file that creates one .ts file
> for HLS and adds byteranges to the .m3u8 file, instead of creating one
> .ts file for each segment.
> 
> This is helpful at least for storing large number of videos, as the
> number of files per video is drastically reduced and copying and storing
> those files takes less requests and inodes.
> 
> This is based on work by Nicolas Martyanoff, discussed on ffmpeg-devel
> in July 2014. That patch seems abandoned by the author, and contained
> unrelated changes. This patch tries to add the minimum amount of code to
> support the byterange playlists.
> ---
>  doc/muxers.texi      | 23 +++++++++++++++----
>  libavformat/hlsenc.c | 65 ++++++++++++++++++++++++++++++++++++++++++----------
>  2 files changed, 72 insertions(+), 16 deletions(-)
> 
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 57e81f4..40ae857 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -194,15 +194,19 @@ can not be smaller than one centi second.
>  Apple HTTP Live Streaming muxer that segments MPEG-TS according to
>  the HTTP Live Streaming (HLS) specification.
>  
> -It creates a playlist file and numbered segment files. The output
> -filename specifies the playlist filename; the segment filenames
> -receive the same basename as the playlist, a sequential number and
> -a .ts extension.
> +It creates a playlist file, and one or more segment files. The output filename
> +specifies the playlist filename.
> +
> +By default, the muxer creates a file for each segment produced. These files
> +have the same name as the playlist, followed by a sequential number and a
> +.ts extension.
>  
>  For example, to convert an input file with @command{ffmpeg}:
>  @example
>  ffmpeg -i in.nut out.m3u8
>  @end example
> +This example will produce the playlist, @file{out.m3u8}, and segment files:
> + at file{out0.ts}, @file{out1.ts}, @file{out2.ts}, etc.
>  
>  See also the @ref{segment} muxer, which provides a more generic and
>  flexible implementation of a segmenter, and can be used to perform HLS
> @@ -241,6 +245,17 @@ Note that the playlist sequence number must be unique for each segment
>  and it is not to be confused with the segment filename sequence number
>  which can be cyclic, for example if the @option{wrap} option is
>  specified.
> +
> + at item hls_flags single_file
> +If this flag is set, the muxer will store all segments in a single MPEG-TS
> +file, and will use byte ranges in the playlist. HLS playlists generated with
> +this way will have the version number 4.
> +For example:
> + at example
> +ffmpeg -i in.nut -hls_flags single_file out.m3u8
> + at end example
> +Will produce the playlist, @file{out.m3u8}, and a single segment file,
> + at file{out.ts}.
>  @end table
>  
>  @anchor{ico}
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index 11f1e5b..9fcb999 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -34,10 +34,17 @@
>  typedef struct HLSSegment {
>      char filename[1024];
>      double duration; /* in seconds */
> +    int64_t pos;
> +    int64_t size;
>  
>      struct HLSSegment *next;
>  } HLSSegment;
>  
> +typedef enum HLSFlags {
> +    // Generate a single media file and use byte ranges in the playlist.
> +    HLS_SINGLE_FILE = (1 << 0),
> +} HLSFlags;
> +
>  typedef struct HLSContext {
>      const AVClass *class;  // Class for private options.
>      unsigned number;
> @@ -50,12 +57,15 @@ typedef struct HLSContext {
>      float time;            // Set by a private option.
>      int max_nb_segments;   // Set by a private option.
>      int  wrap;             // Set by a private option.
> +    uint32_t flags;        // enum HLSFlags
>  
>      int64_t recording_time;
>      int has_video;
>      int64_t start_pts;
>      int64_t end_pts;
>      double duration;      // last segment duration computed so far, in seconds
> +    int64_t start_pos;    // last segment starting position
> +    int64_t size;         // last segment size
>      int nb_entries;
>  
>      HLSSegment *segments;
> @@ -88,12 +98,14 @@ static int hls_mux_init(AVFormatContext *s)
>          avcodec_copy_context(st->codec, s->streams[i]->codec);
>          st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
>      }
> +    hls->start_pos = 0;
>  
>      return 0;
>  }
>  
>  /* Create a new segment and append it to the segment list */
> -static int hls_append_segment(HLSContext *hls, double duration)
> +static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
> +                              int64_t size)
>  {
>      HLSSegment *en = av_malloc(sizeof(*en));
>  
> @@ -103,6 +115,8 @@ static int hls_append_segment(HLSContext *hls, double duration)
>      av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
>  
>      en->duration = duration;
> +    en->pos      = pos;
> +    en->size     = size;
>      en->next     = NULL;
>  
>      if (!hls->segments)
> @@ -142,6 +156,7 @@ static int hls_window(AVFormatContext *s, int last)
>      int target_duration = 0;
>      int ret = 0;
>      int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
> +    int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
>  
>      if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE,
>                            &s->interrupt_callback, NULL)) < 0)
> @@ -153,7 +168,7 @@ static int hls_window(AVFormatContext *s, int last)
>      }
>  
>      avio_printf(hls->pb, "#EXTM3U\n");
> -    avio_printf(hls->pb, "#EXT-X-VERSION:3\n");
> +    avio_printf(hls->pb, "#EXT-X-VERSION:%d\n", version);
>      avio_printf(hls->pb, "#EXT-X-TARGETDURATION:%d\n", target_duration);
>      avio_printf(hls->pb, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
>  
> @@ -162,6 +177,9 @@ static int hls_window(AVFormatContext *s, int last)
>  
>      for (en = hls->segments; en; en = en->next) {
>          avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration);
> +        if (hls->flags & HLS_SINGLE_FILE)
> +             avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
> +                         en->size, en->pos);
>          if (hls->baseurl)
>              avio_printf(hls->pb, "%s", hls->baseurl);
>          avio_printf(hls->pb, "%s\n", en->filename);
> @@ -181,11 +199,15 @@ static int hls_start(AVFormatContext *s)
>      AVFormatContext *oc = c->avf;
>      int err = 0;
>  
> -    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
> -                              c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
> -        av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
> -        return AVERROR(EINVAL);
> -    }
> +    if (c->flags & HLS_SINGLE_FILE)
> +        av_strlcpy(oc->filename, c->basename,
> +                   sizeof(oc->filename));
> +    else
> +        if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
> +                                  c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
> +            av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
> +            return AVERROR(EINVAL);
> +        }
>      c->number++;
>  
>      if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
> @@ -210,6 +232,9 @@ static int hls_write_header(AVFormatContext *s)
>      hls->recording_time = hls->time * AV_TIME_BASE;
>      hls->start_pts      = AV_NOPTS_VALUE;
>  
> +    if (hls->flags & HLS_SINGLE_FILE)
> +        pattern = ".ts";
> +
>      for (i = 0; i < s->nb_streams; i++)
>          hls->has_video +=
>              s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO;

> @@ -289,17 +314,27 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
>  
>      if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
>                                     end_pts, AV_TIME_BASE_Q) >= 0) {
> -        ret = hls_append_segment(hls, hls->duration);
> +        av_write_frame(oc, NULL); /* Flush any buffered data */
> +
> +        hls->size = hls->avf->pb->pos - hls->start_pos;

is it intended to access pos directly instead of using avio_tell() ?


> +        int64_t start_pos = hls->avf->pb->pos;

this mixes declarations and statements, some compilers dislike that


> +        ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
> +        hls->start_pos = start_pos;
>          if (ret)
>              return ret;
>  
>          hls->end_pts = pkt->pts;
>          hls->duration = 0;
>  
> -        av_write_frame(oc, NULL); /* Flush any buffered data */
> -        avio_close(oc->pb);
> +        if (hls->flags & HLS_SINGLE_FILE) {
> +            if (hls->avf->oformat->priv_class && hls->avf->priv_data)
> +                av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
> +            hls->number++;
> +        } else {
> +            avio_close(oc->pb);
>  
> -        ret = hls_start(s);
> +            ret = hls_start(s);
> +        }
>  
>          if (ret)
>              return ret;
> @@ -321,10 +356,13 @@ static int hls_write_trailer(struct AVFormatContext *s)
>      AVFormatContext *oc = hls->avf;
>  
>      av_write_trailer(oc);
> +    hls->size = hls->avf->pb->pos - hls->start_pos;
>      avio_closep(&oc->pb);
>      avformat_free_context(oc);
>      av_free(hls->basename);

> -    hls_append_segment(hls, hls->duration);
> +    if (hls->duration > 0.0) {
> +        hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
> +    }

is this change related to the rest of the patch or a unrelated
bugfix ? if the later please split it in a seperate patch


[...]

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Avoid a single point of failure, be that a person or equipment.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140915/63eaa77c/attachment.asc>


More information about the ffmpeg-devel mailing list