adder: rework timestamping
authorStefan Kost <ensonic@users.sf.net>
Wed, 14 Apr 2010 20:31:20 +0000 (23:31 +0300)
committerStefan Kost <ensonic@users.sf.net>
Fri, 30 Apr 2010 06:24:13 +0000 (09:24 +0300)
Adder was using always incrementing timestamps. Seeking was done by setting the
position in the newsegment event. This was failing when doing segmented seeks
with rate<0.0, as offset (and thus timestamp) would go below 0.

Now we take both cur and end from the seek event. We construct newsegment events
depending including cur and end from the seek event. We set position to the
start of the segment. Timestamp is set to start or end of segment depending on
rate. Offset is recalculated.

gst/adder/gstadder.c
gst/adder/gstadder.h

index 7958721..cdf9648 100644 (file)
@@ -698,13 +698,13 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
     case GST_EVENT_SEEK:
     {
       GstSeekFlags flags;
-      GstSeekType curtype;
-      gint64 cur;
+      GstSeekType curtype, endtype;
+      gint64 cur, end;
       gboolean flush;
 
       /* parse the seek parameters */
       gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
-          &cur, NULL, NULL);
+          &cur, &endtype, &end);
 
       flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
 
@@ -724,9 +724,13 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
        * new collect function will be called for as long as we're flushing. */
       GST_OBJECT_LOCK (adder->collect);
       if (curtype == GST_SEEK_TYPE_SET)
-        adder->segment_position = cur;
+        adder->segment_start = cur;
       else
-        adder->segment_position = 0;
+        adder->segment_start = 0;
+      if (endtype == GST_SEEK_TYPE_SET)
+        adder->segment_end = end;
+      else
+        adder->segment_end = GST_CLOCK_TIME_NONE;
       /* make sure we push a new segment, to inform about new basetime
        * see FIXME in gst_adder_collected() */
       adder->segment_pending = TRUE;
@@ -1071,6 +1075,8 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
   GstBuffer *outbuf = NULL, *gapbuf = NULL;
   gpointer outdata = NULL;
   guint outsize;
+  gint64 next_offset;
+  gint64 next_timestamp;
 
   adder = GST_ADDER (user_data);
 
@@ -1178,37 +1184,30 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
     /* we had an output buffer, unref the gapbuffer we kept */
     gst_buffer_unref (gapbuf);
 
-  /* our timestamping is very simple, just an ever incrementing
-   * counter, the new segment time will take care of their respective
-   * stream time. */
   if (adder->segment_pending) {
     GstEvent *event;
 
     /* FIXME, use rate/applied_rate as set on all sinkpads.
      * - currently we just set rate as received from last seek-event
-     * We could potentially figure out the duration as well using
-     * the current segment positions and the stated stop positions.
-     * Also we just start from stream time 0 which is rather
-     * weird. For non-synchronized mixing, the time should be
-     * the min of the stream times of all received segments,
-     * rationale being that the duration is at least going to
-     * be as long as the earliest stream we start mixing. This
-     * would also be correct for synchronized mixing but then
-     * the later streams would be delayed until the stream times
-     * match.
+     *
+     * When seeking we set the start and stop positions as given in the seek
+     * event. We also adjust offset & timestamp acordingly.
+     * This basically ignores all newsegments sent by upstream.
      */
+    event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
+        1.0, GST_FORMAT_TIME, adder->segment_start, adder->segment_end,
+        adder->segment_start);
     if (adder->segment_rate > 0.0) {
-      event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
-          1.0, GST_FORMAT_TIME, adder->timestamp, GST_CLOCK_TIME_NONE,
-          adder->segment_position);
+      adder->timestamp = adder->segment_start;
     } else {
-      event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
-          1.0, GST_FORMAT_TIME, G_GINT64_CONSTANT (0), adder->timestamp,
-          adder->segment_position);
+      adder->timestamp = adder->segment_end;
     }
-    GST_INFO_OBJECT (adder->srcpad, "new segment event for "
-        "rate:%lf start:%" G_GINT64_FORMAT "  cur:%" G_GUINT64_FORMAT,
-        adder->segment_rate, adder->timestamp, adder->segment_position);
+    adder->offset = gst_util_uint64_scale (adder->timestamp,
+        adder->rate, GST_SECOND);
+    GST_INFO_OBJECT (adder, "seg_start %" G_GUINT64_FORMAT ", seg_end %"
+        G_GUINT64_FORMAT, adder->segment_start, adder->segment_end);
+    GST_INFO_OBJECT (adder, "timestamp %" G_GINT64_FORMAT ",new offset %"
+        G_GINT64_FORMAT, adder->timestamp, adder->offset);
 
     if (event) {
       if (!gst_pad_push_event (adder->srcpad, event)) {
@@ -1216,11 +1215,10 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
             event, GST_EVENT_TYPE_NAME (event));
       }
       adder->segment_pending = FALSE;
-      adder->segment_position = 0;
     } else {
       GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
-          "start:%" G_GINT64_FORMAT "  pos:%" G_GUINT64_FORMAT " failed",
-          adder->timestamp, adder->segment_position);
+          "start:%" G_GINT64_FORMAT "  end:%" G_GINT64_FORMAT " failed",
+          adder->segment_start, adder->segment_end);
     }
   }
 
@@ -1237,27 +1235,35 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
     adder->pending_events = NULL;
   }
 
-  /* set timestamps on the output buffer */
-  GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
-  GST_BUFFER_OFFSET (outbuf) = adder->offset;
-
   /* for the next timestamp, use the sample counter, which will
    * never accumulate rounding errors */
   if (adder->segment_rate > 0.0) {
-    adder->offset += outsize / adder->bps;
+    next_offset = adder->offset + outsize / adder->bps;
+  } else {
+    next_offset = adder->offset - outsize / adder->bps;
+  }
+  next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, adder->rate);
+
+
+  /* set timestamps on the output buffer */
+  if (adder->segment_rate > 0.0) {
+    GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
+    GST_BUFFER_OFFSET (outbuf) = adder->offset;
+    GST_BUFFER_DURATION (outbuf) = next_timestamp - adder->timestamp;
   } else {
-    adder->offset -= outsize / adder->bps;
+    GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp;
+    GST_BUFFER_OFFSET (outbuf) = next_offset;
+    GST_BUFFER_DURATION (outbuf) = adder->timestamp - next_timestamp;
   }
-  adder->timestamp = gst_util_uint64_scale_int (adder->offset,
-      GST_SECOND, adder->rate);
 
-  /* now we can set the duration of the buffer */
-  GST_BUFFER_DURATION (outbuf) = adder->timestamp -
-      GST_BUFFER_TIMESTAMP (outbuf);
+  adder->offset = next_offset;
+  adder->timestamp = next_timestamp;
 
   /* send it out */
-  GST_LOG_OBJECT (adder, "pushing outbuf %p, timestamp %" GST_TIME_FORMAT,
-      outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
+  GST_LOG_OBJECT (adder, "pushing outbuf %p, timestamp %" GST_TIME_FORMAT
+      " offset %" G_GINT64_FORMAT, outbuf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_BUFFER_OFFSET (outbuf));
   ret = gst_pad_push (adder->srcpad, outbuf);
 
   GST_LOG_OBJECT (adder, "pushed outbuf, result = %s", gst_flow_get_name (ret));
@@ -1295,7 +1301,8 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
       adder->offset = 0;
       adder->flush_stop_pending = FALSE;
       adder->segment_pending = TRUE;
-      adder->segment_position = 0;
+      adder->segment_start = 0;
+      adder->segment_end = GST_CLOCK_TIME_NONE;
       adder->segment_rate = 1.0;
       gst_segment_init (&adder->segment, GST_FORMAT_UNDEFINED);
       gst_collect_pads_start (adder->collect);
index 3832dfd..1f78c75 100644 (file)
@@ -85,7 +85,7 @@ struct _GstAdder {
   GstPadEventFunction  collect_event;
   GstSegment      segment;
   gboolean        segment_pending;
-  guint64         segment_position;
+  guint64         segment_start, segment_end;
   gdouble         segment_rate;
   /* src event handling */
   gboolean        flush_stop_pending;