On 15/09/14 01:50, "Michael Niedermayer" <michae...@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: >> +@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 Unrelated, will re-submit without it. Thanks. > > >[...] > >-- >Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB > >Avoid a single point of failure, be that a person or equipment. > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel