diff --git a/doc/muxers.texi b/doc/muxers.texi index da7f186dbc..dc2a08bbef 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -879,6 +879,16 @@ Note that splitting may not be accurate, unless you force the reference stream key-frames at the given time. See the introductory notice and the examples below. +@item segment_atclocktime @var{1|0} +If set to "1" split at regular clock time intervals starting from 00:00 +o'clock. The @var{time} value specified in @option{segment_time} is +used for setting the length of the splitting interval. + +For example with @option{segment_time} set to "900" this makes it possible +to create files at 12:00 o'clock, 12:15, 12:30, etc. + +Default value is "0". + @item segment_time_delta @var{delta} Specify the accuracy time when selecting the start time for a segment, expressed as a duration specification. Default value is "0". diff --git a/libavformat/segment.c b/libavformat/segment.c index fe84f2710d..a09d49d4bf 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -27,6 +27,8 @@ /* #define DEBUG */ #include +#include +#include #include "avformat.h" #include "internal.h" @@ -73,6 +75,12 @@ typedef struct { char *list; ///< filename for the segment list file int list_flags; ///< flags affecting list generation int list_size; ///< number of entries for the segment list file + + int use_clocktime; ///< flag to cut segments at regular clock time + int64_t last_val; ///< remember last time for wrap around detection + int64_t last_cut; ///< remember last cut + int cut_pending; + char *entry_prefix; ///< prefix to add to list entry filenames ListType list_type; ///< set the list type AVIOContext *list_pb; ///< list file put-byte context @@ -661,6 +669,13 @@ fail: return ret; } +#if !HAVE_LOCALTIME_R +static void localtime_r(const time_t *t, struct tm *tm) +{ + *tm = *localtime(t); +} +#endif + static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) { SegmentContext *seg = s->priv_data; @@ -668,6 +683,10 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t end_pts = INT64_MAX, offset; int start_frame = INT_MAX; int ret; + struct tm ti; + struct timeval now; + int64_t usecs; + int64_t wrapped_val; if (seg->times) { end_pts = seg->segment_count < seg->nb_times ? @@ -676,7 +695,19 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) start_frame = seg->segment_count <= seg->nb_frames ? seg->frames[seg->segment_count] : INT_MAX; } else { - end_pts = seg->time * (seg->segment_count+1); + if (seg->use_clocktime) { + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &ti); + usecs = (int64_t)(ti.tm_hour*3600 + ti.tm_min*60 + ti.tm_sec) * 1000000 + now.tv_usec; + wrapped_val = usecs % seg->time; + if (seg->last_cut != usecs && wrapped_val < seg->last_val) { + seg->cut_pending = 1; + seg->last_cut = usecs; + } + seg->last_val = wrapped_val; + } else { + end_pts = seg->time * (seg->segment_count+1); + } } av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n", @@ -686,7 +717,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) if (pkt->stream_index == seg->reference_stream_index && pkt->flags & AV_PKT_FLAG_KEY && - (seg->frame_count >= start_frame || + (seg->cut_pending || seg->frame_count >= start_frame || (pkt->pts != AV_NOPTS_VALUE && av_compare_ts(pkt->pts, st->time_base, end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) { @@ -696,6 +727,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) if ((ret = segment_start(s, seg->individual_header_trailer)) < 0) goto fail; + seg->cut_pending = 0; seg->cur_entry.index = seg->segment_idx + seg->segment_idx_wrap*seg->segment_idx_wrap_nb; seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base); seg->cur_entry.start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q); @@ -795,6 +827,7 @@ static const AVOption options[] = { { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" }, { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" }, + { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E}, { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E }, { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },