1 /* GStreamer
2 *
3 * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * 2011 Stefan Sauer <ensonic@users.sf.net>
5 *
6 * gsttimedvaluecontrolsource.c: Base class for timeed value based control
7 * sources
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 /**
26 * SECTION:gsttimedvaluecontrolsource
27 * @title: GstTimedValueControlSource
28 * @short_description: timed value control source base class
29 *
30 * Base class for #GstControlSource that use time-stamped values.
31 *
32 * When overriding bind, chain up first to give this bind implementation a
33 * chance to setup things.
34 *
35 * All functions are MT-safe.
36 *
37 */
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <glib-object.h>
43 #include <gst/gst.h>
44
45 #include "gstinterpolationcontrolsource.h"
46 #include "gst/glib-compat-private.h"
47
48 #define GST_CAT_DEFAULT controller_debug
49 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
50
51 #define _do_init \
52 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "timed value control source", 0, \
53 "timed value control source base class")
54
55 #define gst_timed_value_control_source_parent_class parent_class
56 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstTimedValueControlSource,
57 gst_timed_value_control_source, GST_TYPE_CONTROL_SOURCE, _do_init);
58
59
60 enum
61 {
62 VALUE_CHANGED_SIGNAL,
63 VALUE_ADDED_SIGNAL,
64 VALUE_REMOVED_SIGNAL,
65 LAST_SIGNAL
66 };
67
68 static guint gst_timed_value_control_source_signals[LAST_SIGNAL] = { 0 };
69
70 /**
71 * gst_control_point_free:
72 * @cp: the object to free
73 *
74 * Frees all data allocated by a #GstControlPoint instance.
75 */
76 void
gst_control_point_free(GstControlPoint * cp)77 gst_control_point_free (GstControlPoint * cp)
78 {
79 g_return_if_fail (cp);
80
81 g_slice_free (GstControlPoint, cp);
82 }
83
84 GstControlPoint *
gst_control_point_copy(GstControlPoint * cp)85 gst_control_point_copy (GstControlPoint * cp)
86 {
87 return g_slice_dup (GstControlPoint, cp);
88 }
89
90 GType
gst_control_point_get_type(void)91 gst_control_point_get_type (void)
92 {
93 static volatile gsize type_id = 0;
94
95 if (g_once_init_enter (&type_id)) {
96 GType tmp =
97 g_boxed_type_register_static (g_intern_static_string
98 ("GstControlPoint"),
99 (GBoxedCopyFunc) gst_control_point_copy,
100 (GBoxedFreeFunc) gst_control_point_free);
101 g_once_init_leave (&type_id, tmp);
102 }
103
104 return type_id;
105 }
106
107 static void
gst_timed_value_control_source_reset(GstTimedValueControlSource * self)108 gst_timed_value_control_source_reset (GstTimedValueControlSource * self)
109 {
110 GstControlSource *csource = (GstControlSource *) self;
111
112 csource->get_value = NULL;
113 csource->get_value_array = NULL;
114
115 if (self->values) {
116 g_sequence_free (self->values);
117 self->values = NULL;
118 }
119
120 self->nvalues = 0;
121 self->valid_cache = FALSE;
122 }
123
124 /*
125 * gst_control_point_compare:
126 * @p1: a pointer to a #GstControlPoint
127 * @p2: a pointer to a #GstControlPoint
128 *
129 * Compare function for g_list operations that operates on two #GstControlPoint
130 * parameters.
131 */
132 static gint
gst_control_point_compare(gconstpointer p1,gconstpointer p2)133 gst_control_point_compare (gconstpointer p1, gconstpointer p2)
134 {
135 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
136 GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp;
137
138 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
139 }
140
141 /*
142 * gst_control_point_find:
143 * @p1: a pointer to a #GstControlPoint
144 * @p2: a pointer to a #GstClockTime
145 * @user_data: supplied user data
146 *
147 * Compare function for g_sequence operations that operates on a #GstControlPoint and
148 * a #GstClockTime.
149 */
150 static gint
gst_control_point_find(gconstpointer p1,gconstpointer p2,gpointer user_data)151 gst_control_point_find (gconstpointer p1, gconstpointer p2, gpointer user_data)
152 {
153 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
154 GstClockTime ct2 = *(GstClockTime *) p2;
155
156 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
157 }
158
159 static GstControlPoint *
_make_new_cp(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)160 _make_new_cp (GstTimedValueControlSource * self, GstClockTime timestamp,
161 const gdouble value)
162 {
163 GstControlPoint *cp;
164
165 /* create a new GstControlPoint */
166 cp = g_slice_new0 (GstControlPoint);
167 cp->timestamp = timestamp;
168 cp->value = value;
169
170 return cp;
171 }
172
173 static void
gst_timed_value_control_source_set_internal(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)174 gst_timed_value_control_source_set_internal (GstTimedValueControlSource *
175 self, GstClockTime timestamp, const gdouble value)
176 {
177 GstControlPoint *cp;
178
179 g_mutex_lock (&self->lock);
180
181 /* check if a control point for the timestamp already exists */
182 if (G_LIKELY (self->values)) {
183 GSequenceIter *iter = g_sequence_lookup (self->values, ×tamp,
184 (GCompareDataFunc) gst_control_point_find, NULL);
185
186 if (iter) {
187 GstControlPoint *cp = g_sequence_get (iter);
188
189 /* update control point */
190 cp->value = value;
191 g_mutex_unlock (&self->lock);
192
193 g_signal_emit (self,
194 gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL], 0, cp);
195 goto done;
196 }
197 } else {
198 self->values = g_sequence_new ((GDestroyNotify) gst_control_point_free);
199 GST_INFO ("create new timed value sequence");
200 }
201
202 /* sort new cp into the prop->values list */
203 cp = _make_new_cp (self, timestamp, value);
204 g_sequence_insert_sorted (self->values, cp,
205 (GCompareDataFunc) gst_control_point_compare, NULL);
206 self->nvalues++;
207 g_mutex_unlock (&self->lock);
208
209 g_signal_emit (self,
210 gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL], 0, cp);
211
212 done:
213 self->valid_cache = FALSE;
214 }
215
216 /**
217 * gst_timed_value_control_source_find_control_point_iter:
218 * @self: the control source to search in
219 * @timestamp: the search key
220 *
221 * Find last value before given timestamp in control point list.
222 * If all values in the control point list come after the given
223 * timestamp or no values exist, %NULL is returned.
224 *
225 * For use in control source implementations.
226 *
227 * Returns: (transfer none): the found #GSequenceIter or %NULL
228 */
gst_timed_value_control_source_find_control_point_iter(GstTimedValueControlSource * self,GstClockTime timestamp)229 GSequenceIter *gst_timed_value_control_source_find_control_point_iter
230 (GstTimedValueControlSource * self, GstClockTime timestamp)
231 {
232 GSequenceIter *iter;
233
234 if (!self->values)
235 return NULL;
236
237 iter =
238 g_sequence_search (self->values, ×tamp,
239 (GCompareDataFunc) gst_control_point_find, NULL);
240
241 /* g_sequence_search() returns the iter where timestamp
242 * would be inserted, i.e. the iter > timestamp, so
243 * we need to get the previous one. And of course, if
244 * there is no previous one, we return NULL. */
245 if (g_sequence_iter_is_begin (iter))
246 return NULL;
247
248 return g_sequence_iter_prev (iter);
249 }
250
251
252 /**
253 * gst_timed_value_control_source_set:
254 * @self: the #GstTimedValueControlSource object
255 * @timestamp: the time the control-change is scheduled for
256 * @value: the control-value
257 *
258 * Set the value of given controller-handled property at a certain time.
259 *
260 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
261 */
262 gboolean
gst_timed_value_control_source_set(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)263 gst_timed_value_control_source_set (GstTimedValueControlSource * self,
264 GstClockTime timestamp, const gdouble value)
265 {
266 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
267 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
268
269 gst_timed_value_control_source_set_internal (self, timestamp, value);
270
271 return TRUE;
272 }
273
274 /**
275 * gst_timed_value_control_source_set_from_list:
276 * @self: the #GstTimedValueControlSource object
277 * @timedvalues: (transfer none) (element-type GstTimedValue): a list
278 * with #GstTimedValue items
279 *
280 * Sets multiple timed values at once.
281 *
282 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
283 */
284 gboolean
gst_timed_value_control_source_set_from_list(GstTimedValueControlSource * self,const GSList * timedvalues)285 gst_timed_value_control_source_set_from_list (GstTimedValueControlSource *
286 self, const GSList * timedvalues)
287 {
288 const GSList *node;
289 GstTimedValue *tv;
290 gboolean res = FALSE;
291
292 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
293
294 for (node = timedvalues; node; node = g_slist_next (node)) {
295 tv = node->data;
296 if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) {
297 GST_WARNING ("GstTimedValued with invalid timestamp passed to %s",
298 GST_FUNCTION);
299 } else {
300 gst_timed_value_control_source_set_internal (self, tv->timestamp,
301 tv->value);
302 res = TRUE;
303 }
304 }
305 return res;
306 }
307
308 /**
309 * gst_timed_value_control_source_unset:
310 * @self: the #GstTimedValueControlSource object
311 * @timestamp: the time the control-change should be removed from
312 *
313 * Used to remove the value of given controller-handled property at a certain
314 * time.
315 *
316 * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise.
317 */
318 gboolean
gst_timed_value_control_source_unset(GstTimedValueControlSource * self,GstClockTime timestamp)319 gst_timed_value_control_source_unset (GstTimedValueControlSource * self,
320 GstClockTime timestamp)
321 {
322 GSequenceIter *iter;
323 gboolean res = FALSE;
324 GstControlPoint *cp = NULL;
325
326 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
327 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
328
329 g_mutex_lock (&self->lock);
330 /* check if a control point for the timestamp exists */
331 if (G_LIKELY (self->values) && (iter =
332 g_sequence_lookup (self->values, ×tamp,
333 (GCompareDataFunc) gst_control_point_find, NULL))) {
334
335 /* Iter contains the iter right after timestamp, i.e.
336 * we need to get the previous one and check the timestamp
337 */
338 cp = g_slice_dup (GstControlPoint, g_sequence_get (iter));
339 g_sequence_remove (iter);
340 self->nvalues--;
341 self->valid_cache = FALSE;
342 res = TRUE;
343 }
344 g_mutex_unlock (&self->lock);
345
346 if (cp) {
347 g_signal_emit (self,
348 gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL], 0, cp);
349 g_slice_free (GstControlPoint, cp);
350 }
351
352 return res;
353 }
354
355 /**
356 * gst_timed_value_control_source_unset_all:
357 * @self: the #GstTimedValueControlSource object
358 *
359 * Used to remove all time-stamped values of given controller-handled property
360 *
361 */
362 void
gst_timed_value_control_source_unset_all(GstTimedValueControlSource * self)363 gst_timed_value_control_source_unset_all (GstTimedValueControlSource * self)
364 {
365 g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
366
367 g_mutex_lock (&self->lock);
368 /* free GstControlPoint structures */
369 if (self->values) {
370 g_sequence_free (self->values);
371 self->values = NULL;
372 }
373 self->nvalues = 0;
374 self->valid_cache = FALSE;
375
376 g_mutex_unlock (&self->lock);
377 }
378
379 static void
_append_control_point(GstControlPoint * cp,GQueue * res)380 _append_control_point (GstControlPoint * cp, GQueue * res)
381 {
382 g_queue_push_tail (res, cp);
383 }
384
385 /**
386 * gst_timed_value_control_source_get_all:
387 * @self: the #GstTimedValueControlSource to get the list from
388 *
389 * Returns a read-only copy of the list of #GstTimedValue for the given property.
390 * Free the list after done with it.
391 *
392 * Returns: (transfer container) (element-type GstTimedValue): a copy
393 * of the list, or %NULL if the property isn't handled by the controller
394 */
395 GList *
gst_timed_value_control_source_get_all(GstTimedValueControlSource * self)396 gst_timed_value_control_source_get_all (GstTimedValueControlSource * self)
397 {
398 GQueue res = G_QUEUE_INIT;
399
400 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), NULL);
401
402 g_mutex_lock (&self->lock);
403 if (G_LIKELY (self->values))
404 g_sequence_foreach (self->values, (GFunc) _append_control_point, &res);
405 g_mutex_unlock (&self->lock);
406
407 return res.head;
408 }
409
410 /**
411 * gst_timed_value_control_source_get_count:
412 * @self: the #GstTimedValueControlSource to get the number of values from
413 *
414 * Get the number of control points that are set.
415 *
416 * Returns: the number of control points that are set.
417 */
418 gint
gst_timed_value_control_source_get_count(GstTimedValueControlSource * self)419 gst_timed_value_control_source_get_count (GstTimedValueControlSource * self)
420 {
421 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), 0);
422 return self->nvalues;
423 }
424
425 /**
426 * gst_timed_value_control_invalidate_cache:
427 * @self: the #GstTimedValueControlSource
428 *
429 * Reset the controlled value cache.
430 */
431 void
gst_timed_value_control_invalidate_cache(GstTimedValueControlSource * self)432 gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self)
433 {
434 g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
435 self->valid_cache = FALSE;
436 }
437
438 static void
gst_timed_value_control_source_init(GstTimedValueControlSource * self)439 gst_timed_value_control_source_init (GstTimedValueControlSource * self)
440 {
441 g_mutex_init (&self->lock);
442 }
443
444 static void
gst_timed_value_control_source_finalize(GObject * obj)445 gst_timed_value_control_source_finalize (GObject * obj)
446 {
447 GstTimedValueControlSource *self = GST_TIMED_VALUE_CONTROL_SOURCE (obj);
448
449 g_mutex_lock (&self->lock);
450 gst_timed_value_control_source_reset (self);
451 g_mutex_unlock (&self->lock);
452 g_mutex_clear (&self->lock);
453
454 G_OBJECT_CLASS (parent_class)->finalize (obj);
455 }
456
457 static void
gst_timed_value_control_source_class_init(GstTimedValueControlSourceClass * klass)458 gst_timed_value_control_source_class_init (GstTimedValueControlSourceClass
459 * klass)
460 {
461 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
462 //GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
463
464 /**
465 * GstTimedValueControlSource::value-changed
466 * @self: The #GstTimedValueControlSource on which a #GstTimedValue has changed
467 * @timed_value: The #GstTimedValue where the value changed
468 *
469 * Emitted right after the new value has been set on @timed_signals
470 *
471 * Since: 1.6
472 */
473 gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL] =
474 g_signal_new ("value-changed", G_TYPE_FROM_CLASS (klass),
475 G_SIGNAL_RUN_FIRST, 0, NULL,
476 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
477 gst_control_point_get_type ());
478
479 /**
480 * GstTimedValueControlSource::value-added
481 * @self: The #GstTimedValueControlSource into which a #GstTimedValue has been
482 * added
483 * @timed_value: The newly added #GstTimedValue
484 *
485 * Emitted right after the new value has been added to @self
486 *
487 * Since: 1.6
488 */
489 gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL] =
490 g_signal_new ("value-added", G_TYPE_FROM_CLASS (klass),
491 G_SIGNAL_RUN_FIRST, 0, NULL,
492 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
493 gst_control_point_get_type ());
494
495 /**
496 * GstTimedValueControlSource::value-removed
497 * @self: The #GstTimedValueControlSource from which a #GstTimedValue has been
498 * removed
499 * @timed_value: The removed #GstTimedValue
500 *
501 * Emitted when @timed_value is removed from @self
502 *
503 * Since: 1.6
504 */
505 gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL] =
506 g_signal_new ("value-removed", G_TYPE_FROM_CLASS (klass),
507 G_SIGNAL_RUN_FIRST, 0, NULL,
508 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
509 gst_control_point_get_type ());
510
511
512 gobject_class->finalize = gst_timed_value_control_source_finalize;
513 }
514