[FFmpeg-devel] [PATCH] hlsenc: single_file, support HLS ver 4 byteranges
Raento Mika
mika.raento at elisa.fi
Mon Sep 15 08:34:44 CEST 2014
On 15/09/14 01:50, "Michael Niedermayer" <michaelni at gmx.at> wrote:
>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
Unrelated, will re-submit without it. Thanks.
>
>
>[...]
>
>--
>Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
>Avoid a single point of failure, be that a person or equipment.
>
More information about the ffmpeg-devel
mailing list