2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wim.taymans@chello.be>
5 * gstscheduler.c: Default scheduling code for most cases
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 #define CLASS(obj) GST_SCHEDULER_CLASS (G_OBJECT_GET_CLASS (obj))
25 #include "gst_private.h"
27 #include "gstsystemclock.h"
28 #include "gstscheduler.h"
29 #include "gstregistry.h"
31 static void gst_scheduler_class_init (GstSchedulerClass *klass);
32 static void gst_scheduler_init (GstScheduler *sched);
34 static GstObjectClass *parent_class = NULL;
36 static gchar *_default_name = NULL;
39 gst_scheduler_get_type (void)
41 static GType _gst_scheduler_type = 0;
43 if (!_gst_scheduler_type) {
44 static const GTypeInfo scheduler_info = {
45 sizeof (GstSchedulerClass),
48 (GClassInitFunc) gst_scheduler_class_init,
51 sizeof (GstScheduler),
53 (GInstanceInitFunc) gst_scheduler_init,
57 _gst_scheduler_type = g_type_register_static (GST_TYPE_OBJECT, "GstScheduler",
58 &scheduler_info, G_TYPE_FLAG_ABSTRACT);
60 return _gst_scheduler_type;
64 gst_scheduler_class_init (GstSchedulerClass *klass)
66 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
70 gst_scheduler_init (GstScheduler *sched)
72 sched->clock_providers = NULL;
73 sched->clock_receivers = NULL;
74 sched->schedulers = NULL;
75 sched->state = GST_SCHEDULER_STATE_NONE;
77 sched->parent_sched = NULL;
82 * gst_scheduler_setup:
83 * @sched: the scheduler
85 * Prepare the scheduler.
88 gst_scheduler_setup (GstScheduler *sched)
90 g_return_if_fail (GST_IS_SCHEDULER (sched));
92 if (CLASS (sched)->setup)
93 CLASS (sched)->setup (sched);
97 * gst_scheduler_reset:
98 * @sched: the scheduler
100 * Reset the scheduler
103 gst_scheduler_reset (GstScheduler *sched)
105 g_return_if_fail (GST_IS_SCHEDULER (sched));
107 if (CLASS (sched)->reset)
108 CLASS (sched)->reset (sched);
112 * gst_scheduler_pad_connect:
113 * @sched: the scheduler
114 * @srcpad: the srcpad to connect
115 * @sinkpad: the sinkpad to connect to
117 * Connect the srcpad to the given sinkpad.
120 gst_scheduler_pad_connect (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad)
122 g_return_if_fail (GST_IS_SCHEDULER (sched));
123 g_return_if_fail (GST_IS_PAD (srcpad));
124 g_return_if_fail (GST_IS_PAD (sinkpad));
126 if (CLASS (sched)->pad_connect)
127 CLASS (sched)->pad_connect (sched, srcpad, sinkpad);
131 * gst_scheduler_pad_disconnect:
132 * @sched: the scheduler
133 * @srcpad: the srcpad to disconnect
134 * @sinkpad: the sinkpad to disconnect from
136 * Disconnect the srcpad to the given sinkpad.
139 gst_scheduler_pad_disconnect (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad)
141 g_return_if_fail (GST_IS_SCHEDULER (sched));
142 g_return_if_fail (GST_IS_PAD (srcpad));
143 g_return_if_fail (GST_IS_PAD (sinkpad));
145 if (CLASS (sched)->pad_disconnect)
146 CLASS (sched)->pad_disconnect (sched, srcpad, sinkpad);
150 * gst_scheduler_pad_select:
151 * @sched: the scheduler
152 * @padlist: the padlist to select on
154 * register the given padlist for a select operation.
156 * Returns: the pad which received a buffer.
159 gst_scheduler_pad_select (GstScheduler *sched, GList *padlist)
161 g_return_val_if_fail (GST_IS_SCHEDULER (sched), NULL);
162 g_return_val_if_fail (padlist != NULL, NULL);
164 if (CLASS (sched)->pad_select)
165 CLASS (sched)->pad_select (sched, padlist);
171 * gst_scheduler_add_element:
172 * @sched: the scheduler
173 * @element: the element to add to the scheduler
175 * Add an element to the scheduler.
178 gst_scheduler_add_element (GstScheduler *sched, GstElement *element)
180 g_return_if_fail (GST_IS_SCHEDULER (sched));
181 g_return_if_fail (GST_IS_ELEMENT (element));
183 if (element->getclockfunc) {
184 sched->clock_providers = g_list_prepend (sched->clock_providers, element);
185 GST_DEBUG (GST_CAT_CLOCK, "added clock provider %s", GST_ELEMENT_NAME (element));
187 if (element->setclockfunc) {
188 sched->clock_receivers = g_list_prepend (sched->clock_receivers, element);
189 GST_DEBUG (GST_CAT_CLOCK, "added clock receiver %s", GST_ELEMENT_NAME (element));
192 if (CLASS (sched)->add_element)
193 CLASS (sched)->add_element (sched, element);
197 * gst_scheduler_remove_element:
198 * @sched: the scheduler
199 * @element: the element to remove
201 * Remove an element from the scheduler.
204 gst_scheduler_remove_element (GstScheduler *sched, GstElement *element)
208 g_return_if_fail (GST_IS_SCHEDULER (sched));
209 g_return_if_fail (GST_IS_ELEMENT (element));
211 sched->clock_providers = g_list_remove (sched->clock_providers, element);
212 sched->clock_receivers = g_list_remove (sched->clock_receivers, element);
214 if (CLASS (sched)->remove_element)
215 CLASS (sched)->remove_element (sched, element);
217 for (pads = element->pads; pads; pads = pads->next) {
218 GstPad *pad = GST_PAD (pads->data);
220 if (GST_IS_REAL_PAD (pad)) {
221 gst_pad_unset_scheduler (GST_PAD (pads->data));
227 * gst_scheduler_state_transition:
228 * @sched: the scheduler
229 * @element: the element with the state transition
230 * @transition: the state transition
232 * Tell the scheduler that an element changed its state.
234 * Returns: a GstElementStateReturn indicating success or failure
235 * of the state transition.
237 GstElementStateReturn
238 gst_scheduler_state_transition (GstScheduler *sched, GstElement *element, gint transition)
240 g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_STATE_FAILURE);
241 g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
243 if (element == sched->parent && sched->parent_sched == NULL) {
245 switch (transition) {
246 case GST_STATE_READY_TO_PAUSED:
248 GstClock *clock = gst_scheduler_get_clock (sched);
251 gst_clock_reset (clock);
253 GST_DEBUG (GST_CAT_CLOCK, "scheduler READY to PAUSED clock is %p (%s)", clock,
254 (clock ? GST_OBJECT_NAME (clock) : "nil"));
256 sched->current_clock = clock;
259 case GST_STATE_PAUSED_TO_PLAYING:
261 GstClock *clock = gst_scheduler_get_clock (sched);
263 GST_DEBUG (GST_CAT_CLOCK, "scheduler PAUSED to PLAYING clock is %p (%s)", clock,
264 (clock ? GST_OBJECT_NAME (clock) : "nil"));
266 sched->current_clock = clock;
268 gst_scheduler_set_clock (sched, sched->current_clock);
269 if (sched->current_clock)
270 gst_clock_set_active (sched->current_clock, TRUE);
273 case GST_STATE_PLAYING_TO_PAUSED:
274 if (sched->current_clock)
275 gst_clock_set_active (sched->current_clock, FALSE);
280 if (CLASS (sched)->state_transition)
281 return CLASS (sched)->state_transition (sched, element, transition);
283 return GST_STATE_SUCCESS;
287 * gst_scheduler_add_scheduler:
288 * @sched: a #GstScheduler to add to
289 * @sched2: the #GstScheduler to add
291 * Notifies the scheduler that it has to monitor this scheduler.
294 gst_scheduler_add_scheduler (GstScheduler *sched, GstScheduler *sched2)
296 g_return_if_fail (GST_IS_SCHEDULER (sched));
297 g_return_if_fail (GST_IS_SCHEDULER (sched2));
299 sched->schedulers = g_list_prepend (sched->schedulers, sched2);
300 sched2->parent_sched = sched;
302 if (CLASS (sched)->add_scheduler)
303 CLASS (sched)->add_scheduler (sched, sched2);
307 * gst_scheduler_remove_scheduler:
308 * @sched: the scheduler
309 * @sched2: the scheduler to remove
311 a Notifies the scheduler that it can stop monitoring this scheduler.
314 gst_scheduler_remove_scheduler (GstScheduler *sched, GstScheduler *sched2)
316 g_return_if_fail (GST_IS_SCHEDULER (sched));
317 g_return_if_fail (GST_IS_SCHEDULER (sched2));
319 sched->schedulers = g_list_remove (sched->schedulers, sched2);
320 sched2->parent_sched = NULL;
322 if (CLASS (sched)->remove_scheduler)
323 CLASS (sched)->remove_scheduler (sched, sched2);
327 * gst_scheduler_lock_element:
328 * @sched: the scheduler
329 * @element: the element to lock
331 * Acquire a lock on the given element in the given scheduler.
334 gst_scheduler_lock_element (GstScheduler *sched, GstElement *element)
336 g_return_if_fail (GST_IS_SCHEDULER (sched));
337 g_return_if_fail (GST_IS_ELEMENT (element));
339 if (CLASS (sched)->lock_element)
340 CLASS (sched)->lock_element (sched, element);
344 * gst_scheduler_unlock_element:
345 * @sched: the scheduler
346 * @element: the element to unlock
348 * Release the lock on the given element in the given scheduler.
351 gst_scheduler_unlock_element (GstScheduler *sched, GstElement *element)
353 g_return_if_fail (GST_IS_SCHEDULER (sched));
354 g_return_if_fail (GST_IS_ELEMENT (element));
356 if (CLASS (sched)->unlock_element)
357 CLASS (sched)->unlock_element (sched, element);
361 * gst_scheduler_error:
362 * @sched: the scheduler
363 * @element: the element with the error
365 * Tell the scheduler an element was in error
368 gst_scheduler_error (GstScheduler *sched, GstElement *element)
370 g_return_if_fail (GST_IS_SCHEDULER (sched));
371 g_return_if_fail (GST_IS_ELEMENT (element));
373 if (CLASS (sched)->error)
374 CLASS (sched)->error (sched, element);
378 * gst_scheduler_yield:
379 * @sched: the scheduler
380 * @element: the element requesting a yield
382 * Tell the scheduler to schedule another element.
385 gst_scheduler_yield (GstScheduler *sched, GstElement *element)
387 g_return_if_fail (GST_IS_SCHEDULER (sched));
388 g_return_if_fail (GST_IS_ELEMENT (element));
390 if (CLASS (sched)->yield)
391 CLASS (sched)->yield (sched, element);
395 * gst_scheduler_interrupt:
396 * @sched: the scheduler
397 * @element: the element requesting an interrupt
399 * Tell the scheduler to interrupt execution of this element.
401 * Returns: TRUE if the element should return NULL from the chain/get
405 gst_scheduler_interrupt (GstScheduler *sched, GstElement *element)
407 g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
408 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
410 if (CLASS (sched)->interrupt)
411 return CLASS (sched)->interrupt (sched, element);
417 * gst_scheduler_get_clock:
418 * @sched: the scheduler
420 * Get the current clock used by the scheduler
422 * Returns: a GstClock
425 gst_scheduler_get_clock (GstScheduler *sched)
427 GstClock *clock = NULL;
429 /* if we have a fixed clock, use that one */
430 if (GST_FLAG_IS_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK)) {
431 clock = sched->clock;
433 GST_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock,
434 (clock ? GST_OBJECT_NAME (clock) : "nil"));
437 GList *schedulers = sched->schedulers;
438 GList *providers = sched->clock_providers;
440 /* try to get a clock from one of the schedulers we manage first */
442 GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
444 clock = gst_scheduler_get_clock (scheduler);
448 schedulers = g_list_next (schedulers);
450 /* still no clock, try to find one in the providers */
451 while (!clock && providers) {
452 clock = gst_element_get_clock (GST_ELEMENT (providers->data));
454 providers = g_list_next (providers);
456 /* still no clock, use a system clock */
457 if (!clock && sched->parent_sched == NULL) {
458 clock = gst_system_clock_obtain ();
461 GST_DEBUG (GST_CAT_CLOCK, "scheduler selected clock %p (%s)", clock,
462 (clock ? GST_OBJECT_NAME (clock) : "nil"));
468 * gst_scheduler_use_clock:
469 * @sched: the scheduler
470 * @clock: the clock to use
472 * Force the scheduler to use the given clock. The scheduler will
473 * always use the given clock even if new clock providers are added
477 gst_scheduler_use_clock (GstScheduler *sched, GstClock *clock)
479 g_return_if_fail (sched != NULL);
480 g_return_if_fail (GST_IS_SCHEDULER (sched));
482 GST_FLAG_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
483 sched->clock = clock;
485 GST_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock,
486 (clock ? GST_OBJECT_NAME (clock) : "nil"));
490 * gst_scheduler_set_clock:
491 * @sched: the scheduler
492 * @clock: the clock to set
494 * Set the clock for the scheduler. The clock will be distributed
495 * to all the elements managed by the scheduler.
498 gst_scheduler_set_clock (GstScheduler *sched, GstClock *clock)
503 g_return_if_fail (sched != NULL);
504 g_return_if_fail (GST_IS_SCHEDULER (sched));
506 receivers = sched->clock_receivers;
507 schedulers = sched->schedulers;
509 sched->current_clock = clock;
511 GST_DEBUG (GST_CAT_CLOCK, "scheduler setting clock %p (%s)", clock,
512 (clock ? GST_OBJECT_NAME (clock) : "nil"));
515 GstElement *element = GST_ELEMENT (receivers->data);
517 gst_element_set_clock (element, clock);
518 receivers = g_list_next (receivers);
521 GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
523 gst_scheduler_set_clock (scheduler, clock);
524 schedulers = g_list_next (schedulers);
529 * gst_scheduler_auto_clock:
530 * @sched: the scheduler
532 * Let the scheduler select a clock automatically.
535 gst_scheduler_auto_clock (GstScheduler *sched)
537 g_return_if_fail (sched != NULL);
538 g_return_if_fail (GST_IS_SCHEDULER (sched));
540 GST_FLAG_UNSET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
543 GST_DEBUG (GST_CAT_CLOCK, "scheduler using automatic clock");
547 * gst_scheduler_clock_wait:
548 * @sched: the scheduler
549 * @element: the element that wants to wait
550 * @clock: the clock to use
551 * @time: the time to wait for
552 * @jitter: the time difference between requested time and actual time
554 * Wait till the clock reaches a specific time
556 * Returns: the status of the operation
559 gst_scheduler_clock_wait (GstScheduler *sched, GstElement *element, GstClock *clock, GstClockTime time,
560 GstClockTimeDiff *jitter)
562 g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_CLOCK_ERROR);
564 if (CLASS (sched)->clock_wait)
565 return CLASS (sched)->clock_wait (sched, element, clock, time, jitter);
567 return GST_CLOCK_TIMEOUT;
571 * gst_scheduler_iterate:
572 * @sched: the scheduler
574 * Perform one iteration on the scheduler.
576 * Returns: a boolean indicating something usefull has happened.
579 gst_scheduler_iterate (GstScheduler *sched)
581 g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
583 if (CLASS (sched)->iterate)
584 return CLASS (sched)->iterate (sched);
591 * gst_scheduler_show:
592 * @sched: the scheduler
594 * Dump the state of the scheduler
597 gst_scheduler_show (GstScheduler *sched)
599 g_return_if_fail (GST_IS_SCHEDULER (sched));
601 if (CLASS (sched)->show)
602 CLASS (sched)->show (sched);
606 * Factory stuff starts here
609 static void gst_scheduler_factory_class_init (GstSchedulerFactoryClass *klass);
610 static void gst_scheduler_factory_init (GstSchedulerFactory *factory);
612 static GstPluginFeatureClass *factory_parent_class = NULL;
613 /* static guint gst_scheduler_factory_signals[LAST_SIGNAL] = { 0 }; */
616 gst_scheduler_factory_get_type (void)
618 static GType schedulerfactory_type = 0;
620 if (!schedulerfactory_type) {
621 static const GTypeInfo schedulerfactory_info = {
622 sizeof (GstSchedulerFactoryClass),
625 (GClassInitFunc) gst_scheduler_factory_class_init,
628 sizeof(GstSchedulerFactory),
630 (GInstanceInitFunc) gst_scheduler_factory_init,
633 schedulerfactory_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE,
634 "GstSchedulerFactory", &schedulerfactory_info, 0);
636 return schedulerfactory_type;
640 gst_scheduler_factory_class_init (GstSchedulerFactoryClass *klass)
642 GObjectClass *gobject_class;
643 GstObjectClass *gstobject_class;
644 GstPluginFeatureClass *gstpluginfeature_class;
646 gobject_class = (GObjectClass*)klass;
647 gstobject_class = (GstObjectClass*)klass;
648 gstpluginfeature_class = (GstPluginFeatureClass*) klass;
650 factory_parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE);
653 _default_name = g_strdup ("standard");
657 gst_scheduler_factory_init (GstSchedulerFactory *factory)
663 * gst_scheduler_factory_new:
664 * @name: name of schedulerfactory to create
665 * @longdesc: long description of schedulerfactory to create
666 * @type: the gtk type of the GstScheduler element of this factory
668 * Create a new schedulerfactory with the given parameters
670 * Returns: a new #GstSchedulerFactory.
673 gst_scheduler_factory_new (const gchar *name, const gchar *longdesc, GType type)
675 GstSchedulerFactory *factory;
677 g_return_val_if_fail(name != NULL, NULL);
678 factory = gst_scheduler_factory_find (name);
680 factory = GST_SCHEDULER_FACTORY (g_object_new (GST_TYPE_SCHEDULER_FACTORY, NULL));
683 GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (name);
684 if (factory->longdesc)
685 g_free (factory->longdesc);
686 factory->longdesc = g_strdup (longdesc);
687 factory->type = type;
693 * gst_scheduler_factory_destroy:
694 * @factory: factory to destroy
696 * Removes the scheduler from the global list.
699 gst_scheduler_factory_destroy (GstSchedulerFactory *factory)
701 g_return_if_fail (factory != NULL);
703 /* we don't free the struct bacause someone might have a handle to it.. */
707 * gst_scheduler_factory_find:
708 * @name: name of schedulerfactory to find
710 * Search for an schedulerfactory of the given name.
712 * Returns: #GstSchedulerFactory if found, NULL otherwise
715 gst_scheduler_factory_find (const gchar *name)
717 GstPluginFeature *feature;
719 g_return_val_if_fail(name != NULL, NULL);
721 GST_DEBUG (0,"gstscheduler: find \"%s\"", name);
723 feature = gst_registry_pool_find_feature (name, GST_TYPE_SCHEDULER_FACTORY);
725 return GST_SCHEDULER_FACTORY (feature);
731 * gst_scheduler_factory_create:
732 * @factory: the factory used to create the instance
733 * @parent: the parent element of this scheduler
735 * Create a new #GstScheduler instance from the
736 * given schedulerfactory with the given parent. @parent will
737 * have its scheduler set to the returned #GstScheduler instance.
739 * Returns: A new #GstScheduler instance with a reference count of %1.
742 gst_scheduler_factory_create (GstSchedulerFactory *factory, GstElement *parent)
744 GstScheduler *new = NULL;
746 g_return_val_if_fail (factory != NULL, NULL);
747 g_return_val_if_fail (parent != NULL, NULL);
749 if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
750 g_return_val_if_fail (factory->type != 0, NULL);
752 new = GST_SCHEDULER (g_object_new (factory->type, NULL));
753 new->parent = parent;
755 GST_ELEMENT_SCHED (parent) = new;
757 /* let's refcount the scheduler */
758 gst_object_ref (GST_OBJECT (new));
759 gst_object_sink (GST_OBJECT (new));
766 * gst_scheduler_factory_make:
767 * @name: the name of the factory used to create the instance
768 * @parent: the parent element of this scheduler
770 * Create a new #GstScheduler instance from the
771 * schedulerfactory with the given name and parent. @parent will
772 * have its scheduler set to the returned #GstScheduler instance.
773 * If %NULL is passed as @name, the default scheduler name will
776 * Returns: A new #GstScheduler instance with a reference count of %1.
779 gst_scheduler_factory_make (const gchar *name, GstElement *parent)
781 GstSchedulerFactory *factory;
784 factory = gst_scheduler_factory_find (name);
786 factory = gst_scheduler_factory_find (gst_scheduler_factory_get_default_name ());
791 return gst_scheduler_factory_create (factory, parent);
795 * gst_scheduler_factory_set_default_name:
796 * @name: the name of the factory used as a default
798 * Set the default schedulerfactory name.
801 gst_scheduler_factory_set_default_name (const gchar* name)
804 g_free (_default_name);
806 _default_name = g_strdup (name);
810 * gst_scheduler_factory_get_default_name:
812 * Get the default schedulerfactory name.
814 * Returns: the name of the default scheduler.
817 gst_scheduler_factory_get_default_name (void)
819 return _default_name;