[FFmpeg-devel] [PATCH] lavf/segment: add reset_timestamps option

Stefano Sabatini stefasab at gmail.com
Thu Nov 29 13:46:53 CET 2012


The new options reset the timestamps at each new segment, so that the
generated segments will have timestamps starting from 0.

It is meant to address trac ticket #1425.
---
 doc/muxers.texi       |    7 +++++++
 libavformat/segment.c |   56 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 1c8f93b..be22298 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -588,8 +588,15 @@ the specified time and the time set by @var{force_key_frames}.
 @item segment_times @var{times}
 Specify a list of split points. @var{times} contains a list of comma
 separated duration specifications, in increasing order.
+
 @item segment_wrap @var{limit}
 Wrap around segment index once it reaches @var{limit}.
+
+ at item reset_timestamps @var{1|0}
+Reset timestamps at the begin of each segment, so that each segment
+will start with near-zero timestamps. It is meant to ease the playback
+of the generated segments. May not work with some combinations of
+muxers/codecs. It is set to @code{0} by default.
 @end table
 
 Some examples follow.
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 1ad410e..7c8eb28 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -35,6 +35,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/mathematics.h"
+#include "libavutil/timestamp.h"
 
 typedef enum {
     LIST_TYPE_UNDEFINED = -1,
@@ -49,6 +50,10 @@ typedef enum {
 #define SEGMENT_LIST_FLAG_CACHE 1
 #define SEGMENT_LIST_FLAG_LIVE  2
 
+struct stream_timestamps {
+    int64_t last_out_pts, last_out_dts, start_pts, start_dts;
+};
+
 typedef struct {
     const AVClass *class;  /**< Class for private options. */
     int segment_idx;       ///< index of the segment file to write, starting from 0
@@ -72,6 +77,10 @@ typedef struct {
     int64_t time_delta;
     int  individual_header_trailer; /**< Set by a private option. */
     int  write_header_trailer; /**< Set by a private option. */
+
+    int reset_timestamps;  ///< reset timestamps at the begin of each segment
+    struct stream_timestamps *timestamps;
+    int nb_timestamps;
     int has_video;
     double start_time, end_time;
 } SegmentContext;
@@ -131,7 +140,7 @@ static int segment_start(AVFormatContext *s, int write_header)
 {
     SegmentContext *seg = s->priv_data;
     AVFormatContext *oc = seg->avf;
-    int err = 0;
+    int err = 0, i;
 
     if (write_header) {
         avformat_free_context(oc);
@@ -163,6 +172,9 @@ static int segment_start(AVFormatContext *s, int write_header)
         if ((err = avformat_write_header(oc, NULL)) < 0)
             return err;
     }
+    if (seg->reset_timestamps)
+        for (i = 0; i < seg->nb_timestamps; i++)
+             seg->timestamps[i].start_pts = seg->timestamps[i].start_dts = AV_NOPTS_VALUE;
 
     return 0;
 }
@@ -379,9 +391,19 @@ static int seg_write_header(AVFormatContext *s)
     if (seg->list_type == LIST_TYPE_EXT)
         av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
 
-    for (i = 0; i < s->nb_streams; i++)
+    seg->timestamps = av_malloc(sizeof(*seg->timestamps) * s->nb_streams);
+    if (!seg->timestamps) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+    seg->nb_timestamps = s->nb_streams;
+
+    for (i = 0; i < seg->nb_timestamps; i++) {
         seg->has_video +=
             (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
+        seg->timestamps[i].last_out_pts = seg->timestamps[i].start_pts =
+        seg->timestamps[i].last_out_dts = seg->timestamps[i].start_dts = AV_NOPTS_VALUE;
+    }
 
     if (seg->has_video > 1)
         av_log(s, AV_LOG_WARNING,
@@ -486,6 +508,33 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
                               (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
     }
 
+    if (seg->reset_timestamps) {
+        struct stream_timestamps *ts = &seg->timestamps[pkt->stream_index];
+
+        /* compute initial timestamps */
+        if (ts->start_pts == AV_NOPTS_VALUE)
+            ts->start_pts = pkt->pts != AV_NOPTS_VALUE ? pkt->pts : ts->last_out_pts;
+        if (ts->start_dts == AV_NOPTS_VALUE)
+            ts->start_dts = pkt->dts != AV_NOPTS_VALUE ? pkt->dts : ts->last_out_dts;
+
+        av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s",
+               av_ts2str(ts->start_pts), av_ts2str(pkt->pts), av_ts2str(ts->start_dts), av_ts2str(pkt->dts));
+
+        /* compute last timestamps */
+        if (pkt->pts != AV_NOPTS_VALUE)
+            ts->last_out_pts = pkt->pts + (pkt->duration <= 0 ? 1 : pkt->duration);
+        if (pkt->dts != AV_NOPTS_VALUE)
+            ts->last_out_dts = pkt->dts + (pkt->duration <= 0 ? 1 : pkt->duration);
+
+        /* compute new timestamps */
+        if (ts->start_pts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE)
+            pkt->pts -= ts->start_pts;
+        if (ts->start_dts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE)
+            pkt->dts -= ts->start_dts;
+
+        av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n", av_ts2str(pkt->pts), av_ts2str(pkt->dts));
+    }
+
     ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
 
 fail:
@@ -518,6 +567,7 @@ fail:
 
     av_opt_free(seg);
     av_freep(&seg->times);
+    av_freep(&seg->timestamps);
 
     avformat_free_context(oc);
     return ret;
@@ -544,8 +594,10 @@ static const AVOption options[] = {
     { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
     { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
     { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
+
     { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
     { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
+    { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
     { NULL },
 };
 
-- 
1.7.9.5



More information about the ffmpeg-devel mailing list