Index: gst/gstsegment.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstsegment.c,v
retrieving revision 1.27
diff -u -B -b -p -r1.27 gstsegment.c
--- gst/gstsegment.c	5 Jun 2007 16:25:06 -0000	1.27
+++ gst/gstsegment.c	9 Nov 2007 10:50:17 -0000
@@ -443,12 +443,22 @@ gst_segment_set_newsegment_full (GstSegm
   g_return_if_fail (segment->format == format);
 
   if (update) {
+    if (segment->rate > 0.0) {
     /* an update to the current segment is done, elapsed time is
      * difference between the old start and new start. */
     if (start > segment->start)
       duration = start - segment->start;
     else
       duration = 0;
+    }
+    else {
+      /* for negative rates, the elapsed duration is the diff between the stop
+       * positions */
+      if (stop != -1 && stop < segment->stop)
+        duration = segment->stop - stop;
+      else
+        duration = 0;
+    }
   } else {
     /* the new segment has to be aligned with the old segment.
      * We first update the accumulated time of the previous
@@ -508,7 +518,7 @@ gint64
 gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
     gint64 position)
 {
-  gint64 result;
+  gint64 result, start, stop, time;
   gdouble abs_applied_rate;
 
   g_return_val_if_fail (segment != NULL, -1);
@@ -519,23 +529,34 @@ gst_segment_to_stream_time (GstSegment *
 
   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
     segment->format = format;
-  else
-    g_return_val_if_fail (segment->format == format, -1);
+
+  /* if we have the position for the same format as the segment, we can compare
+   * the start and stop values, otherwise we assume 0 and -1 */
+  if (segment->format == format) {
+    start = segment->start;
+    stop = segment->stop;
+    time = segment->time;
+  }
+  else {
+    start = 0;
+    stop = -1;
+    time = 0;
+  }
 
   /* outside of the segment boundary stop */
-  if (G_UNLIKELY (segment->stop != -1 && position > segment->stop))
+  if (G_UNLIKELY (stop != -1 && position > stop))
     return -1;
 
   /* before the segment boundary */
-  if (G_UNLIKELY (position < segment->start))
+  if (G_UNLIKELY (position < start))
     return -1;
 
   /* time must be known */
-  if (G_UNLIKELY (segment->time == -1))
+  if (G_UNLIKELY (time == -1))
     return -1;
 
   /* bring to uncorrected position in segment */
-  result = position - segment->start;
+  result = position - start;
 
   abs_applied_rate = ABS (segment->applied_rate);
 
@@ -546,11 +567,11 @@ gst_segment_to_stream_time (GstSegment *
   /* add or subtract from segment time based on applied rate */
   if (segment->applied_rate > 0.0) {
     /* correct for segment time */
-    result += segment->time;
+    result += time;
   } else {
     /* correct for segment time, clamp at 0 */
-    if (segment->time > result)
-      result = segment->time - result;
+    if (time > result)
+      result = time - result;
     else
       result = 0;
   }
@@ -583,6 +604,7 @@ gst_segment_to_running_time (GstSegment 
     gint64 position)
 {
   gint64 result;
+  gint64 start, stop, accum;
 
   g_return_val_if_fail (segment != NULL, -1);
 
@@ -591,28 +613,39 @@ gst_segment_to_running_time (GstSegment 
 
   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
     segment->format = format;
-  else if (segment->accum)
-    g_return_val_if_fail (segment->format == format, -1);
+
+  /* if we have the position for the same format as the segment, we can compare
+   * the start and stop values, otherwise we assume 0 and -1 */
+  if (segment->format == format) {
+    start = segment->start;
+    stop = segment->stop;
+    accum = segment->accum;
+  }
+  else {
+    start = 0;
+    stop = -1;
+    accum = 0;
+  }
 
   /* before the segment boundary */
-  if (G_UNLIKELY (position < segment->start))
+  if (G_UNLIKELY (position < start))
     return -1;
 
   if (segment->rate > 0.0) {
     /* outside of the segment boundary stop */
-    if (G_UNLIKELY (segment->stop != -1 && position > segment->stop))
+    if (G_UNLIKELY (stop != -1 && position > stop))
       return -1;
 
     /* bring to uncorrected position in segment */
-    result = position - segment->start;
+    result = position - start;
   } else {
     /* cannot continue if no stop position set or outside of
      * the segment. */
-    if (G_UNLIKELY (segment->stop == -1 || position > segment->stop))
+    if (G_UNLIKELY (stop == -1 || position > stop))
       return -1;
 
     /* bring to uncorrected position in segment */
-    result = segment->stop - position;
+    result = stop - position;
   }
 
   /* scale based on the rate, avoid division by and conversion to 
@@ -621,7 +654,7 @@ gst_segment_to_running_time (GstSegment 
     result /= segment->abs_rate;
 
   /* correct for accumulated segments */
-  result += segment->accum;
+  result += accum;
 
   return result;
 }
Index: tests/check/gst/gstsegment.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/gst/gstsegment.c,v
retrieving revision 1.12
diff -u -B -b -p -r1.12 gstsegment.c
--- tests/check/gst/gstsegment.c	13 Feb 2007 15:51:00 -0000	1.12
+++ tests/check/gst/gstsegment.c	9 Nov 2007 10:50:17 -0000
@@ -1331,7 +1331,163 @@ GST_START_TEST (segment_newsegment_runni
   /* previous segment lasted 100, and was at 700 so we should get 800 */
   fail_unless (segment.accum == 800);
 }
+GST_END_TEST;
+
+/* mess with the segment structure in the time format */
+GST_START_TEST (segment_newsegment_accum)
+{
+  GstSegment segment;
+  gint64 result;
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
 
+  /***************************
+   * Normal reverse segment
+   ***************************/
+  gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 1.0,
+      GST_FORMAT_TIME, 0, 200, 0);
+
+  fail_unless (segment.rate == -1.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 0);
+  fail_unless (segment.stop == 200);
+  fail_unless (segment.time == 0);
+  fail_unless (segment.accum == 0);
+  fail_unless (segment.last_stop == 0);
+  fail_unless (segment.duration == -1);
+
+  /* invalid time gives invalid result */
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
+  fail_unless (result == -1);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 200);
+  fail_unless (result == 0);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 150);
+  fail_unless (result == 50);
+
+  /* update segment, this accumulates 50 from the previous segment. */
+  gst_segment_set_newsegment_full (&segment, TRUE, -2.0, 1.0,
+      GST_FORMAT_TIME, 0, 150, 0);
+
+  fail_unless (segment.rate == -2.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 0);
+  fail_unless (segment.stop == 150);
+  fail_unless (segment.time == 0);
+  fail_unless (segment.accum == 50);
+  fail_unless (segment.last_stop == 0);
+  fail_unless (segment.duration == -1);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 150);
+  fail_unless (result == 50);
+
+  /* 50 accumulated + 50 / 2 */
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 100);
+  fail_unless (result == 75);
+
+  /* update segment, this does not accumulate anything. */
+  gst_segment_set_newsegment_full (&segment, TRUE, 1.0, 1.0,
+      GST_FORMAT_TIME, 100, 200, 100);
+
+  fail_unless (segment.rate == 1.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 100);
+  fail_unless (segment.stop == 200);
+  fail_unless (segment.time == 100);
+  fail_unless (segment.accum == 50);
+  fail_unless (segment.last_stop == 100);
+  fail_unless (segment.duration == -1);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 100);
+  fail_unless (result == 50);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 150);
+  fail_unless (result == 100);
+}
+GST_END_TEST;
+
+/* mess with the segment structure in the time format */
+GST_START_TEST (segment_newsegment_accum2)
+{
+  GstSegment segment;
+  gint64 result;
+
+  gst_segment_init (&segment, GST_FORMAT_TIME);
+
+  /***************************
+   * Normal reverse segment
+   ***************************/
+  gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 1.0,
+      GST_FORMAT_TIME, 0, 200, 0);
+
+  fail_unless (segment.rate == -1.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 0);
+  fail_unless (segment.stop == 200);
+  fail_unless (segment.time == 0);
+  fail_unless (segment.accum == 0);
+  fail_unless (segment.last_stop == 0);
+  fail_unless (segment.duration == -1);
+
+  /* invalid time gives invalid result */
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
+  fail_unless (result == -1);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 200);
+  fail_unless (result == 0);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 150);
+  fail_unless (result == 50);
+
+  /* close segment, this accumulates nothing. */
+  gst_segment_set_newsegment_full (&segment, TRUE, -1.0, 1.0,
+      GST_FORMAT_TIME, 150, 200, 0);
+
+  fail_unless (segment.rate == -1.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 150);
+  fail_unless (segment.stop == 200);
+  fail_unless (segment.time == 0);
+  fail_unless (segment.accum == 0);
+  fail_unless (segment.last_stop == 150);
+  fail_unless (segment.duration == -1);
+
+  /* new segment, this accumulates 50. */
+  gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
+      GST_FORMAT_TIME, 150, 300, 150);
+
+  fail_unless (segment.rate == 1.0);
+  fail_unless (segment.applied_rate == 1.0);
+  fail_unless (segment.format == GST_FORMAT_TIME);
+  fail_unless (segment.flags == 0);
+  fail_unless (segment.start == 150);
+  fail_unless (segment.stop == 300);
+  fail_unless (segment.time == 150);
+  fail_unless (segment.accum == 50);
+  fail_unless (segment.last_stop == 150);
+  fail_unless (segment.duration == -1);
+
+  /* invalid time gives invalid result */
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
+  fail_unless (result == -1);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 150);
+  fail_unless (result == 50);
+
+  result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 200);
+  fail_unless (result == 100);
+}
 GST_END_TEST;
 
 Suite *
@@ -1353,6 +1509,8 @@ gst_segment_suite (void)
   tcase_add_test (tc_chain, segment_newsegment_streamtime_applied_rate);
   tcase_add_test (tc_chain, segment_newsegment_streamtime_applied_rate_rate);
   tcase_add_test (tc_chain, segment_newsegment_runningtime);
+  tcase_add_test (tc_chain, segment_newsegment_accum);
+  tcase_add_test (tc_chain, segment_newsegment_accum2);
 
   return s;
 }
