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