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: > +@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. > + > +@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: > +@example > +ffmpeg -i in.nut -hls_flags single_file out.m3u8 > +@end example > +Will produce the playlist, @file{out.m3u8}, and a single segment file, > +@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.
signature.asc
Description: Digital signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel