lavf: Provide a monotonic timestamp to the outside world
authorHarald Axmann <harald.axmann@hotmail.com>
Sun, 2 Dec 2012 18:27:21 +0000 (19:27 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Sat, 15 Dec 2012 20:28:45 +0000 (21:28 +0100)
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
libavformat/avformat.h
libavformat/options_table.h
libavformat/utils.c
tests/ref/fate/nuv-rtjpeg-fh

index 7500881..c907d4e 100644 (file)
@@ -630,6 +630,13 @@ typedef struct AVIndexEntry {
 #define AV_DISPOSITION_ATTACHED_PIC      0x0400
 
 /**
+ * Options for behavior on timestamp wrap detection.
+ */
+#define AV_PTS_WRAP_IGNORE      0   ///< ignore the wrap
+#define AV_PTS_WRAP_ADD_OFFSET  1   ///< add the format specific offset on wrap detection
+#define AV_PTS_WRAP_SUB_OFFSET  -1  ///< subtract the format specific offset on wrap detection
+
+/**
  * Stream structure.
  * New fields can be added to the end with minor version bumps.
  * Removal, reordering and changes to existing fields require a major
@@ -847,6 +854,23 @@ typedef struct AVStream {
      */
     int64_t mux_ts_offset;
 
+    /**
+     * Internal data to check for wrapping of the time stamp
+     */
+    int64_t pts_wrap_reference;
+
+    /**
+     * Options for behavior, when a wrap is detected.
+     *
+     * Defined by AV_PTS_WRAP_ values.
+     *
+     * If correction is enabled, there are two possibilities:
+     * If the first time stamp is near the wrap point, the wrap offset
+     * will be subtracted, which will create negative time stamps.
+     * Otherwise the offset will be added.
+     */
+    int pts_wrap_behavior;
+
 } AVStream;
 
 #define AV_PROGRAM_RUNNING 1
@@ -878,6 +902,9 @@ typedef struct AVProgram {
      */
     int64_t start_time;
     int64_t end_time;
+
+    int64_t pts_wrap_reference;    ///< reference dts for wrap detection
+    int pts_wrap_behavior;         ///< behavior on wrap detection
 } AVProgram;
 
 #define AVFMTCTX_NOHEADER      0x0001 /**< signal that no header is present
@@ -1169,6 +1196,13 @@ typedef struct AVFormatContext {
      */
     unsigned int skip_initial_bytes;
 
+    /**
+     * Correct single timestamp overflows
+     * - encoding: unused
+     * - decoding: Set by user via AVOPtions (NO direct access)
+     */
+    unsigned int correct_ts_overflow;
+
     /*****************************************************************
      * All fields below this line are not part of the public API. They
      * may not be used outside of libavformat and can be changed and
index 84fb5ee..300334f 100644 (file)
@@ -73,6 +73,7 @@ static const AVOption options[]={
 {"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D},
 {"avoid_negative_ts", "shift timestamps to make them positive. 1 enables, 0 disables, default of -1 enables when required by target format.", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, E},
 {"skip_initial_bytes", "skip initial bytes", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D},
+{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D},
 {NULL},
 };
 
index 596253b..f627f88 100644 (file)
@@ -78,6 +78,27 @@ static int is_relative(int64_t ts) {
     return ts > (RELATIVE_TS_BASE - (1LL<<48));
 }
 
+/**
+ * Wrap a given time stamp, if there is an indication for an overflow
+ *
+ * @param st stream
+ * @param timestamp the time stamp to wrap
+ * @return resulting time stamp
+ */
+static int64_t wrap_timestamp(AVStream *st, int64_t timestamp)
+{
+    if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE && st->pts_wrap_bits < 64 &&
+        st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
+        if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET &&
+            timestamp < st->pts_wrap_reference)
+            return timestamp + (1LL<<st->pts_wrap_bits);
+        else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET &&
+            timestamp >= st->pts_wrap_reference)
+            return timestamp - (1LL<<st->pts_wrap_bits);
+    }
+    return timestamp;
+}
+
 /** head of registered input format linked list */
 static AVInputFormat *first_iformat = NULL;
 /** head of registered output format linked list */
@@ -751,6 +772,8 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
         }
 
         st= s->streams[pkt->stream_index];
+        pkt->dts = wrap_timestamp(st, pkt->dts);
+        pkt->pts = wrap_timestamp(st, pkt->pts);
 
         force_codec_ids(s, st);
 
@@ -900,8 +923,67 @@ static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList
     return NULL;
 }
 
+static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index)
+{
+    if (s->correct_ts_overflow && st->pts_wrap_bits != 64 &&
+        st->pts_wrap_reference == AV_NOPTS_VALUE && st->first_dts != AV_NOPTS_VALUE) {
+        int i;
+
+        // reference time stamp should be 60 s before first time stamp
+        int64_t pts_wrap_reference = st->first_dts - av_rescale(60, st->time_base.den, st->time_base.num);
+        // if first time stamp is not more than 1/8 and 60s before the wrap point, subtract rather than add wrap offset
+        int pts_wrap_behavior = (st->first_dts < (1LL<<st->pts_wrap_bits) - (1LL<<st->pts_wrap_bits-3)) ||
+            (st->first_dts < (1LL<<st->pts_wrap_bits) - av_rescale(60, st->time_base.den, st->time_base.num)) ?
+            AV_PTS_WRAP_ADD_OFFSET : AV_PTS_WRAP_SUB_OFFSET;
+
+        AVProgram *first_program = av_find_program_from_stream(s, NULL, stream_index);
+
+        if (!first_program) {
+            int default_stream_index = av_find_default_stream_index(s);
+            if (s->streams[default_stream_index]->pts_wrap_reference == AV_NOPTS_VALUE) {
+                for (i=0; i<s->nb_streams; i++) {
+                    s->streams[i]->pts_wrap_reference = pts_wrap_reference;
+                    s->streams[i]->pts_wrap_behavior = pts_wrap_behavior;
+                }
+            }
+            else {
+                st->pts_wrap_reference = s->streams[default_stream_index]->pts_wrap_reference;
+                st->pts_wrap_behavior = s->streams[default_stream_index]->pts_wrap_behavior;
+            }
+        }
+        else {
+            AVProgram *program = first_program;
+            while (program) {
+                if (program->pts_wrap_reference != AV_NOPTS_VALUE) {
+                    pts_wrap_reference = program->pts_wrap_reference;
+                    pts_wrap_behavior = program->pts_wrap_behavior;
+                    break;
+                }
+                program = av_find_program_from_stream(s, program, stream_index);
+            }
+
+            // update every program with differing pts_wrap_reference
+            program = first_program;
+            while(program) {
+                if (program->pts_wrap_reference != pts_wrap_reference) {
+                    for (i=0; i<program->nb_stream_indexes; i++) {
+                        s->streams[program->stream_index[i]]->pts_wrap_reference = pts_wrap_reference;
+                        s->streams[program->stream_index[i]]->pts_wrap_behavior = pts_wrap_behavior;
+                    }
+
+                    program->pts_wrap_reference = pts_wrap_reference;
+                    program->pts_wrap_behavior = pts_wrap_behavior;
+                }
+                program = av_find_program_from_stream(s, program, stream_index);
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
 static void update_initial_timestamps(AVFormatContext *s, int stream_index,
-                                      int64_t dts, int64_t pts)
+                                      int64_t dts, int64_t pts, AVPacket *pkt)
 {
     AVStream *st= s->streams[stream_index];
     AVPacketList *pktl= s->parse_queue ? s->parse_queue : s->packet_buffer;
@@ -943,6 +1025,16 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index,
                 pktl->pkt.dts= pts_buffer[0];
         }
     }
+
+    if (update_wrap_reference(s, st, stream_index) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
+        // correct first time stamps to negative values
+        st->first_dts = wrap_timestamp(st, st->first_dts);
+        st->cur_dts = wrap_timestamp(st, st->cur_dts);
+        pkt->dts = wrap_timestamp(st, pkt->dts);
+        pkt->pts = wrap_timestamp(st, pkt->pts);
+        pts = wrap_timestamp(st, pts);
+    }
+
     if (st->start_time == AV_NOPTS_VALUE)
         st->start_time = pts;
 }
@@ -1088,7 +1180,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
             /* PTS = presentation timestamp */
             if (pkt->dts == AV_NOPTS_VALUE)
                 pkt->dts = st->last_IP_pts;
-            update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts);
+            update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt);
             if (pkt->dts == AV_NOPTS_VALUE)
                 pkt->dts = st->cur_dts;
 
@@ -1125,7 +1217,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
             if (pkt->pts == AV_NOPTS_VALUE)
                 pkt->pts = pkt->dts;
             update_initial_timestamps(s, pkt->stream_index, pkt->pts,
-                                      pkt->pts);
+                                      pkt->pts, pkt);
             if (pkt->pts == AV_NOPTS_VALUE)
                 pkt->pts = st->cur_dts;
             pkt->dts = pkt->pts;
@@ -1142,7 +1234,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st,
             pkt->dts= st->pts_buffer[0];
     }
     if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here
-        update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet
+        update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); // this should happen on the first packet
     }
     if(pkt->dts > st->cur_dts)
         st->cur_dts = pkt->dts;
@@ -1629,6 +1721,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries,
 int av_add_index_entry(AVStream *st,
                        int64_t pos, int64_t timestamp, int size, int distance, int flags)
 {
+    timestamp = wrap_timestamp(st, timestamp);
     return ff_add_index_entry(&st->index_entries, &st->nb_index_entries,
                               &st->index_entries_allocated_size, pos,
                               timestamp, size, distance, flags);
@@ -1675,6 +1768,12 @@ int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp,
                                      wanted_timestamp, flags);
 }
 
+static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit,
+                                 int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t ))
+{
+    return wrap_timestamp(s->streams[stream_index], read_timestamp(s, stream_index, ppos, pos_limit));
+}
+
 int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags)
 {
     AVInputFormat *avif= s->iformat;
@@ -1750,7 +1849,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
 
     if(ts_min == AV_NOPTS_VALUE){
         pos_min = s->data_offset;
-        ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+        ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
         if (ts_min == AV_NOPTS_VALUE)
             return -1;
     }
@@ -1766,7 +1865,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
         pos_max = filesize - 1;
         do{
             pos_max -= step;
-            ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step);
+            ts_max = ff_read_timestamp(s, stream_index, &pos_max, pos_max + step, read_timestamp);
             step += step;
         }while(ts_max == AV_NOPTS_VALUE && pos_max >= step);
         if (ts_max == AV_NOPTS_VALUE)
@@ -1774,7 +1873,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
 
         for(;;){
             int64_t tmp_pos= pos_max + 1;
-            int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
+            int64_t tmp_ts= ff_read_timestamp(s, stream_index, &tmp_pos, INT64_MAX, read_timestamp);
             if(tmp_ts == AV_NOPTS_VALUE)
                 break;
             ts_max= tmp_ts;
@@ -1821,7 +1920,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
             pos= pos_limit;
         start_pos= pos;
 
-        ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1
+        ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp); //may pass pos_limit instead of -1
         if(pos == pos_max)
             no_change++;
         else
@@ -1850,9 +1949,9 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts,
     ts  = (flags & AVSEEK_FLAG_BACKWARD) ?  ts_min :  ts_max;
 #if 0
     pos_min = pos;
-    ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+    ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
     pos_min++;
-    ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+    ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp);
     av_dlog(s, "pos=0x%"PRIx64" %s<=%s<=%s\n",
             pos, av_ts2str(ts_min), av_ts2str(target_ts), av_ts2str(ts_max));
 #endif
@@ -3191,6 +3290,8 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
     st->cur_dts = s->iformat ? RELATIVE_TS_BASE : 0;
     st->first_dts = AV_NOPTS_VALUE;
     st->probe_packets = MAX_PROBE_PACKETS;
+    st->pts_wrap_reference = AV_NOPTS_VALUE;
+    st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;
 
     /* default pts setting is MPEG-like */
     avpriv_set_pts_info(st, 33, 1, 90000);
@@ -3230,6 +3331,8 @@ AVProgram *av_new_program(AVFormatContext *ac, int id)
         program->discard = AVDISCARD_NONE;
     }
     program->id = id;
+    program->pts_wrap_reference = AV_NOPTS_VALUE;
+    program->pts_wrap_behavior = AV_PTS_WRAP_IGNORE;
 
     program->start_time =
     program->end_time   = AV_NOPTS_VALUE;
index 151ef88..beb6ccd 100644 (file)
@@ -1,51 +1,51 @@
 #tb 0: 1/50
-0,          0,          0,        1,   221184, 0xf48c94f6
-0,          2,          2,        1,   221184, 0x89b625b2
-0,          3,          3,        1,   221184, 0x37e04714
-0,          4,          4,        1,   221184, 0x4f4c5224
-0,          5,          5,        1,   221184, 0x9193c9f1
-0,          6,          6,        1,   221184, 0x5d1a6197
-0,          7,          7,        1,   221184, 0x40cd51e7
-0,          8,          8,        1,   221184, 0xb2c1a729
-0,         10,         10,        1,   221184, 0x998d6144
-0,         11,         11,        1,   221184, 0xf5d52311
-0,         12,         12,        1,   221184, 0xea9dd6bf
-0,         13,         13,        1,   221184, 0x0e2ed854
-0,         14,         14,        1,   221184, 0xe295ba58
-0,         15,         15,        1,   221184, 0x8aedbb69
-0,         16,         16,        1,   221184, 0x253c9aaa
-0,         17,         17,        1,   221184, 0x5eaf9fb1
-0,         18,         18,        1,   221184, 0xcdb5a0cb
-0,         19,         19,        1,   221184, 0xcdb5a0cb
-0,         20,         20,        1,   221184, 0x23f89994
-0,         21,         21,        1,   221184, 0x23f89994
-0,         22,         22,        1,   221184, 0x10dc98d6
-0,         23,         23,        1,   221184, 0x799b9d98
-0,         24,         24,        1,   221184, 0xb226996c
-0,         25,         25,        1,   221184, 0x0ac59a42
-0,         26,         26,        1,   221184, 0x87c2a654
-0,         27,         27,        1,   221184, 0xf4c1a711
-0,         28,         28,        1,   221184, 0xf60fa72e
-0,         29,         29,        1,   221184, 0xc8f8b6fc
-0,         30,         30,        1,   221184, 0xd709b813
-0,         31,         31,        1,   221184, 0x5fdfb76b
-0,         32,         32,        1,   221184, 0x5798b0aa
-0,         33,         33,        1,   221184, 0xf572b1c3
-0,         34,         34,        1,   221184, 0x14b0afdf
-0,         35,         35,        1,   221184, 0x0a66b5b8
-0,         36,         36,        1,   221184, 0xe316c620
-0,         37,         37,        1,   221184, 0xbc76c5c2
-0,         38,         38,        1,   221184, 0x77c7c5e5
-0,         39,         39,        1,   221184, 0xfc7ac63e
-0,         40,         40,        1,   221184, 0x05a29ffe
-0,         41,         41,        1,   221184, 0x9bffbf6c
-0,         42,         42,        1,   221184, 0x3c55be40
-0,         43,         43,        1,   221184, 0x6f46c14e
-0,         44,         44,        1,   221184, 0x9cf4ae70
-0,         45,         45,        1,   221184, 0xf205b2f8
-0,         46,         46,        1,   221184, 0x7180aff8
-0,         47,         47,        1,   221184, 0x125eaffe
-0,         48,         48,        1,   221184, 0x6970a32d
-0,         49,         49,        1,   221184, 0xaea79f62
-0,         50,         50,        1,   221184, 0x48d2a093
-0,         51,         51,        1,   221184, 0x10a59eb5
+0,         80,         80,        1,   221184, 0xf48c94f6
+0,         82,         82,        1,   221184, 0x89b625b2
+0,         83,         83,        1,   221184, 0x37e04714
+0,         84,         84,        1,   221184, 0x4f4c5224
+0,         85,         85,        1,   221184, 0x9193c9f1
+0,         86,         86,        1,   221184, 0x5d1a6197
+0,         87,         87,        1,   221184, 0x40cd51e7
+0,         88,         88,        1,   221184, 0xb2c1a729
+0,         90,         90,        1,   221184, 0x998d6144
+0,         91,         91,        1,   221184, 0xf5d52311
+0,         92,         92,        1,   221184, 0xea9dd6bf
+0,         93,         93,        1,   221184, 0x0e2ed854
+0,         94,         94,        1,   221184, 0xe295ba58
+0,         95,         95,        1,   221184, 0x8aedbb69
+0,         96,         96,        1,   221184, 0x253c9aaa
+0,         97,         97,        1,   221184, 0x5eaf9fb1
+0,         98,         98,        1,   221184, 0xcdb5a0cb
+0,         99,         99,        1,   221184, 0xcdb5a0cb
+0,        100,        100,        1,   221184, 0x23f89994
+0,        101,        101,        1,   221184, 0x23f89994
+0,        102,        102,        1,   221184, 0x10dc98d6
+0,        103,        103,        1,   221184, 0x799b9d98
+0,        104,        104,        1,   221184, 0xb226996c
+0,        105,        105,        1,   221184, 0x0ac59a42
+0,        106,        106,        1,   221184, 0x87c2a654
+0,        107,        107,        1,   221184, 0xf4c1a711
+0,        108,        108,        1,   221184, 0xf60fa72e
+0,        109,        109,        1,   221184, 0xc8f8b6fc
+0,        110,        110,        1,   221184, 0xd709b813
+0,        111,        111,        1,   221184, 0x5fdfb76b
+0,        112,        112,        1,   221184, 0x5798b0aa
+0,        113,        113,        1,   221184, 0xf572b1c3
+0,        114,        114,        1,   221184, 0x14b0afdf
+0,        115,        115,        1,   221184, 0x0a66b5b8
+0,        116,        116,        1,   221184, 0xe316c620
+0,        117,        117,        1,   221184, 0xbc76c5c2
+0,        118,        118,        1,   221184, 0x77c7c5e5
+0,        119,        119,        1,   221184, 0xfc7ac63e
+0,        120,        120,        1,   221184, 0x05a29ffe
+0,        121,        121,        1,   221184, 0x9bffbf6c
+0,        122,        122,        1,   221184, 0x3c55be40
+0,        123,        123,        1,   221184, 0x6f46c14e
+0,        124,        124,        1,   221184, 0x9cf4ae70
+0,        125,        125,        1,   221184, 0xf205b2f8
+0,        126,        126,        1,   221184, 0x7180aff8
+0,        127,        127,        1,   221184, 0x125eaffe
+0,        128,        128,        1,   221184, 0x6970a32d
+0,        129,        129,        1,   221184, 0xaea79f62
+0,        130,        130,        1,   221184, 0x48d2a093
+0,        131,        131,        1,   221184, 0x10a59eb5