segment: add gst_segment_set_running_time
[platform/upstream/gstreamer.git] / gst / gstsegment.c
1 /* GStreamer
2  * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
3  *
4  * gstsegment.c: GstSegment subsystem
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22
23 #include "gst_private.h"
24
25 #include "gstutils.h"
26 #include "gstsegment.h"
27
28 /**
29  * SECTION:gstsegment
30  * @short_description: Structure describing the configured region of interest
31  *                     in a media file.
32  * @see_also: #GstEvent
33  *
34  * This helper structure holds the relevant values for tracking the region of
35  * interest in a media file, called a segment. 
36  *
37  * The structure can be used for two purposes:
38  * <itemizedlist>
39  *   <listitem><para>performing seeks (handling seek events)</para></listitem>
40  *   <listitem><para>tracking playback regions (handling newsegment events)</para></listitem>
41  * </itemizedlist>
42  *
43  * The segment is usually configured by the application with a seek event which 
44  * is propagated upstream and eventually handled by an element that performs the seek.
45  *
46  * The configured segment is then propagated back downstream with a newsegment event.
47  * This information is then used to clip media to the segment boundaries.
48  *
49  * A segment structure is initialized with gst_segment_init(), which takes a #GstFormat
50  * that will be used as the format of the segment values. The segment will be configured
51  * with a start value of 0 and a stop/duration of -1, which is undefined. The default
52  * rate and applied_rate is 1.0.
53  *
54  * If the segment is used for managing seeks, the segment duration should be set with
55  * gst_segment_set_duration(). The public duration field contains the duration of the
56  * segment. When using the segment for seeking, the start and time members should 
57  * normally be left to their default 0 value. The stop position is left to -1 unless
58  * explicitly configured to a different value after a seek event.
59  *
60  * The current position in the segment should be set with the gst_segment_set_last_stop().
61  * The public last_stop field contains the last set stop position in the segment.
62  *
63  * For elements that perform seeks, the current segment should be updated with the
64  * gst_segment_set_seek() and the values from the seek event. This method will update
65  * all the segment fields. The last_stop field will contain the new playback position.
66  * If the cur_type was different from GST_SEEK_TYPE_NONE, playback continues from
67  * the last_stop position, possibly with updated flags or rate.
68  *
69  * For elements that want to use #GstSegment to track the playback region, use
70  * gst_segment_set_newsegment() to update the segment fields with the information from
71  * the newsegment event. The gst_segment_clip() method can be used to check and clip
72  * the media data to the segment boundaries.
73  *
74  * For elements that want to synchronize to the pipeline clock, gst_segment_to_running_time()
75  * can be used to convert a timestamp to a value that can be used to synchronize
76  * to the clock. This function takes into account all accumulated segments as well as
77  * any rate or applied_rate conversions.
78  *
79  * For elements that need to perform operations on media data in stream_time, 
80  * gst_segment_to_stream_time() can be used to convert a timestamp and the segment
81  * info to stream time (which is always between 0 and the duration of the stream).
82  *
83  * Last reviewed on 2007-05-17 (0.10.13)
84  */
85
86 /**
87  * gst_segment_copy:
88  * @segment: a #GstSegment
89  *
90  * Create a copy of given @segment.
91  *
92  * Returns: a new #GstSegment, free with gst_segment_free().
93  *
94  * Since: 0.10.20
95  */
96 GstSegment *
97 gst_segment_copy (GstSegment * segment)
98 {
99   GstSegment *result = NULL;
100
101   if (segment) {
102     result = (GstSegment *) g_slice_copy (sizeof (GstSegment), segment);
103   }
104   return result;
105 }
106
107 GType
108 gst_segment_get_type (void)
109 {
110   static GType gst_segment_type = 0;
111
112   if (G_UNLIKELY (gst_segment_type == 0)) {
113     gst_segment_type = g_boxed_type_register_static ("GstSegment",
114         (GBoxedCopyFunc) gst_segment_copy, (GBoxedFreeFunc) gst_segment_free);
115   }
116
117   return gst_segment_type;
118 }
119
120 /**
121  * gst_segment_new:
122  *
123  * Allocate a new #GstSegment structure and initialize it using 
124  * gst_segment_init().
125  *
126  * Returns: a new #GstSegment, free with gst_segment_free().
127  */
128 GstSegment *
129 gst_segment_new (void)
130 {
131   GstSegment *result;
132
133   result = g_slice_new0 (GstSegment);
134   gst_segment_init (result, GST_FORMAT_UNDEFINED);
135
136   return result;
137 }
138
139 /**
140  * gst_segment_free:
141  * @segment: a #GstSegment
142  *
143  * Free the allocated segment @segment.
144  */
145 void
146 gst_segment_free (GstSegment * segment)
147 {
148   g_slice_free (GstSegment, segment);
149 }
150
151 /**
152  * gst_segment_init:
153  * @segment: a #GstSegment structure.
154  * @format: the format of the segment.
155  *
156  * The start/last_stop positions are set to 0 and the stop/duration
157  * fields are set to -1 (unknown). The default rate of 1.0 and no
158  * flags are set.
159  *
160  * Initialize @segment to its default values.
161  */
162 void
163 gst_segment_init (GstSegment * segment, GstFormat format)
164 {
165   g_return_if_fail (segment != NULL);
166
167   segment->rate = 1.0;
168   segment->abs_rate = 1.0;
169   segment->applied_rate = 1.0;
170   segment->format = format;
171   segment->flags = 0;
172   segment->start = 0;
173   segment->stop = -1;
174   segment->time = 0;
175   segment->accum = 0;
176   segment->last_stop = 0;
177   segment->duration = -1;
178 }
179
180 /**
181  * gst_segment_set_duration:
182  * @segment: a #GstSegment structure.
183  * @format: the format of the segment.
184  * @duration: the duration of the segment info or -1 if unknown.
185  *
186  * Set the duration of the segment to @duration. This function is mainly
187  * used by elements that perform seeking and know the total duration of the
188  * segment. 
189  * 
190  * This field should be set to allow seeking requests relative to the
191  * duration.
192  */
193 void
194 gst_segment_set_duration (GstSegment * segment, GstFormat format,
195     gint64 duration)
196 {
197   g_return_if_fail (segment != NULL);
198
199   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
200     segment->format = format;
201   else
202     g_return_if_fail (segment->format == format);
203
204   segment->duration = duration;
205 }
206
207 /**
208  * gst_segment_set_last_stop:
209  * @segment: a #GstSegment structure.
210  * @format: the format of the segment.
211  * @position: the position 
212  *
213  * Set the last observed stop position in the segment to @position.
214  *
215  * This field should be set to allow seeking requests relative to the
216  * current playing position.
217  */
218 void
219 gst_segment_set_last_stop (GstSegment * segment, GstFormat format,
220     gint64 position)
221 {
222   g_return_if_fail (segment != NULL);
223
224   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
225     segment->format = format;
226   else
227     g_return_if_fail (segment->format == format);
228
229   segment->last_stop = MAX (segment->start, position);
230 }
231
232 /**
233  * gst_segment_set_seek:
234  * @segment: a #GstSegment structure.
235  * @rate: the rate of the segment.
236  * @format: the format of the segment.
237  * @flags: the seek flags for the segment
238  * @start_type: the seek method
239  * @start: the seek start value
240  * @stop_type: the seek method
241  * @stop: the seek stop value
242  * @update: boolean holding whether last_stop was updated.
243  *
244  * Update the segment structure with the field values of a seek event (see
245  * gst_event_new_seek()).
246  *
247  * After calling this method, the segment field last_stop and time will
248  * contain the requested new position in the segment. The new requested
249  * position in the segment depends on @rate and @start_type and @stop_type. 
250  *
251  * For positive @rate, the new position in the segment is the new @segment
252  * start field when it was updated with a @start_type different from
253  * #GST_SEEK_TYPE_NONE. If no update was performed on @segment start position
254  * (#GST_SEEK_TYPE_NONE), @start is ignored and @segment last_stop is
255  * unmodified.
256  *
257  * For negative @rate, the new position in the segment is the new @segment
258  * stop field when it was updated with a @stop_type different from
259  * #GST_SEEK_TYPE_NONE. If no stop was previously configured in the segment, the
260  * duration of the segment will be used to update the stop position.
261  * If no update was performed on @segment stop position (#GST_SEEK_TYPE_NONE),
262  * @stop is ignored and @segment last_stop is unmodified.
263  *
264  * The applied rate of the segment will be set to 1.0 by default.
265  * If the caller can apply a rate change, it should update @segment
266  * rate and applied_rate after calling this function.
267  *
268  * @update will be set to TRUE if a seek should be performed to the segment 
269  * last_stop field. This field can be FALSE if, for example, only the @rate
270  * has been changed but not the playback position.
271  */
272 void
273 gst_segment_set_seek (GstSegment * segment, gdouble rate,
274     GstFormat format, GstSeekFlags flags,
275     GstSeekType start_type, gint64 start,
276     GstSeekType stop_type, gint64 stop, gboolean * update)
277 {
278   gboolean update_stop, update_start;
279   gint64 last_stop;
280
281   g_return_if_fail (rate != 0.0);
282   g_return_if_fail (segment != NULL);
283
284   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
285     segment->format = format;
286
287   update_start = update_stop = TRUE;
288
289   /* segment->start is never invalid */
290   switch (start_type) {
291     case GST_SEEK_TYPE_NONE:
292       /* no update to segment, take previous start */
293       start = segment->start;
294       update_start = FALSE;
295       break;
296     case GST_SEEK_TYPE_SET:
297       /* start holds desired position, map -1 to the start */
298       if (start == -1)
299         start = 0;
300       /* start must be 0 or the formats must match */
301       g_return_if_fail (start == 0 || segment->format == format);
302       break;
303     case GST_SEEK_TYPE_CUR:
304       g_return_if_fail (start == 0 || segment->format == format);
305       /* add start to currently configured segment */
306       start = segment->start + start;
307       break;
308     case GST_SEEK_TYPE_END:
309       if (segment->duration != -1) {
310         g_return_if_fail (start == 0 || segment->format == format);
311         /* add start to total length */
312         start = segment->duration + start;
313       } else {
314         /* no update if duration unknown */
315         start = segment->start;
316         update_start = FALSE;
317       }
318       break;
319   }
320   /* bring in sane range */
321   if (segment->duration != -1)
322     start = CLAMP (start, 0, segment->duration);
323   else
324     start = MAX (start, 0);
325
326   /* stop can be -1 if we have not configured a stop. */
327   switch (stop_type) {
328     case GST_SEEK_TYPE_NONE:
329       stop = segment->stop;
330       update_stop = FALSE;
331       break;
332     case GST_SEEK_TYPE_SET:
333       /* stop holds required value, if it's not -1, it must be of the same
334        * format as the segment. */
335       g_return_if_fail (stop == -1 || segment->format == format);
336       break;
337     case GST_SEEK_TYPE_CUR:
338       if (segment->stop != -1) {
339         /* only add compatible formats or 0 */
340         g_return_if_fail (stop == 0 || segment->format == format);
341         stop = segment->stop + stop;
342       } else
343         stop = -1;
344       break;
345     case GST_SEEK_TYPE_END:
346       if (segment->duration != -1) {
347         /* only add compatible formats or 0 */
348         g_return_if_fail (stop == 0 || segment->format == format);
349         stop = segment->duration + stop;
350       } else {
351         stop = segment->stop;
352         update_stop = FALSE;
353       }
354       break;
355   }
356
357   /* if we have a valid stop time, make sure it is clipped */
358   if (stop != -1) {
359     if (segment->duration != -1)
360       stop = CLAMP (stop, 0, segment->duration);
361     else
362       stop = MAX (stop, 0);
363   }
364
365   /* we can't have stop before start */
366   if (stop != -1)
367     g_return_if_fail (start <= stop);
368
369   segment->rate = rate;
370   segment->abs_rate = ABS (rate);
371   segment->applied_rate = 1.0;
372   segment->flags = flags;
373   segment->start = start;
374   segment->stop = stop;
375   segment->time = start;
376
377   last_stop = segment->last_stop;
378   if (update_start && rate > 0.0) {
379     last_stop = start;
380   }
381   if (update_stop && rate < 0.0) {
382     if (stop != -1)
383       last_stop = stop;
384     else {
385       if (segment->duration != -1)
386         last_stop = segment->duration;
387       else
388         last_stop = 0;
389     }
390   }
391   /* set update arg to reflect update of last_stop */
392   if (update)
393     *update = last_stop != segment->last_stop;
394
395   /* update new position */
396   segment->last_stop = last_stop;
397 }
398
399 /**
400  * gst_segment_set_newsegment:
401  * @segment: a #GstSegment structure.
402  * @update: flag indicating a new segment is started or updated
403  * @rate: the rate of the segment.
404  * @format: the format of the segment.
405  * @start: the new start value
406  * @stop: the new stop value
407  * @time: the new stream time
408  *
409  * Update the segment structure with the field values of a new segment event and
410  * with a default applied_rate of 1.0.
411  *
412  * Since: 0.10.6
413  */
414 void
415 gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
416     GstFormat format, gint64 start, gint64 stop, gint64 time)
417 {
418   gst_segment_set_newsegment_full (segment, update, rate, 1.0, format, start,
419       stop, time);
420 }
421
422 /**
423  * gst_segment_set_newsegment_full:
424  * @segment: a #GstSegment structure.
425  * @update: flag indicating a new segment is started or updated
426  * @rate: the rate of the segment.
427  * @applied_rate: the applied rate of the segment.
428  * @format: the format of the segment.
429  * @start: the new start value
430  * @stop: the new stop value
431  * @time: the new stream time
432  *
433  * Update the segment structure with the field values of a new segment event.
434  */
435 void
436 gst_segment_set_newsegment_full (GstSegment * segment, gboolean update,
437     gdouble rate, gdouble applied_rate, GstFormat format, gint64 start,
438     gint64 stop, gint64 time)
439 {
440   gint64 duration, last_stop;
441
442   g_return_if_fail (rate != 0.0);
443   g_return_if_fail (applied_rate != 0.0);
444   g_return_if_fail (segment != NULL);
445
446   GST_DEBUG ("configuring segment update %d, rate %lf, format %s, "
447       "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %"
448       G_GINT64_FORMAT, update, rate, gst_format_get_name (format), start,
449       stop, time);
450   GST_DEBUG ("old segment was: %" GST_SEGMENT_FORMAT, segment);
451
452   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
453     segment->format = format;
454
455   /* any other format with 0 also gives time 0, the other values are
456    * invalid in the format though. */
457   if (format != segment->format && start == 0) {
458     format = segment->format;
459     if (stop != 0)
460       stop = -1;
461     if (time != 0)
462       time = -1;
463   }
464
465   g_return_if_fail (segment->format == format);
466
467   if (update) {
468     if (G_LIKELY (segment->rate > 0.0)) {
469       /* an update to the current segment is done, elapsed time is
470        * difference between the old start and new start. */
471       if (start > segment->start)
472         duration = start - segment->start;
473       else
474         duration = 0;
475     } else {
476       /* for negative rates, the elapsed duration is the diff between the stop
477        * positions */
478       if (stop != -1 && stop < segment->stop)
479         duration = segment->stop - stop;
480       else
481         duration = 0;
482     }
483     /* update last_stop to be a valid value in the updated segment */
484     if (start > segment->last_stop)
485       last_stop = start;
486     else if (stop != -1 && stop < segment->last_stop)
487       last_stop = stop;
488     else
489       last_stop = segment->last_stop;
490   } else {
491     /* the new segment has to be aligned with the old segment.
492      * We first update the accumulated time of the previous
493      * segment. the accumulated time is used when syncing to the
494      * clock. */
495     if (segment->stop != -1) {
496       duration = segment->stop - segment->start;
497     } else if (segment->last_stop != -1) {
498       /* else use last seen timestamp as segment stop */
499       duration = segment->last_stop - segment->start;
500     } else {
501       /* else we don't know and throw a warning.. really, this should
502        * be fixed in the element. */
503       g_warning ("closing segment of unknown duration, assuming duration of 0");
504       duration = 0;
505     }
506     /* position the last_stop to the next expected position in the new segment,
507      * which is the start or the stop of the segment */
508     if (rate > 0.0)
509       last_stop = start;
510     else
511       last_stop = stop;
512   }
513   /* use previous rate to calculate duration */
514   if (G_LIKELY (segment->abs_rate != 1.0))
515     duration /= segment->abs_rate;
516
517   /* accumulate duration */
518   segment->accum += duration;
519
520   /* then update the current segment */
521   segment->rate = rate;
522   segment->abs_rate = ABS (rate);
523   segment->applied_rate = applied_rate;
524   segment->start = start;
525   segment->last_stop = last_stop;
526   segment->stop = stop;
527   segment->time = time;
528 }
529
530 /**
531  * gst_segment_to_stream_time:
532  * @segment: a #GstSegment structure.
533  * @format: the format of the segment.
534  * @position: the position in the segment
535  *
536  * Translate @position to stream time using the currently configured 
537  * segment. The @position value must be between @segment start and
538  * stop value. 
539  *
540  * This function is typically used by elements that need to operate on
541  * the stream time of the buffers it receives, such as effect plugins.
542  * In those use cases, @position is typically the buffer timestamp or 
543  * clock time that one wants to convert to the stream time.
544  * The stream time is always between 0 and the total duration of the 
545  * media stream. 
546  *
547  * Returns: the position in stream_time or -1 when an invalid position
548  * was given.
549  */
550 gint64
551 gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
552     gint64 position)
553 {
554   gint64 result, start, stop, time;
555   gdouble abs_applied_rate;
556
557   g_return_val_if_fail (segment != NULL, -1);
558
559   /* format does not matter for -1 */
560   if (G_UNLIKELY (position == -1))
561     return -1;
562
563   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
564     segment->format = format;
565
566   /* if we have the position for the same format as the segment, we can compare
567    * the start and stop values, otherwise we assume 0 and -1 */
568   if (G_LIKELY (segment->format == format)) {
569     start = segment->start;
570     stop = segment->stop;
571     time = segment->time;
572   } else {
573     start = 0;
574     stop = -1;
575     time = 0;
576   }
577
578   /* outside of the segment boundary stop */
579   if (G_UNLIKELY (stop != -1 && position > stop))
580     return -1;
581
582   /* before the segment boundary */
583   if (G_UNLIKELY (position < start))
584     return -1;
585
586   /* time must be known */
587   if (G_UNLIKELY (time == -1))
588     return -1;
589
590   /* bring to uncorrected position in segment */
591   result = position - start;
592
593   abs_applied_rate = ABS (segment->applied_rate);
594
595   /* correct for applied rate if needed */
596   if (G_UNLIKELY (abs_applied_rate != 1.0))
597     result *= abs_applied_rate;
598
599   /* add or subtract from segment time based on applied rate */
600   if (G_LIKELY (segment->applied_rate > 0.0)) {
601     /* correct for segment time */
602     result += time;
603   } else {
604     /* correct for segment time, clamp at 0. Streams with a negative
605      * applied_rate have timestamps between start and stop, as usual, but have
606      * the time member starting high and going backwards.  */
607     if (G_LIKELY (time > result))
608       result = time - result;
609     else
610       result = 0;
611   }
612
613   return result;
614 }
615
616 /**
617  * gst_segment_to_running_time:
618  * @segment: a #GstSegment structure.
619  * @format: the format of the segment.
620  * @position: the position in the segment
621  *
622  * Translate @position to the total running time using the currently configured 
623  * and previously accumulated segments. Position is a value between @segment
624  * start and stop time.
625  *
626  * This function is typically used by elements that need to synchronize to the
627  * global clock in a pipeline. The runnning time is a constantly increasing value
628  * starting from 0. When gst_segment_init() is called, this value will reset to
629  * 0.
630  *
631  * This function returns -1 if the position is outside of @segment start and stop.
632  *
633  * Returns: the position as the total running time or -1 when an invalid position
634  * was given.
635  */
636 gint64
637 gst_segment_to_running_time (GstSegment * segment, GstFormat format,
638     gint64 position)
639 {
640   gint64 result;
641   gint64 start, stop, accum;
642
643   g_return_val_if_fail (segment != NULL, -1);
644
645   if (G_UNLIKELY (position == -1))
646     return -1;
647
648   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
649     segment->format = format;
650
651   /* if we have the position for the same format as the segment, we can compare
652    * the start and stop values, otherwise we assume 0 and -1 */
653   if (G_LIKELY (segment->format == format)) {
654     start = segment->start;
655     stop = segment->stop;
656     accum = segment->accum;
657   } else {
658     start = 0;
659     stop = -1;
660     accum = 0;
661   }
662
663   /* before the segment boundary */
664   if (G_UNLIKELY (position < start))
665     return -1;
666
667   if (G_LIKELY (segment->rate > 0.0)) {
668     /* outside of the segment boundary stop */
669     if (G_UNLIKELY (stop != -1 && position > stop))
670       return -1;
671
672     /* bring to uncorrected position in segment */
673     result = position - start;
674   } else {
675     /* cannot continue if no stop position set or outside of
676      * the segment. */
677     if (G_UNLIKELY (stop == -1 || position > stop))
678       return -1;
679
680     /* bring to uncorrected position in segment */
681     result = stop - position;
682   }
683
684   /* scale based on the rate, avoid division by and conversion to 
685    * float when not needed */
686   if (G_UNLIKELY (segment->abs_rate != 1.0))
687     result /= segment->abs_rate;
688
689   /* correct for accumulated segments */
690   result += accum;
691
692   return result;
693 }
694
695 /**
696  * gst_segment_clip:
697  * @segment: a #GstSegment structure.
698  * @format: the format of the segment.
699  * @start: the start position in the segment
700  * @stop: the stop position in the segment
701  * @clip_start: the clipped start position in the segment
702  * @clip_stop: the clipped stop position in the segment
703  *
704  * Clip the given @start and @stop values to the segment boundaries given
705  * in @segment. @start and @stop are compared and clipped to @segment 
706  * start and stop values.
707  *
708  * If the function returns FALSE, @start and @stop are known to fall
709  * outside of @segment and @clip_start and @clip_stop are not updated.
710  *
711  * When the function returns TRUE, @clip_start and @clip_stop will be
712  * updated. If @clip_start or @clip_stop are different from @start or @stop
713  * respectively, the region fell partially in the segment.
714  *
715  * Note that when @stop is -1, @clip_stop will be set to the end of the
716  * segment. Depending on the use case, this may or may not be what you want.
717  *
718  * Returns: TRUE if the given @start and @stop times fall partially or 
719  *     completely in @segment, FALSE if the values are completely outside 
720  *     of the segment.
721  */
722 gboolean
723 gst_segment_clip (GstSegment * segment, GstFormat format, gint64 start,
724     gint64 stop, gint64 * clip_start, gint64 * clip_stop)
725 {
726   g_return_val_if_fail (segment != NULL, FALSE);
727
728   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
729     segment->format = format;
730   else
731     g_return_val_if_fail (segment->format == format, FALSE);
732
733   /* if we have a stop position and a valid start and start is bigger, 
734    * we're outside of the segment */
735   if (G_UNLIKELY (segment->stop != -1 && start != -1 && start >= segment->stop))
736     return FALSE;
737
738   /* if a stop position is given and is before the segment start,
739    * we're outside of the segment */
740   if (G_UNLIKELY (stop != -1 && stop != start && stop <= segment->start))
741     return FALSE;
742
743   if (clip_start) {
744     if (start == -1)
745       *clip_start = -1;
746     else
747       *clip_start = MAX (start, segment->start);
748   }
749
750   if (clip_stop) {
751     if (stop == -1)
752       *clip_stop = segment->stop;
753     else if (segment->stop == -1)
754       *clip_stop = MAX (-1, stop);
755     else
756       *clip_stop = MIN (stop, segment->stop);
757
758     if (segment->duration != -1)
759       *clip_stop = MIN (*clip_stop, segment->duration);
760   }
761
762   return TRUE;
763 }
764
765 /**
766  * gst_segment_to_position:
767  * @segment: a #GstSegment structure.
768  * @format: the format of the segment.
769  * @running_time: the running_time in the segment
770  *
771  * Convert @running_time into a position in the segment so that
772  * gst_segment_to_running_time() with that position returns @running_time.
773  *
774  * Returns: the position in the segment for @running_time. This function returns
775  * -1 when @running_time is -1 or when it is not inside @segment.
776  *
777  * Since: 0.10.24
778  */
779 gint64
780 gst_segment_to_position (GstSegment * segment, GstFormat format,
781     gint64 running_time)
782 {
783   gint64 result;
784   gint64 start, stop, accum;
785
786   g_return_val_if_fail (segment != NULL, -1);
787
788   if (G_UNLIKELY (running_time == -1))
789     return -1;
790
791   if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
792     segment->format = format;
793
794   /* if we have the position for the same format as the segment, we can compare
795    * the start and stop values, otherwise we assume 0 and -1 */
796   if (G_LIKELY (segment->format == format)) {
797     start = segment->start;
798     stop = segment->stop;
799     accum = segment->accum;
800   } else {
801     start = 0;
802     stop = -1;
803     accum = 0;
804   }
805
806   /* this running_time was for a previous segment */
807   if (running_time < accum)
808     return -1;
809
810   /* start by subtracting the accumulated time */
811   result = running_time - accum;
812
813   /* move into the segment at the right rate */
814   if (G_UNLIKELY (segment->abs_rate != 1.0))
815     result *= segment->abs_rate;
816
817   if (G_LIKELY (segment->rate > 0.0)) {
818     /* bring to corrected position in segment */
819     result += start;
820
821     /* outside of the segment boundary stop */
822     if (G_UNLIKELY (stop != -1 && result > stop))
823       return -1;
824   } else {
825     /* cannot continue if no stop position set or outside of
826      * the segment. */
827     if (G_UNLIKELY (stop == -1 || result + start > stop))
828       return -1;
829
830     /* bring to corrected position in segment */
831     result = stop - result;
832   }
833   return result;
834 }
835
836
837 /**
838  * gst_segment_set_running_time:
839  * @segment: a #GstSegment structure.
840  * @format: the format of the segment.
841  * @running_time: the running_time in the segment
842  *
843  * Adjust the start/stop and accum values of @segment such that the next valid
844  * buffer will be one with @running_time.
845  *
846  * Returns: %TRUE if the segment could be updated successfully. If %FALSE is
847  * returned, @running_time is -1 or not in @segment.
848  *
849  * Since: 0.10.24
850  */
851 gboolean
852 gst_segment_set_running_time (GstSegment * segment, GstFormat format,
853     gint64 running_time)
854 {
855   gint64 position;
856   gint64 start, stop, last_stop;
857
858   /* start by bringing the running_time into the segment position */
859   position = gst_segment_to_position (segment, format, running_time);
860
861   /* we must have a valid position now */
862   if (G_UNLIKELY (position == -1))
863     return FALSE;
864
865   start = segment->start;
866   stop = segment->stop;
867   last_stop = segment->last_stop;
868
869   if (G_LIKELY (segment->rate > 0.0)) {
870     /* update the start/last_stop and time values */
871     start = position;
872     if (last_stop < start)
873       last_stop = start;
874   } else {
875     /* reverse, update stop */
876     stop = position;
877     /* if we were past the position, go back */
878     if (last_stop > stop)
879       last_stop = stop;
880   }
881   /* and accumulated time is exactly the running time */
882   segment->time = gst_segment_to_stream_time (segment, format, start);
883   segment->start = start;
884   segment->stop = stop;
885   segment->last_stop = last_stop;
886   segment->accum = running_time;
887
888   return TRUE;
889 }