|
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 static GstSegment * |
|
87 gst_segment_copy (GstSegment * segment) |
|
88 { |
|
89 GstSegment *result = NULL; |
|
90 |
|
91 if (segment) { |
|
92 result = gst_segment_new (); |
|
93 memcpy (result, segment, sizeof (GstSegment)); |
|
94 } |
|
95 return result; |
|
96 } |
|
97 #ifdef __SYMBIAN32__ |
|
98 EXPORT_C |
|
99 #endif |
|
100 |
|
101 |
|
102 GType |
|
103 gst_segment_get_type (void) |
|
104 { |
|
105 static GType gst_segment_type = 0; |
|
106 |
|
107 if (G_UNLIKELY (gst_segment_type == 0)) { |
|
108 gst_segment_type = g_boxed_type_register_static ("GstSegment", |
|
109 (GBoxedCopyFunc) gst_segment_copy, (GBoxedFreeFunc) gst_segment_free); |
|
110 } |
|
111 |
|
112 return gst_segment_type; |
|
113 } |
|
114 |
|
115 /** |
|
116 * gst_segment_new: |
|
117 * |
|
118 * Allocate a new #GstSegment structure and initialize it using |
|
119 * gst_segment_init(). |
|
120 * |
|
121 * Returns: a new #GstSegment, free with gst_segment_free(). |
|
122 */ |
|
123 #ifdef __SYMBIAN32__ |
|
124 EXPORT_C |
|
125 #endif |
|
126 |
|
127 GstSegment * |
|
128 gst_segment_new (void) |
|
129 { |
|
130 GstSegment *result; |
|
131 |
|
132 result = g_new0 (GstSegment, 1); |
|
133 gst_segment_init (result, GST_FORMAT_UNDEFINED); |
|
134 |
|
135 return result; |
|
136 } |
|
137 |
|
138 /** |
|
139 * gst_segment_free: |
|
140 * @segment: a #GstSegment |
|
141 * |
|
142 * Free the allocated segment @segment. |
|
143 */ |
|
144 #ifdef __SYMBIAN32__ |
|
145 EXPORT_C |
|
146 #endif |
|
147 |
|
148 void |
|
149 gst_segment_free (GstSegment * segment) |
|
150 { |
|
151 g_free (segment); |
|
152 } |
|
153 |
|
154 /** |
|
155 * gst_segment_init: |
|
156 * @segment: a #GstSegment structure. |
|
157 * @format: the format of the segment. |
|
158 * |
|
159 * The start/last_stop positions are set to 0 and the stop/duration |
|
160 * fields are set to -1 (unknown). The default rate of 1.0 and no |
|
161 * flags are set. |
|
162 * |
|
163 * Initialize @segment to its default values. |
|
164 */ |
|
165 #ifdef __SYMBIAN32__ |
|
166 EXPORT_C |
|
167 #endif |
|
168 |
|
169 void |
|
170 gst_segment_init (GstSegment * segment, GstFormat format) |
|
171 { |
|
172 g_return_if_fail (segment != NULL); |
|
173 |
|
174 segment->rate = 1.0; |
|
175 segment->abs_rate = 1.0; |
|
176 segment->applied_rate = 1.0; |
|
177 segment->format = format; |
|
178 segment->flags = 0; |
|
179 segment->start = 0; |
|
180 segment->stop = -1; |
|
181 segment->time = 0; |
|
182 segment->accum = 0; |
|
183 segment->last_stop = 0; |
|
184 segment->duration = -1; |
|
185 } |
|
186 |
|
187 /** |
|
188 * gst_segment_set_duration: |
|
189 * @segment: a #GstSegment structure. |
|
190 * @format: the format of the segment. |
|
191 * @duration: the duration of the segment info or -1 if unknown. |
|
192 * |
|
193 * Set the duration of the segment to @duration. This function is mainly |
|
194 * used by elements that perform seeking and know the total duration of the |
|
195 * segment. |
|
196 * |
|
197 * This field should be set to allow seeking requests relative to the |
|
198 * duration. |
|
199 */ |
|
200 #ifdef __SYMBIAN32__ |
|
201 EXPORT_C |
|
202 #endif |
|
203 |
|
204 void |
|
205 gst_segment_set_duration (GstSegment * segment, GstFormat format, |
|
206 gint64 duration) |
|
207 { |
|
208 g_return_if_fail (segment != NULL); |
|
209 |
|
210 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
211 segment->format = format; |
|
212 else |
|
213 g_return_if_fail (segment->format == format); |
|
214 |
|
215 segment->duration = duration; |
|
216 } |
|
217 |
|
218 /** |
|
219 * gst_segment_set_last_stop: |
|
220 * @segment: a #GstSegment structure. |
|
221 * @format: the format of the segment. |
|
222 * @position: the position |
|
223 * |
|
224 * Set the last observed stop position in the segment to @position. |
|
225 * |
|
226 * This field should be set to allow seeking requests relative to the |
|
227 * current playing position. |
|
228 */ |
|
229 #ifdef __SYMBIAN32__ |
|
230 EXPORT_C |
|
231 #endif |
|
232 |
|
233 void |
|
234 gst_segment_set_last_stop (GstSegment * segment, GstFormat format, |
|
235 gint64 position) |
|
236 { |
|
237 g_return_if_fail (segment != NULL); |
|
238 |
|
239 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
240 segment->format = format; |
|
241 else |
|
242 g_return_if_fail (segment->format == format); |
|
243 |
|
244 segment->last_stop = MAX (segment->start, position); |
|
245 } |
|
246 |
|
247 /** |
|
248 * gst_segment_set_seek: |
|
249 * @segment: a #GstSegment structure. |
|
250 * @rate: the rate of the segment. |
|
251 * @format: the format of the segment. |
|
252 * @flags: the seek flags for the segment |
|
253 * @start_type: the seek method |
|
254 * @start: the seek start value |
|
255 * @stop_type: the seek method |
|
256 * @stop: the seek stop value |
|
257 * @update: boolean holding whether last_stop was updated. |
|
258 * |
|
259 * Update the segment structure with the field values of a seek event (see |
|
260 * gst_event_new_seek()). |
|
261 * |
|
262 * After calling this method, the segment field last_stop and time will |
|
263 * contain the requested new position in the segment. The new requested |
|
264 * position in the segment depends on @rate and @start_type and @stop_type. |
|
265 * |
|
266 * For positive @rate, the new position in the segment is the new @segment |
|
267 * start field when it was updated with a @start_type different from |
|
268 * #GST_SEEK_TYPE_NONE. If no update was performed on @segment start position |
|
269 * (#GST_SEEK_TYPE_NONE), @start is ignored and @segment last_stop is |
|
270 * unmodified. |
|
271 * |
|
272 * For negative @rate, the new position in the segment is the new @segment |
|
273 * stop field when it was updated with a @stop_type different from |
|
274 * #GST_SEEK_TYPE_NONE. If no stop was previously configured in the segment, the |
|
275 * duration of the segment will be used to update the stop position. |
|
276 * If no update was performed on @segment stop position (#GST_SEEK_TYPE_NONE), |
|
277 * @stop is ignored and @segment last_stop is unmodified. |
|
278 * |
|
279 * The applied rate of the segment will be set to 1.0 by default. |
|
280 * If the caller can apply a rate change, it should update @segment |
|
281 * rate and applied_rate after calling this function. |
|
282 * |
|
283 * @update will be set to TRUE if a seek should be performed to the segment |
|
284 * last_stop field. This field can be FALSE if, for example, only the @rate |
|
285 * has been changed but not the playback position. |
|
286 */ |
|
287 #ifdef __SYMBIAN32__ |
|
288 EXPORT_C |
|
289 #endif |
|
290 |
|
291 void |
|
292 gst_segment_set_seek (GstSegment * segment, gdouble rate, |
|
293 GstFormat format, GstSeekFlags flags, |
|
294 GstSeekType start_type, gint64 start, |
|
295 GstSeekType stop_type, gint64 stop, gboolean * update) |
|
296 { |
|
297 gboolean update_stop, update_start; |
|
298 gint64 last_stop; |
|
299 |
|
300 g_return_if_fail (rate != 0.0); |
|
301 g_return_if_fail (segment != NULL); |
|
302 |
|
303 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
304 segment->format = format; |
|
305 |
|
306 update_start = update_stop = TRUE; |
|
307 |
|
308 /* segment->start is never invalid */ |
|
309 switch (start_type) { |
|
310 case GST_SEEK_TYPE_NONE: |
|
311 /* no update to segment, take previous start */ |
|
312 start = segment->start; |
|
313 update_start = FALSE; |
|
314 break; |
|
315 case GST_SEEK_TYPE_SET: |
|
316 /* start holds desired position, map -1 to the start */ |
|
317 if (start == -1) |
|
318 start = 0; |
|
319 /* start must be 0 or the formats must match */ |
|
320 g_return_if_fail (start == 0 || segment->format == format); |
|
321 break; |
|
322 case GST_SEEK_TYPE_CUR: |
|
323 g_return_if_fail (start == 0 || segment->format == format); |
|
324 /* add start to currently configured segment */ |
|
325 start = segment->start + start; |
|
326 break; |
|
327 case GST_SEEK_TYPE_END: |
|
328 if (segment->duration != -1) { |
|
329 g_return_if_fail (start == 0 || segment->format == format); |
|
330 /* add start to total length */ |
|
331 start = segment->duration + start; |
|
332 } else { |
|
333 /* no update if duration unknown */ |
|
334 start = segment->start; |
|
335 update_start = FALSE; |
|
336 } |
|
337 break; |
|
338 } |
|
339 /* bring in sane range */ |
|
340 if (segment->duration != -1) |
|
341 start = CLAMP (start, 0, segment->duration); |
|
342 else |
|
343 start = MAX (start, 0); |
|
344 |
|
345 /* stop can be -1 if we have not configured a stop. */ |
|
346 switch (stop_type) { |
|
347 case GST_SEEK_TYPE_NONE: |
|
348 stop = segment->stop; |
|
349 update_stop = FALSE; |
|
350 break; |
|
351 case GST_SEEK_TYPE_SET: |
|
352 /* stop holds required value, if it's not -1, it must be of the same |
|
353 * format as the segment. */ |
|
354 g_return_if_fail (stop == -1 || segment->format == format); |
|
355 break; |
|
356 case GST_SEEK_TYPE_CUR: |
|
357 if (segment->stop != -1) { |
|
358 /* only add compatible formats or 0 */ |
|
359 g_return_if_fail (stop == 0 || segment->format == format); |
|
360 stop = segment->stop + stop; |
|
361 } else |
|
362 stop = -1; |
|
363 break; |
|
364 case GST_SEEK_TYPE_END: |
|
365 if (segment->duration != -1) { |
|
366 /* only add compatible formats or 0 */ |
|
367 g_return_if_fail (stop == 0 || segment->format == format); |
|
368 stop = segment->duration + stop; |
|
369 } else { |
|
370 stop = segment->stop; |
|
371 update_stop = FALSE; |
|
372 } |
|
373 break; |
|
374 } |
|
375 |
|
376 /* if we have a valid stop time, make sure it is clipped */ |
|
377 if (stop != -1) { |
|
378 if (segment->duration != -1) |
|
379 stop = CLAMP (stop, 0, segment->duration); |
|
380 else |
|
381 stop = MAX (stop, 0); |
|
382 } |
|
383 |
|
384 /* we can't have stop before start */ |
|
385 if (stop != -1) |
|
386 g_return_if_fail (start <= stop); |
|
387 |
|
388 segment->rate = rate; |
|
389 segment->abs_rate = ABS (rate); |
|
390 segment->applied_rate = 1.0; |
|
391 segment->flags = flags; |
|
392 segment->start = start; |
|
393 segment->stop = stop; |
|
394 segment->time = start; |
|
395 |
|
396 last_stop = segment->last_stop; |
|
397 if (update_start && rate > 0.0) { |
|
398 last_stop = start; |
|
399 } |
|
400 if (update_stop && rate < 0.0) { |
|
401 if (stop != -1) |
|
402 last_stop = stop; |
|
403 else { |
|
404 if (segment->duration != -1) |
|
405 last_stop = segment->duration; |
|
406 else |
|
407 last_stop = 0; |
|
408 } |
|
409 } |
|
410 /* set update arg to reflect update of last_stop */ |
|
411 if (update) |
|
412 *update = last_stop != segment->last_stop; |
|
413 |
|
414 /* update new position */ |
|
415 segment->last_stop = last_stop; |
|
416 } |
|
417 |
|
418 /** |
|
419 * gst_segment_set_newsegment: |
|
420 * @segment: a #GstSegment structure. |
|
421 * @update: flag indicating a new segment is started or updated |
|
422 * @rate: the rate of the segment. |
|
423 * @format: the format of the segment. |
|
424 * @start: the new start value |
|
425 * @stop: the new stop value |
|
426 * @time: the new stream time |
|
427 * |
|
428 * Update the segment structure with the field values of a new segment event and |
|
429 * with a default applied_rate of 1.0. |
|
430 * |
|
431 * Since: 0.10.6 |
|
432 */ |
|
433 #ifdef __SYMBIAN32__ |
|
434 EXPORT_C |
|
435 #endif |
|
436 |
|
437 void |
|
438 gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate, |
|
439 GstFormat format, gint64 start, gint64 stop, gint64 time) |
|
440 { |
|
441 gst_segment_set_newsegment_full (segment, update, rate, 1.0, format, start, |
|
442 stop, time); |
|
443 } |
|
444 |
|
445 /** |
|
446 * gst_segment_set_newsegment_full: |
|
447 * @segment: a #GstSegment structure. |
|
448 * @update: flag indicating a new segment is started or updated |
|
449 * @rate: the rate of the segment. |
|
450 * @applied_rate: the applied rate of the segment. |
|
451 * @format: the format of the segment. |
|
452 * @start: the new start value |
|
453 * @stop: the new stop value |
|
454 * @time: the new stream time |
|
455 * |
|
456 * Update the segment structure with the field values of a new segment event. |
|
457 */ |
|
458 #ifdef __SYMBIAN32__ |
|
459 EXPORT_C |
|
460 #endif |
|
461 |
|
462 void |
|
463 gst_segment_set_newsegment_full (GstSegment * segment, gboolean update, |
|
464 gdouble rate, gdouble applied_rate, GstFormat format, gint64 start, |
|
465 gint64 stop, gint64 time) |
|
466 { |
|
467 gint64 duration; |
|
468 |
|
469 g_return_if_fail (rate != 0.0); |
|
470 g_return_if_fail (applied_rate != 0.0); |
|
471 g_return_if_fail (segment != NULL); |
|
472 |
|
473 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
474 segment->format = format; |
|
475 |
|
476 /* any other format with 0 also gives time 0, the other values are |
|
477 * invalid in the format though. */ |
|
478 if (format != segment->format && start == 0) { |
|
479 format = segment->format; |
|
480 if (stop != 0) |
|
481 stop = -1; |
|
482 if (time != 0) |
|
483 time = -1; |
|
484 } |
|
485 |
|
486 g_return_if_fail (segment->format == format); |
|
487 |
|
488 if (update) { |
|
489 if (segment->rate > 0.0) { |
|
490 /* an update to the current segment is done, elapsed time is |
|
491 * difference between the old start and new start. */ |
|
492 if (start > segment->start) |
|
493 duration = start - segment->start; |
|
494 else |
|
495 duration = 0; |
|
496 } else { |
|
497 /* for negative rates, the elapsed duration is the diff between the stop |
|
498 * positions */ |
|
499 if (stop != -1 && stop < segment->stop) |
|
500 duration = segment->stop - stop; |
|
501 else |
|
502 duration = 0; |
|
503 } |
|
504 } else { |
|
505 /* the new segment has to be aligned with the old segment. |
|
506 * We first update the accumulated time of the previous |
|
507 * segment. the accumulated time is used when syncing to the |
|
508 * clock. |
|
509 */ |
|
510 if (segment->stop != -1) { |
|
511 duration = segment->stop - segment->start; |
|
512 } else if (segment->last_stop != -1) { |
|
513 /* else use last seen timestamp as segment stop */ |
|
514 duration = segment->last_stop - segment->start; |
|
515 } else { |
|
516 /* else we don't know and throw a warning.. really, this should |
|
517 * be fixed in the element. */ |
|
518 g_warning ("closing segment of unknown duration, assuming duration of 0"); |
|
519 duration = 0; |
|
520 } |
|
521 } |
|
522 /* use previous rate to calculate duration */ |
|
523 if (segment->abs_rate != 1.0) |
|
524 duration /= segment->abs_rate; |
|
525 |
|
526 /* accumulate duration */ |
|
527 segment->accum += duration; |
|
528 |
|
529 /* then update the current segment */ |
|
530 segment->rate = rate; |
|
531 segment->abs_rate = ABS (rate); |
|
532 segment->applied_rate = applied_rate; |
|
533 segment->start = start; |
|
534 segment->last_stop = start; |
|
535 segment->stop = stop; |
|
536 segment->time = time; |
|
537 } |
|
538 |
|
539 /** |
|
540 * gst_segment_to_stream_time: |
|
541 * @segment: a #GstSegment structure. |
|
542 * @format: the format of the segment. |
|
543 * @position: the position in the segment |
|
544 * |
|
545 * Translate @position to stream time using the currently configured |
|
546 * segment. The @position value must be between @segment start and |
|
547 * stop value. |
|
548 * |
|
549 * This function is typically used by elements that need to operate on |
|
550 * the stream time of the buffers it receives, such as effect plugins. |
|
551 * In those use cases, @position is typically the buffer timestamp or |
|
552 * clock time that one wants to convert to the stream time. |
|
553 * The stream time is always between 0 and the total duration of the |
|
554 * media stream. |
|
555 * |
|
556 * Returns: the position in stream_time or -1 when an invalid position |
|
557 * was given. |
|
558 */ |
|
559 #ifdef __SYMBIAN32__ |
|
560 EXPORT_C |
|
561 #endif |
|
562 |
|
563 gint64 |
|
564 gst_segment_to_stream_time (GstSegment * segment, GstFormat format, |
|
565 gint64 position) |
|
566 { |
|
567 gint64 result, start, stop, time; |
|
568 gdouble abs_applied_rate; |
|
569 |
|
570 g_return_val_if_fail (segment != NULL, -1); |
|
571 |
|
572 /* format does not matter for -1 */ |
|
573 if (G_UNLIKELY (position == -1)) |
|
574 return -1; |
|
575 |
|
576 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
577 segment->format = format; |
|
578 |
|
579 /* if we have the position for the same format as the segment, we can compare |
|
580 * the start and stop values, otherwise we assume 0 and -1 */ |
|
581 if (segment->format == format) { |
|
582 start = segment->start; |
|
583 stop = segment->stop; |
|
584 time = segment->time; |
|
585 } else { |
|
586 start = 0; |
|
587 stop = -1; |
|
588 time = 0; |
|
589 } |
|
590 |
|
591 /* outside of the segment boundary stop */ |
|
592 if (G_UNLIKELY (stop != -1 && position > stop)) |
|
593 return -1; |
|
594 |
|
595 /* before the segment boundary */ |
|
596 if (G_UNLIKELY (position < start)) |
|
597 return -1; |
|
598 |
|
599 /* time must be known */ |
|
600 if (G_UNLIKELY (time == -1)) |
|
601 return -1; |
|
602 |
|
603 /* bring to uncorrected position in segment */ |
|
604 result = position - start; |
|
605 |
|
606 abs_applied_rate = ABS (segment->applied_rate); |
|
607 |
|
608 /* correct for applied rate if needed */ |
|
609 if (abs_applied_rate != 1.0) |
|
610 result *= abs_applied_rate; |
|
611 |
|
612 /* add or subtract from segment time based on applied rate */ |
|
613 if (segment->applied_rate > 0.0) { |
|
614 /* correct for segment time */ |
|
615 result += time; |
|
616 } else { |
|
617 /* correct for segment time, clamp at 0. Streams with a negative |
|
618 * applied_rate have timestamps between start and stop, as usual, but have |
|
619 * the time member starting high and going backwards. */ |
|
620 if (time > result) |
|
621 result = time - result; |
|
622 else |
|
623 result = 0; |
|
624 } |
|
625 |
|
626 return result; |
|
627 } |
|
628 |
|
629 /** |
|
630 * gst_segment_to_running_time: |
|
631 * @segment: a #GstSegment structure. |
|
632 * @format: the format of the segment. |
|
633 * @position: the position in the segment |
|
634 * |
|
635 * Translate @position to the total running time using the currently configured |
|
636 * and previously accumulated segments. Position is a value between @segment |
|
637 * start and stop time. |
|
638 * |
|
639 * This function is typically used by elements that need to synchronize to the |
|
640 * global clock in a pipeline. The runnning time is a constantly increasing value |
|
641 * starting from 0. When gst_segment_init() is called, this value will reset to |
|
642 * 0. |
|
643 * |
|
644 * This function returns -1 if the position is outside of @segment start and stop. |
|
645 * |
|
646 * Returns: the position as the total running time or -1 when an invalid position |
|
647 * was given. |
|
648 */ |
|
649 #ifdef __SYMBIAN32__ |
|
650 EXPORT_C |
|
651 #endif |
|
652 |
|
653 gint64 |
|
654 gst_segment_to_running_time (GstSegment * segment, GstFormat format, |
|
655 gint64 position) |
|
656 { |
|
657 gint64 result; |
|
658 gint64 start, stop, accum; |
|
659 |
|
660 g_return_val_if_fail (segment != NULL, -1); |
|
661 |
|
662 if (G_UNLIKELY (position == -1)) |
|
663 return -1; |
|
664 |
|
665 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
666 segment->format = format; |
|
667 |
|
668 /* if we have the position for the same format as the segment, we can compare |
|
669 * the start and stop values, otherwise we assume 0 and -1 */ |
|
670 if (segment->format == format) { |
|
671 start = segment->start; |
|
672 stop = segment->stop; |
|
673 accum = segment->accum; |
|
674 } else { |
|
675 start = 0; |
|
676 stop = -1; |
|
677 accum = 0; |
|
678 } |
|
679 |
|
680 /* before the segment boundary */ |
|
681 if (G_UNLIKELY (position < start)) |
|
682 return -1; |
|
683 |
|
684 if (segment->rate > 0.0) { |
|
685 /* outside of the segment boundary stop */ |
|
686 if (G_UNLIKELY (stop != -1 && position > stop)) |
|
687 return -1; |
|
688 |
|
689 /* bring to uncorrected position in segment */ |
|
690 result = position - start; |
|
691 } else { |
|
692 /* cannot continue if no stop position set or outside of |
|
693 * the segment. */ |
|
694 if (G_UNLIKELY (stop == -1 || position > stop)) |
|
695 return -1; |
|
696 |
|
697 /* bring to uncorrected position in segment */ |
|
698 result = stop - position; |
|
699 } |
|
700 |
|
701 /* scale based on the rate, avoid division by and conversion to |
|
702 * float when not needed */ |
|
703 if (segment->abs_rate != 1.0) |
|
704 result /= segment->abs_rate; |
|
705 |
|
706 /* correct for accumulated segments */ |
|
707 result += accum; |
|
708 |
|
709 return result; |
|
710 } |
|
711 |
|
712 /** |
|
713 * gst_segment_clip: |
|
714 * @segment: a #GstSegment structure. |
|
715 * @format: the format of the segment. |
|
716 * @start: the start position in the segment |
|
717 * @stop: the stop position in the segment |
|
718 * @clip_start: the clipped start position in the segment |
|
719 * @clip_stop: the clipped stop position in the segment |
|
720 * |
|
721 * Clip the given @start and @stop values to the segment boundaries given |
|
722 * in @segment. @start and @stop are compared and clipped to @segment |
|
723 * start and stop values. |
|
724 * |
|
725 * If the function returns FALSE, @start and @stop are known to fall |
|
726 * outside of @segment and @clip_start and @clip_stop are not updated. |
|
727 * |
|
728 * When the function returns TRUE, @clip_start and @clip_stop will be |
|
729 * updated. If @clip_start or @clip_stop are different from @start or @stop |
|
730 * respectively, the region fell partially in the segment. |
|
731 * |
|
732 * Note that when @stop is -1, @clip_stop will be set to the end of the |
|
733 * segment. Depending on the use case, this may or may not be what you want. |
|
734 * |
|
735 * Returns: TRUE if the given @start and @stop times fall partially or |
|
736 * completely in @segment, FALSE if the values are completely outside |
|
737 * of the segment. |
|
738 */ |
|
739 #ifdef __SYMBIAN32__ |
|
740 EXPORT_C |
|
741 #endif |
|
742 |
|
743 gboolean |
|
744 gst_segment_clip (GstSegment * segment, GstFormat format, gint64 start, |
|
745 gint64 stop, gint64 * clip_start, gint64 * clip_stop) |
|
746 { |
|
747 g_return_val_if_fail (segment != NULL, FALSE); |
|
748 |
|
749 if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED)) |
|
750 segment->format = format; |
|
751 else |
|
752 g_return_val_if_fail (segment->format == format, FALSE); |
|
753 |
|
754 /* if we have a stop position and a valid start and start is bigger, |
|
755 * we're outside of the segment */ |
|
756 if (G_UNLIKELY (segment->stop != -1 && start != -1 && start >= segment->stop)) |
|
757 return FALSE; |
|
758 |
|
759 /* if a stop position is given and is before the segment start, |
|
760 * we're outside of the segment */ |
|
761 if (G_UNLIKELY (stop != -1 && stop != start && stop <= segment->start)) |
|
762 return FALSE; |
|
763 |
|
764 if (clip_start) { |
|
765 if (start == -1) |
|
766 *clip_start = -1; |
|
767 else |
|
768 *clip_start = MAX (start, segment->start); |
|
769 } |
|
770 |
|
771 if (clip_stop) { |
|
772 if (stop == -1) |
|
773 *clip_stop = segment->stop; |
|
774 else if (segment->stop == -1) |
|
775 *clip_stop = MAX (-1, stop); |
|
776 else |
|
777 *clip_stop = MIN (stop, segment->stop); |
|
778 |
|
779 if (segment->duration != -1) |
|
780 *clip_stop = MIN (*clip_stop, segment->duration); |
|
781 } |
|
782 |
|
783 return TRUE; |
|
784 } |