segment: add helper to get negative running-time
authorWim Taymans <wtaymans@redhat.com>
Wed, 18 Mar 2015 10:31:51 +0000 (11:31 +0100)
committerWim Taymans <wtaymans@redhat.com>
Wed, 18 Mar 2015 10:35:42 +0000 (11:35 +0100)
Add a helper method to get a running-time with a little more features
such as detecting if the value was before or after the segment and
negative running-time.

API: gst_segment_to_running_time_full()

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=740575

gst/gstsegment.c
gst/gstsegment.h
tests/check/gst/gstsegment.c
win32/common/libgstreamer.def

index 2f1b037..e132c65 100644 (file)
@@ -453,93 +453,168 @@ gst_segment_to_stream_time (const GstSegment * segment, GstFormat format,
 }
 
 /**
- * gst_segment_to_running_time:
+ * gst_segment_to_running_time_full:
  * @segment: a #GstSegment structure.
  * @format: the format of the segment.
  * @position: the position in the segment
+ * @running_time: result running-time
  *
  * Translate @position to the total running time using the currently configured
- * segment. Position is a value between @segment start and stop time.
+ * segment. Position is a value between @segment start and stop time. Compared to
+ * gst_segment_to_running_time() this function can return negative running-time
+ * and specify if the position was before or after the segment incase it is outside
+ * of the segment.
  *
- * This function is typically used by elements that need to synchronize to the
- * global clock in a pipeline. The running time is a constantly increasing value
- * starting from 0. When gst_segment_init() is called, this value will reset to
- * 0.
+ * This function is typically used by elements that need to synchronize buffers
+ * against the clock or eachother.
  *
- * This function returns -1 if the position is outside of @segment start and stop.
+ * When #GST_SEGMENT_RESULT_OK is returned, @position is between start and stop of
+ * @segment and thus resulted in a positive running-time returned in @running_time.
  *
- * Returns: the position as the total running time or -1 when an invalid position
- * was given.
+ * When @position is outside of the segment start and stop values,
+ * #GST_SEGMENT_RESULT_BEFORE or #GST_SEGMENT_RESULT_AFTER is returned depending
+ * if @position is respectively before or after the segment.
+ *
+ * When this function returns #GST_SEGMENT_RESULT_NEGATIVE, the returned
+ * @running_time should be negated to get the real negative running time.
+ *
+ * Returns: a #GstSegmentResult
+ *
+ * Since: 1.6
  */
-guint64
-gst_segment_to_running_time (const GstSegment * segment, GstFormat format,
-    guint64 position)
+GstSegmentResult
+gst_segment_to_running_time_full (const GstSegment * segment, GstFormat format,
+    guint64 position, guint64 * running_time)
 {
+  GstSegmentResult res;
   guint64 result;
-  guint64 start, stop;
+  guint64 start, stop, offset;
   gdouble abs_rate;
 
   if (G_UNLIKELY (position == -1)) {
     GST_DEBUG ("invalid position (-1)");
-    return -1;
+    res = GST_SEGMENT_RESULT_INVALID;
+    goto done;
   }
 
-  g_return_val_if_fail (segment != NULL, -1);
-  g_return_val_if_fail (segment->format == format, -1);
+  g_return_val_if_fail (segment != NULL, GST_SEGMENT_RESULT_INVALID);
+  g_return_val_if_fail (segment->format == format, GST_SEGMENT_RESULT_INVALID);
 
   start = segment->start;
-
-  if (segment->rate > 0.0)
-    start += segment->offset;
-
   /* before the segment boundary */
   if (G_UNLIKELY (position < start)) {
     GST_DEBUG ("position(%" G_GUINT64_FORMAT ") < start(%" G_GUINT64_FORMAT
         ")", position, start);
-    return -1;
+    if (G_LIKELY (segment->rate > 0.0))
+      res = GST_SEGMENT_RESULT_BEFORE;
+    else
+      res = GST_SEGMENT_RESULT_AFTER;
+    goto done;
   }
 
   stop = segment->stop;
+  /* after the segment boundary */
+  if (G_UNLIKELY (stop != -1 && position > stop)) {
+    GST_DEBUG ("position(%" G_GUINT64_FORMAT ") > stop(%" G_GUINT64_FORMAT
+        ")", position, stop);
+    if (G_LIKELY (segment->rate > 0.0))
+      res = GST_SEGMENT_RESULT_AFTER;
+    else
+      res = GST_SEGMENT_RESULT_BEFORE;
+    goto done;
+  }
+
+  offset = segment->offset;
 
   if (G_LIKELY (segment->rate > 0.0)) {
-    /* after of the segment boundary */
-    if (G_UNLIKELY (stop != -1 && position > stop)) {
-      GST_DEBUG ("position(%" G_GUINT64_FORMAT ") > stop(%" G_GUINT64_FORMAT
-          ")", position, stop);
-      return -1;
+    /* bring to uncorrected position in segment */
+    if (position < start + offset) {
+      /* negative value */
+      result = (start + offset) - position;
+      res = GST_SEGMENT_RESULT_NEGATIVE;
+    } else {
+      result = position - (start + offset);
+      res = GST_SEGMENT_RESULT_OK;
     }
+  } else {
+    /* cannot continue if no stop position set or invalid offset */
+    g_return_val_if_fail (stop != -1, GST_SEGMENT_RESULT_INVALID);
+    g_return_val_if_fail (stop >= segment->offset, GST_SEGMENT_RESULT_INVALID);
 
     /* bring to uncorrected position in segment */
-    result = position - start;
-  } else {
-    /* cannot continue if no stop position set or outside of
-     * the segment. */
-    if (G_UNLIKELY (stop == -1)) {
-      GST_DEBUG ("invalid stop (-1)");
-      return -1;
+    if (position > stop - offset) {
+      /* negative value */
+      result = position - (stop - offset);
+      res = GST_SEGMENT_RESULT_NEGATIVE;
+    } else {
+      result = (stop - offset) - position;
+      res = GST_SEGMENT_RESULT_OK;
     }
+  }
 
-    stop -= segment->offset;
-    if (G_UNLIKELY (position > stop)) {
-      GST_DEBUG ("position(%" G_GUINT64_FORMAT ") > stop(%" G_GUINT64_FORMAT
-          ")", position, stop);
-      return -1;
+  if (running_time) {
+    /* scale based on the rate, avoid division by and conversion to
+     * float when not needed */
+    abs_rate = ABS (segment->rate);
+    if (G_UNLIKELY (abs_rate != 1.0))
+      result /= abs_rate;
+
+    /* correct for base of the segment */
+    if (res == GST_SEGMENT_RESULT_OK)
+      /* positive, add base */
+      *running_time = result + segment->base;
+    else if (segment->base >= result) {
+      /* negative and base is bigger, subtract from base and we have a
+       * positive value again */
+      *running_time = segment->base - result;
+      res = GST_SEGMENT_RESULT_OK;
+    } else {
+      /* negative and base is smaller, subtract base and remainder is
+       * negative */
+      *running_time = result - segment->base;
     }
+  }
+  return res;
 
-    /* bring to uncorrected position in segment */
-    result = stop - position;
+done:
+  {
+    if (running_time)
+      *running_time = -1;
+    return res;
   }
+}
 
-  /* scale based on the rate, avoid division by and conversion to
-   * float when not needed */
-  abs_rate = ABS (segment->rate);
-  if (G_UNLIKELY (abs_rate != 1.0))
-    result /= abs_rate;
+/**
+ * gst_segment_to_running_time:
+ * @segment: a #GstSegment structure.
+ * @format: the format of the segment.
+ * @position: the position in the segment
+ *
+ * Translate @position to the total running time using the currently configured
+ * segment. Position is a value between @segment start and stop time.
+ *
+ * This function is typically used by elements that need to synchronize to the
+ * global clock in a pipeline. The running time is a constantly increasing value
+ * starting from 0. When gst_segment_init() is called, this value will reset to
+ * 0.
+ *
+ * This function returns -1 if the position is outside of @segment start and stop.
+ *
+ * Returns: the position as the total running time or -1 when an invalid position
+ * was given.
+ */
+guint64
+gst_segment_to_running_time (const GstSegment * segment, GstFormat format,
+    guint64 position)
+{
+  guint64 result;
+  GstSegmentResult res;
 
-  /* correct for base of the segment */
-  result += segment->base;
+  res = gst_segment_to_running_time_full (segment, format, position, &result);
+  if (res == GST_SEGMENT_RESULT_OK)
+    return result;
 
-  return result;
+  return -1;
 }
 
 /**
index 4787e49..29212b9 100644 (file)
@@ -169,6 +169,27 @@ typedef enum { /*< flags >*/
 } GstSegmentFlags;
 
 /**
+ * GstSegmentResult:
+ * @GST_SEGMENT_RESULT_INVALID: input value is invalid and no output value can
+ *     be calculated.
+ * @GST_SEGMENT_RESULT_OK: value is within the segment and positive.
+ * @GST_SEGMENT_RESULT_NEGATIVE: value is within the segment but negative.
+ * @GST_SEGMENT_RESULT_BEFORE: value is outside before the segment.
+ * @GST_SEGMENT_RESULT_AFTER: value is outside after the segment.
+ *
+ * Possible return values from gst_segment_to_running_time_full().
+ *
+ * Since: 1.6
+ */
+typedef enum {
+  GST_SEGMENT_RESULT_INVALID = 0,
+  GST_SEGMENT_RESULT_OK = 1,
+  GST_SEGMENT_RESULT_NEGATIVE = -1,
+  GST_SEGMENT_RESULT_BEFORE = -2,
+  GST_SEGMENT_RESULT_AFTER = -3
+} GstSegmentResult;
+
+/**
  * GstSegment:
  * @flags: flags for this segment
  * @rate: the rate of the segment
@@ -217,6 +238,9 @@ void         gst_segment_init                (GstSegment *segment, GstFormat for
 
 guint64      gst_segment_to_stream_time      (const GstSegment *segment, GstFormat format, guint64 position);
 guint64      gst_segment_to_running_time     (const GstSegment *segment, GstFormat format, guint64 position);
+GstSegmentResult
+             gst_segment_to_running_time_full (const GstSegment *segment, GstFormat format, guint64 position,
+                                               guint64 * running_time);
 guint64      gst_segment_to_position         (const GstSegment *segment, GstFormat format, guint64 running_time);
 
 gboolean     gst_segment_set_running_time    (GstSegment *segment, GstFormat format, guint64 running_time);
index 3e5f16b..8f642d5 100644 (file)
@@ -30,6 +30,8 @@ check_times (GstSegment * segment, guint64 position, guint64 stream_time,
 
   st = gst_segment_to_stream_time (segment, segment->format, position);
   rt = gst_segment_to_running_time (segment, segment->format, position);
+  GST_DEBUG ("position %" G_GUINT64_FORMAT ", st %" G_GUINT64_FORMAT ", rt %"
+      G_GUINT64_FORMAT, position, stream_time, running_time);
 
   fail_unless_equals_int64 (st, stream_time);
   fail_unless_equals_int64 (rt, running_time);
@@ -744,6 +746,45 @@ GST_START_TEST (segment_offset)
 
 GST_END_TEST;
 
+GST_START_TEST (segment_full)
+{
+  GstSegment segment;
+  guint64 rt;
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
+
+  segment.start = 50;
+  segment.position = 150;
+  segment.stop = 200;
+  segment.time = 0;
+
+  check_times (&segment, 100, 50, 50);
+  check_times (&segment, 220, -1, -1);
+
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          50, &rt) == GST_SEGMENT_RESULT_OK);
+  fail_unless (rt == 0);
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          200, &rt) == GST_SEGMENT_RESULT_OK);
+  fail_unless (rt == 150);
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          40, &rt) == GST_SEGMENT_RESULT_BEFORE);
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          49, &rt) == GST_SEGMENT_RESULT_BEFORE);
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          201, &rt) == GST_SEGMENT_RESULT_AFTER);
+
+  fail_unless (gst_segment_offset_running_time (&segment, GST_FORMAT_TIME,
+          -50) == TRUE);
+  fail_unless (segment.offset == 50);
+
+  fail_unless (gst_segment_to_running_time_full (&segment, GST_FORMAT_TIME,
+          50, &rt) == GST_SEGMENT_RESULT_NEGATIVE);
+  GST_DEBUG ("%" G_GUINT64_FORMAT, rt);
+  fail_unless (rt == 50);
+}
+
+GST_END_TEST;
 
 static Suite *
 gst_segment_suite (void)
@@ -761,6 +802,7 @@ gst_segment_suite (void)
   tcase_add_test (tc_chain, segment_copy);
   tcase_add_test (tc_chain, segment_seek_noupdate);
   tcase_add_test (tc_chain, segment_offset);
+  tcase_add_test (tc_chain, segment_full);
 
   return s;
 }
index 22acb9d..e21f498 100644 (file)
@@ -1124,9 +1124,11 @@ EXPORTS
        gst_segment_init
        gst_segment_new
        gst_segment_offset_running_time
+       gst_segment_result_get_type
        gst_segment_set_running_time
        gst_segment_to_position
        gst_segment_to_running_time
+       gst_segment_to_running_time_full
        gst_segment_to_stream_time
        gst_segtrap_is_enabled
        gst_segtrap_set_enabled