More segment updates, replace code in plugins with segment helper functions.
[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  * Last reviewed on 2005-20-09 (0.9.5)
38  */
39
40 /**
41  * gst_segment_init:
42  * @segment: a #GstSegment structure.
43  * @format: the format of the segment.
44  *
45  * Initialize @segment to its default values, which is a rate of 1.0, a
46  * start time of 0.
47  */
48 void
49 gst_segment_init (GstSegment * segment, GstFormat format)
50 {
51   g_return_if_fail (segment != NULL);
52
53   segment->rate = 1.0;
54   segment->abs_rate = 1.0;
55   segment->format = format;
56   segment->flags = 0;
57   segment->start = 0;
58   segment->stop = -1;
59   segment->time = 0;
60   segment->accum = 0;
61   segment->last_stop = -1;
62   segment->duration = -1;
63 }
64
65 /**
66  * gst_segment_set_duration:
67  * @segment: a #GstSegment structure.
68  * @format: the format of the segment.
69  * @duration: the duration of the segment info.
70  *
71  * Set the duration of the segment to @duration. This function is mainly
72  * used by elements that perform seeking and know the total duration of the
73  * segment.
74  */
75 void
76 gst_segment_set_duration (GstSegment * segment, GstFormat format,
77     gint64 duration)
78 {
79   g_return_if_fail (segment != NULL);
80
81   if (segment->format == GST_FORMAT_UNDEFINED)
82     segment->format = format;
83   else
84     g_return_if_fail (segment->format == format);
85
86   segment->duration = duration;
87 }
88
89 /**
90  * gst_segment_set_last_stop:
91  * @segment: a #GstSegment structure.
92  * @format: the format of the segment.
93  * @position: the position 
94  *
95  * Set the last observed stop position in the segment to @position.
96  */
97 void
98 gst_segment_set_last_stop (GstSegment * segment, GstFormat format,
99     gint64 position)
100 {
101   g_return_if_fail (segment != NULL);
102
103   if (segment->format == GST_FORMAT_UNDEFINED)
104     segment->format = format;
105   else
106     g_return_if_fail (segment->format == format);
107
108   segment->last_stop = position;
109 }
110
111 /**
112  * gst_segment_set_seek:
113  * @segment: a #GstSegment structure.
114  * @rate: the rate of the segment.
115  * @format: the format of the segment.
116  * @flags: the seek flags for the segment
117  * @cur_type: the seek method
118  * @cur: the seek start value
119  * @stop_type: the seek method
120  * @stop: the seek stop value
121  * @update: boolean holding whether an update the current segment is
122  *    needed.
123  *
124  * Update the segment structure with the field values of a seek event.
125  */
126 void
127 gst_segment_set_seek (GstSegment * segment, gdouble rate,
128     GstFormat format, GstSeekFlags flags,
129     GstSeekType cur_type, gint64 cur,
130     GstSeekType stop_type, gint64 stop, gboolean * update)
131 {
132   gboolean update_stop, update_start;
133
134   g_return_if_fail (rate != 0.0);
135   g_return_if_fail (segment != NULL);
136
137   if (segment->format == GST_FORMAT_UNDEFINED)
138     segment->format = format;
139   else
140     g_return_if_fail (segment->format == format);
141
142   update_stop = update_start = TRUE;
143
144   /* start is never invalid */
145   switch (cur_type) {
146     case GST_SEEK_TYPE_NONE:
147       /* no update to segment */
148       cur = segment->start;
149       update_start = FALSE;
150       break;
151     case GST_SEEK_TYPE_SET:
152       /* cur holds desired position */
153       break;
154     case GST_SEEK_TYPE_CUR:
155       /* add cur to currently configure segment */
156       cur = segment->start + cur;
157       break;
158     case GST_SEEK_TYPE_END:
159       if (segment->duration != -1) {
160         /* add cur to total length */
161         cur = segment->duration + cur;
162       } else {
163         /* no update if duration unknown */
164         cur = segment->start;
165         update_start = FALSE;
166       }
167       break;
168   }
169   /* bring in sane range */
170   if (segment->duration != -1)
171     cur = CLAMP (cur, 0, segment->duration);
172   else
173     cur = MAX (cur, 0);
174
175   /* stop can be -1 if we have not configured a stop. */
176   switch (stop_type) {
177     case GST_SEEK_TYPE_NONE:
178       stop = segment->stop;
179       update_stop = FALSE;
180       break;
181     case GST_SEEK_TYPE_SET:
182       /* stop folds required value */
183       break;
184     case GST_SEEK_TYPE_CUR:
185       if (segment->stop != -1)
186         stop = segment->stop + stop;
187       else
188         stop = -1;
189       break;
190     case GST_SEEK_TYPE_END:
191       if (segment->duration != -1)
192         stop = segment->duration + stop;
193       else {
194         stop = segment->stop;
195         update_stop = FALSE;
196       }
197       break;
198   }
199
200   /* if we have a valid stop time, make sure it is clipped */
201   if (stop != -1) {
202     if (segment->duration != -1)
203       stop = CLAMP (stop, 0, segment->duration);
204     else
205       stop = MAX (stop, 0);
206   }
207
208   /* we can't have stop before start */
209   if (stop != -1)
210     g_return_if_fail (cur <= stop);
211
212   segment->rate = rate;
213   segment->abs_rate = ABS (rate);
214   segment->flags = flags;
215   segment->start = cur;
216   segment->stop = stop;
217
218   if (update)
219     *update = update_start || update_stop;
220 }
221
222 /**
223  * gst_segment_set_newsegment:
224  * @segment: a #GstSegment structure.
225  * @update: flag indicating a new segment is started or updated
226  * @rate: the rate of the segment.
227  * @format: the format of the segment.
228  * @start: the new start value
229  * @stop: the new stop value
230  * @time: the new stream time
231  *
232  * Update the segment structure with the field values of a new segment event.
233  */
234 void
235 gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
236     GstFormat format, gint64 start, gint64 stop, gint64 time)
237 {
238   gint64 duration;
239
240   g_return_if_fail (rate != 0.0);
241   g_return_if_fail (segment != NULL);
242
243   if (segment->format == GST_FORMAT_UNDEFINED)
244     segment->format = format;
245
246   /* any other format with 0 also gives time 0, the other values are
247    * invalid in the format though. */
248   if (format != segment->format && start == 0) {
249     format = segment->format;
250     if (stop != 0)
251       stop = -1;
252     if (time != 0)
253       time = -1;
254   }
255
256   g_return_if_fail (segment->format == format);
257
258   if (update) {
259     /* an update to the current segment is done, elapsed time is
260      * difference between the old start and new start. */
261     duration = start - segment->start;
262   } else {
263     /* the new segment has to be aligned with the old segment.
264      * We first update the accumulated time of the previous
265      * segment. the accumulated time is used when syncing to the
266      * clock. 
267      */
268     if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
269       duration = segment->stop - segment->start;
270     } else if (GST_CLOCK_TIME_IS_VALID (segment->last_stop)) {
271       /* else use last seen timestamp as segment stop */
272       duration = segment->last_stop - segment->start;
273     } else {
274       /* else we don't know */
275       duration = 0;
276     }
277   }
278   /* use previous rate to calculate duration */
279   segment->accum += gst_gdouble_to_guint64 (
280       (gst_guint64_to_gdouble (duration) / segment->abs_rate));
281   /* then update the current segment */
282   segment->rate = rate;
283   segment->abs_rate = ABS (rate);
284   segment->start = start;
285   segment->stop = stop;
286   segment->time = time;
287 }
288
289 /**
290  * gst_segment_to_stream_time:
291  * @segment: a #GstSegment structure.
292  * @format: the format of the segment.
293  * @position: the position in the segment
294  *
295  * Translate @position to stream time using the currently configured 
296  * segment.
297  *
298  * This function is typically used by elements that need to operate on
299  * the stream time of the buffers it receives, such as effect plugins.
300  *
301  * Returns: the position in stream_time.
302  */
303 gint64
304 gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
305     gint64 position)
306 {
307   gint64 result, time;
308
309   g_return_val_if_fail (segment != NULL, FALSE);
310
311   if (segment->format == GST_FORMAT_UNDEFINED)
312     segment->format = format;
313   else
314     g_return_val_if_fail (segment->format == format, FALSE);
315
316   if ((time = segment->time) == -1)
317     time = 0;
318
319   if (position != -1)
320     result = ((position - segment->start) / segment->abs_rate) + time;
321   else
322     result = -1;
323
324   return result;
325 }
326
327 /**
328  * gst_segment_to_running_time:
329  * @segment: a #GstSegment structure.
330  * @format: the format of the segment.
331  * @position: the position in the segment
332  *
333  * Translate @position to the total running time using the currently configured 
334  * segment.
335  *
336  * This function is typically used by elements that need to synchronize to the
337  * global clock in a pipeline.
338  *
339  * Returns: the position as the total running time.
340  */
341 gint64
342 gst_segment_to_running_time (GstSegment * segment, GstFormat format,
343     gint64 position)
344 {
345   gint64 result;
346
347   g_return_val_if_fail (segment != NULL, -1);
348
349   if (segment->format == GST_FORMAT_UNDEFINED)
350     segment->format = format;
351   else
352     g_return_val_if_fail (segment->format == format, -1);
353
354   if (position != -1)
355     result = ((position - segment->start) / segment->abs_rate) + segment->accum;
356   else
357     result = -1;
358
359   return result;
360 }
361
362 /**
363  * gst_segment_clip:
364  * @segment: a #GstSegment structure.
365  * @format: the format of the segment.
366  * @start: the start position in the segment
367  * @stop: the stop position in the segment
368  * @clip_start: the clipped start position in the segment
369  * @clip_stop: the clipped stop position in the segment
370  *
371  * Clip the given @start and @stop values to the segment boundaries given
372  * in @segment.
373  *
374  * Returns: TRUE if the given @start and @stop times fall partially in 
375  *     @segment, FALSE if the values are completely outside of the segment.
376  */
377 gboolean
378 gst_segment_clip (GstSegment * segment, GstFormat format, gint64 start,
379     gint64 stop, gint64 * clip_start, gint64 * clip_stop)
380 {
381   g_return_val_if_fail (segment != NULL, FALSE);
382
383   if (segment->format == GST_FORMAT_UNDEFINED)
384     segment->format = format;
385   else
386     g_return_val_if_fail (segment->format == format, FALSE);
387
388   /* we need a valid start position */
389   if (start == -1)
390     return FALSE;
391
392   /* if we have a stop position and start is bigger, we're
393    * outside of the segment */
394   if (segment->stop != -1 && start >= segment->stop)
395     return FALSE;
396
397   /* if a stop position is given and is before the segment start,
398    * we're outside of the segment */
399   if (stop != -1 && stop <= segment->start)
400     return FALSE;
401
402   if (clip_start)
403     *clip_start = MAX (start, segment->start);
404
405   if (clip_stop) {
406     if (stop == -1)
407       *clip_stop = segment->stop;
408     else if (segment->stop == -1)
409       *clip_stop = MAX (-1, stop);
410     else
411       *clip_stop = MIN (stop, segment->stop);
412
413     if (segment->duration != -1)
414       *clip_stop = MIN (*clip_stop, segment->duration);
415   }
416
417   return TRUE;
418 }