gst/gstbin.c: Help the compiler a bit with type registration.
[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 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.
57  *
58  * The current position in the segment should be set with the gst_segment_set_last_stop().
59  * The public last_stop field contains the last set stop position in the segment.
60  *
61  * For elements that perform seeks, the current segment should be updated with the
62  * gst_segment_set_seek() and the values from the seek event. This method will update
63  * all the segment fields. The last_pos field will contain the new playback position.
64  * If the cur_type was different from GST_SEEK_TYPE_NONE, playback continues from
65  * the last_pos position, possibly with updated flags or rate.
66  *
67  * For elements that want to us #GstSegment to track the playback region, use
68  * gst_segment_set_newsegment() to update the segment fields with the information from
69  * the newsegment event. The gst_segment_clip() method can be used to check and clip
70  * the media data to the segment boundaries.
71  *
72  * For elements that want to synchronize to the pipeline clock, gst_segment_to_running_time()
73  * can be used to convert a timestamp to a value that can be used to synchronize
74  * to the clock. This function takes into account all accumulated segments.
75  *
76  * For elements that need to perform operations on media data in stream_time, 
77  * gst_segment_to_stream_time() can be used to convert a timestamp and the segment
78  * info to stream time (which is always between 0 and the duration of the stream).
79  *
80  * Last reviewed on 2005-12-12 (0.10.0)
81  */
82
83 static GstSegment *
84 gst_segment_copy (GstSegment * segment)
85 {
86   GstSegment *result = NULL;
87
88   if (segment) {
89     result = gst_segment_new ();
90     memcpy (result, segment, sizeof (GstSegment));
91   }
92   return NULL;
93 }
94
95 GType
96 gst_segment_get_type (void)
97 {
98   static GType gst_segment_type = 0;
99
100   if (G_UNLIKELY (gst_segment_type == 0)) {
101     gst_segment_type = g_boxed_type_register_static ("GstSegment",
102         (GBoxedCopyFunc) gst_segment_copy, (GBoxedFreeFunc) gst_segment_free);
103   }
104
105   return gst_segment_type;
106 }
107
108 /**
109  * gst_segment_new:
110  *
111  * Allocate a new #GstSegment structure and initialize it using 
112  * gst_segment_init().
113  *
114  * Returns: a new #GstSegment, free with gst_segment_free().
115  */
116 GstSegment *
117 gst_segment_new (void)
118 {
119   GstSegment *result;
120
121   result = g_new0 (GstSegment, 1);
122   gst_segment_init (result, GST_FORMAT_UNDEFINED);
123
124   return result;
125 }
126
127 /**
128  * gst_segment_free:
129  * @segment: a #GstSegment
130  *
131  * Free the allocated segment @segment.
132  */
133 void
134 gst_segment_free (GstSegment * segment)
135 {
136   g_free (segment);
137 }
138
139 /**
140  * gst_segment_init:
141  * @segment: a #GstSegment structure.
142  * @format: the format of the segment.
143  *
144  * The start/last_stop positions are set to 0 and the stop/duration
145  * fields are set to -1 (unknown). The default rate of 1.0 and no
146  * flags are set.
147  *
148  * Initialize @segment to its default values.
149  */
150 void
151 gst_segment_init (GstSegment * segment, GstFormat format)
152 {
153   g_return_if_fail (segment != NULL);
154
155   segment->rate = 1.0;
156   segment->abs_rate = 1.0;
157   segment->format = format;
158   segment->flags = 0;
159   segment->start = 0;
160   segment->stop = -1;
161   segment->time = 0;
162   segment->accum = 0;
163   segment->last_stop = 0;
164   segment->duration = -1;
165 }
166
167 /**
168  * gst_segment_set_duration:
169  * @segment: a #GstSegment structure.
170  * @format: the format of the segment.
171  * @duration: the duration of the segment info.
172  *
173  * Set the duration of the segment to @duration. This function is mainly
174  * used by elements that perform seeking and know the total duration of the
175  * segment.
176  * 
177  * This field should be set to allow seeking request relative to the
178  * duration.
179  */
180 void
181 gst_segment_set_duration (GstSegment * segment, GstFormat format,
182     gint64 duration)
183 {
184   g_return_if_fail (segment != NULL);
185
186   if (segment->format == GST_FORMAT_UNDEFINED)
187     segment->format = format;
188   else
189     g_return_if_fail (segment->format == format);
190
191   segment->duration = duration;
192 }
193
194 /**
195  * gst_segment_set_last_stop:
196  * @segment: a #GstSegment structure.
197  * @format: the format of the segment.
198  * @position: the position 
199  *
200  * Set the last observed stop position in the segment to @position.
201  */
202 void
203 gst_segment_set_last_stop (GstSegment * segment, GstFormat format,
204     gint64 position)
205 {
206   g_return_if_fail (segment != NULL);
207
208   if (segment->format == GST_FORMAT_UNDEFINED)
209     segment->format = format;
210   else
211     g_return_if_fail (segment->format == format);
212
213   segment->last_stop = MAX (segment->start, position);
214 }
215
216 /**
217  * gst_segment_set_seek:
218  * @segment: a #GstSegment structure.
219  * @rate: the rate of the segment.
220  * @format: the format of the segment.
221  * @flags: the seek flags for the segment
222  * @cur_type: the seek method
223  * @cur: the seek start value
224  * @stop_type: the seek method
225  * @stop: the seek stop value
226  * @update: boolean holding whether an update the current segment is
227  *    needed.
228  *
229  * Update the segment structure with the field values of a seek event.
230  *
231  * After calling this method, the segment field last_stop will contain
232  * the requested new position in the segment. If the cur_type is different
233  * from GST_SEEK_TYPE_NONE, the current position is not updated and 
234  * streaming should continue from the last position, possibly with
235  * updated rate, flags or stop position.
236  */
237 void
238 gst_segment_set_seek (GstSegment * segment, gdouble rate,
239     GstFormat format, GstSeekFlags flags,
240     GstSeekType cur_type, gint64 cur,
241     GstSeekType stop_type, gint64 stop, gboolean * update)
242 {
243   gboolean update_stop, update_start;
244
245   g_return_if_fail (rate != 0.0);
246   g_return_if_fail (segment != NULL);
247
248   if (segment->format == GST_FORMAT_UNDEFINED)
249     segment->format = format;
250   else
251     g_return_if_fail (segment->format == format);
252
253   update_stop = update_start = TRUE;
254
255   /* start is never invalid */
256   switch (cur_type) {
257     case GST_SEEK_TYPE_NONE:
258       /* no update to segment */
259       cur = segment->start;
260       update_start = FALSE;
261       break;
262     case GST_SEEK_TYPE_SET:
263       /* cur holds desired position */
264       break;
265     case GST_SEEK_TYPE_CUR:
266       /* add cur to currently configure segment */
267       cur = segment->start + cur;
268       break;
269     case GST_SEEK_TYPE_END:
270       if (segment->duration != -1) {
271         /* add cur to total length */
272         cur = segment->duration + cur;
273       } else {
274         /* no update if duration unknown */
275         cur = segment->start;
276         update_start = FALSE;
277       }
278       break;
279   }
280   /* bring in sane range */
281   if (segment->duration != -1)
282     cur = CLAMP (cur, 0, segment->duration);
283   else
284     cur = MAX (cur, 0);
285
286   /* stop can be -1 if we have not configured a stop. */
287   switch (stop_type) {
288     case GST_SEEK_TYPE_NONE:
289       stop = segment->stop;
290       update_stop = FALSE;
291       break;
292     case GST_SEEK_TYPE_SET:
293       /* stop folds required value */
294       break;
295     case GST_SEEK_TYPE_CUR:
296       if (segment->stop != -1)
297         stop = segment->stop + stop;
298       else
299         stop = -1;
300       break;
301     case GST_SEEK_TYPE_END:
302       if (segment->duration != -1)
303         stop = segment->duration + stop;
304       else {
305         stop = segment->stop;
306         update_stop = FALSE;
307       }
308       break;
309   }
310
311   /* if we have a valid stop time, make sure it is clipped */
312   if (stop != -1) {
313     if (segment->duration != -1)
314       stop = CLAMP (stop, 0, segment->duration);
315     else
316       stop = MAX (stop, 0);
317   }
318
319   /* we can't have stop before start */
320   if (stop != -1)
321     g_return_if_fail (cur <= stop);
322
323   segment->rate = rate;
324   segment->abs_rate = ABS (rate);
325   segment->flags = flags;
326   segment->start = cur;
327   if (update_start) {
328     segment->last_stop = cur;
329   }
330   segment->time = segment->last_stop;
331   segment->stop = stop;
332
333   if (update)
334     *update = update_start || update_stop;
335 }
336
337 /**
338  * gst_segment_set_newsegment:
339  * @segment: a #GstSegment structure.
340  * @update: flag indicating a new segment is started or updated
341  * @rate: the rate of the segment.
342  * @format: the format of the segment.
343  * @start: the new start value
344  * @stop: the new stop value
345  * @time: the new stream time
346  *
347  * Update the segment structure with the field values of a new segment event.
348  */
349 void
350 gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
351     GstFormat format, gint64 start, gint64 stop, gint64 time)
352 {
353   gint64 duration;
354
355   g_return_if_fail (rate != 0.0);
356   g_return_if_fail (segment != NULL);
357
358   if (segment->format == GST_FORMAT_UNDEFINED)
359     segment->format = format;
360
361   /* any other format with 0 also gives time 0, the other values are
362    * invalid in the format though. */
363   if (format != segment->format && start == 0) {
364     format = segment->format;
365     if (stop != 0)
366       stop = -1;
367     if (time != 0)
368       time = -1;
369   }
370
371   g_return_if_fail (segment->format == format);
372
373   if (update) {
374     /* an update to the current segment is done, elapsed time is
375      * difference between the old start and new start. */
376     duration = start - segment->start;
377   } else {
378     /* the new segment has to be aligned with the old segment.
379      * We first update the accumulated time of the previous
380      * segment. the accumulated time is used when syncing to the
381      * clock. 
382      */
383     if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
384       duration = segment->stop - segment->start;
385     } else if (GST_CLOCK_TIME_IS_VALID (segment->last_stop)) {
386       /* else use last seen timestamp as segment stop */
387       duration = segment->last_stop - segment->start;
388     } else {
389       /* else we don't know */
390       duration = 0;
391     }
392   }
393   /* use previous rate to calculate duration */
394   segment->accum += gst_gdouble_to_guint64 (
395       (gst_guint64_to_gdouble (duration) / segment->abs_rate));
396
397   /* then update the current segment */
398   segment->rate = rate;
399   segment->abs_rate = ABS (rate);
400   segment->start = start;
401   segment->last_stop = start;
402   segment->stop = stop;
403   segment->time = time;
404 }
405
406 /**
407  * gst_segment_to_stream_time:
408  * @segment: a #GstSegment structure.
409  * @format: the format of the segment.
410  * @position: the position in the segment
411  *
412  * Translate @position to stream time using the currently configured 
413  * segment.
414  *
415  * This function is typically used by elements that need to operate on
416  * the stream time of the buffers it receives, such as effect plugins.
417  * The stream time is always between 0 and the total duration of the 
418  * media stream.
419  *
420  * Returns: the position in stream_time.
421  */
422 gint64
423 gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
424     gint64 position)
425 {
426   gint64 result, time;
427
428   g_return_val_if_fail (segment != NULL, FALSE);
429
430   if (segment->format == GST_FORMAT_UNDEFINED)
431     segment->format = format;
432   else
433     g_return_val_if_fail (segment->format == format, FALSE);
434
435   if ((time = segment->time) == -1)
436     time = 0;
437
438   if (position != -1 && position >= segment->start)
439     result = ((position - segment->start) / segment->abs_rate) + time;
440   else
441     result = -1;
442
443   return result;
444 }
445
446 /**
447  * gst_segment_to_running_time:
448  * @segment: a #GstSegment structure.
449  * @format: the format of the segment.
450  * @position: the position in the segment
451  *
452  * Translate @position to the total running time using the currently configured 
453  * segment.
454  *
455  * This function is typically used by elements that need to synchronize to the
456  * global clock in a pipeline. The runnning time is a constantly increasing value
457  * starting from 0.
458  *
459  * Returns: the position as the total running time.
460  */
461 gint64
462 gst_segment_to_running_time (GstSegment * segment, GstFormat format,
463     gint64 position)
464 {
465   gint64 result;
466
467   g_return_val_if_fail (segment != NULL, -1);
468
469   if (segment->format == GST_FORMAT_UNDEFINED)
470     segment->format = format;
471   else if (segment->accum)
472     g_return_val_if_fail (segment->format == format, -1);
473
474   if (position != -1 && position >= segment->start)
475     result = ((position - segment->start) / segment->abs_rate) + segment->accum;
476   else
477     result = -1;
478
479   return result;
480 }
481
482 /**
483  * gst_segment_clip:
484  * @segment: a #GstSegment structure.
485  * @format: the format of the segment.
486  * @start: the start position in the segment
487  * @stop: the stop position in the segment
488  * @clip_start: the clipped start position in the segment
489  * @clip_stop: the clipped stop position in the segment
490  *
491  * Clip the given @start and @stop values to the segment boundaries given
492  * in @segment.
493  *
494  * Returns: TRUE if the given @start and @stop times fall partially in 
495  *     @segment, FALSE if the values are completely outside of the segment.
496  */
497 gboolean
498 gst_segment_clip (GstSegment * segment, GstFormat format, gint64 start,
499     gint64 stop, gint64 * clip_start, gint64 * clip_stop)
500 {
501   g_return_val_if_fail (segment != NULL, FALSE);
502
503   if (segment->format == GST_FORMAT_UNDEFINED)
504     segment->format = format;
505   else
506     g_return_val_if_fail (segment->format == format, FALSE);
507
508   /* if we have a stop position and a valid start and start is bigger, 
509    * we're outside of the segment */
510   if (segment->stop != -1 && start != -1 && start >= segment->stop)
511     return FALSE;
512
513   /* if a stop position is given and is before the segment start,
514    * we're outside of the segment */
515   if (stop != -1 && stop <= segment->start)
516     return FALSE;
517
518   if (clip_start) {
519     if (start == -1)
520       *clip_start = -1;
521     else
522       *clip_start = MAX (start, segment->start);
523   }
524
525   if (clip_stop) {
526     if (stop == -1)
527       *clip_stop = segment->stop;
528     else if (segment->stop == -1)
529       *clip_stop = MAX (-1, stop);
530     else
531       *clip_stop = MIN (stop, segment->stop);
532
533     if (segment->duration != -1)
534       *clip_stop = MIN (*clip_stop, segment->duration);
535   }
536
537   return TRUE;
538 }