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"
30 #include "gstregistry.h"
32 static void gst_scheduler_class_init (GstSchedulerClass *klass);
33 static void gst_scheduler_init (GstScheduler *sched);
35 static GstObjectClass *parent_class = NULL;
37 static gchar *_default_name = NULL;
40 gst_scheduler_get_type (void)
42 static GType _gst_scheduler_type = 0;
44 if (!_gst_scheduler_type) {
45 static const GTypeInfo scheduler_info = {
46 sizeof (GstSchedulerClass),
49 (GClassInitFunc) gst_scheduler_class_init,
52 sizeof (GstScheduler),
54 (GInstanceInitFunc) gst_scheduler_init,
58 _gst_scheduler_type = g_type_register_static (GST_TYPE_OBJECT, "GstScheduler",
59 &scheduler_info, G_TYPE_FLAG_ABSTRACT);
61 return _gst_scheduler_type;
65 gst_scheduler_class_init (GstSchedulerClass *klass)
67 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
71 gst_scheduler_init (GstScheduler *sched)
73 sched->clock_providers = NULL;
74 sched->clock_receivers = NULL;
75 sched->schedulers = NULL;
76 sched->state = GST_SCHEDULER_STATE_NONE;
78 sched->parent_sched = NULL;
83 * gst_scheduler_setup:
84 * @sched: the scheduler
86 * Prepare the scheduler.
89 gst_scheduler_setup (GstScheduler *sched)
91 g_return_if_fail (GST_IS_SCHEDULER (sched));
93 if (CLASS (sched)->setup)
94 CLASS (sched)->setup (sched);
98 * gst_scheduler_get_preferred_stack:
99 * @sched: a #GstScheduler to query.
100 * @stack: the pointer to store the location of the preferred stack in.
101 * @size: the pointer to store the size of the preferred stack in.
103 * Gets the preferred stack location and size of this scheduler.
105 * Returns: TRUE if the scheduler suggested a preferred stacksize and location.
108 gst_scheduler_get_preferred_stack (GstScheduler *sched, gpointer *stack, gulong *size)
110 g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
112 if (CLASS (sched)->get_preferred_stack)
113 return CLASS (sched)->get_preferred_stack (sched, stack, size);
119 * gst_scheduler_reset:
120 * @sched: a #GstScheduler to reset.
122 * Reset the schedulers.
125 gst_scheduler_reset (GstScheduler *sched)
127 g_return_if_fail (GST_IS_SCHEDULER (sched));
129 if (CLASS (sched)->reset)
130 CLASS (sched)->reset (sched);
134 * gst_scheduler_pad_connect:
135 * @sched: the scheduler
136 * @srcpad: the srcpad to connect
137 * @sinkpad: the sinkpad to connect to
139 * Connect the srcpad to the given sinkpad.
142 gst_scheduler_pad_connect (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad)
144 g_return_if_fail (GST_IS_SCHEDULER (sched));
145 g_return_if_fail (GST_IS_PAD (srcpad));
146 g_return_if_fail (GST_IS_PAD (sinkpad));
148 if (CLASS (sched)->pad_connect)
149 CLASS (sched)->pad_connect (sched, srcpad, sinkpad);
153 * gst_scheduler_pad_disconnect:
154 * @sched: the scheduler
155 * @srcpad: the srcpad to disconnect
156 * @sinkpad: the sinkpad to disconnect from
158 * Disconnect the srcpad to the given sinkpad.
161 gst_scheduler_pad_disconnect (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad)
163 g_return_if_fail (GST_IS_SCHEDULER (sched));
164 g_return_if_fail (GST_IS_PAD (srcpad));
165 g_return_if_fail (GST_IS_PAD (sinkpad));
167 if (CLASS (sched)->pad_disconnect)
168 CLASS (sched)->pad_disconnect (sched, srcpad, sinkpad);
172 * gst_scheduler_pad_select:
173 * @sched: the scheduler
174 * @padlist: the padlist to select on
176 * register the given padlist for a select operation.
178 * Returns: the pad which received a buffer.
181 gst_scheduler_pad_select (GstScheduler *sched, GList *padlist)
183 g_return_val_if_fail (GST_IS_SCHEDULER (sched), NULL);
184 g_return_val_if_fail (padlist != NULL, NULL);
186 if (CLASS (sched)->pad_select)
187 CLASS (sched)->pad_select (sched, padlist);
193 * gst_scheduler_add_element:
194 * @sched: the scheduler
195 * @element: the element to add to the scheduler
197 * Add an element to the scheduler.
200 gst_scheduler_add_element (GstScheduler *sched, GstElement *element)
202 g_return_if_fail (GST_IS_SCHEDULER (sched));
203 g_return_if_fail (GST_IS_ELEMENT (element));
205 if (element->getclockfunc) {
206 sched->clock_providers = g_list_prepend (sched->clock_providers, element);
207 GST_DEBUG (GST_CAT_CLOCK, "added clock provider %s", GST_ELEMENT_NAME (element));
209 if (element->setclockfunc) {
210 sched->clock_receivers = g_list_prepend (sched->clock_receivers, element);
211 GST_DEBUG (GST_CAT_CLOCK, "added clock receiver %s", GST_ELEMENT_NAME (element));
214 gst_element_set_scheduler (element, sched);
216 if (CLASS (sched)->add_element)
217 CLASS (sched)->add_element (sched, element);
221 * gst_scheduler_remove_element:
222 * @sched: the scheduler
223 * @element: the element to remove
225 * Remove an element from the scheduler.
228 gst_scheduler_remove_element (GstScheduler *sched, GstElement *element)
230 g_return_if_fail (GST_IS_SCHEDULER (sched));
231 g_return_if_fail (GST_IS_ELEMENT (element));
233 sched->clock_providers = g_list_remove (sched->clock_providers, element);
234 sched->clock_receivers = g_list_remove (sched->clock_receivers, element);
236 gst_element_set_scheduler (element, NULL);
238 if (CLASS (sched)->remove_element)
239 CLASS (sched)->remove_element (sched, element);
243 * gst_scheduler_state_transition:
244 * @sched: the scheduler
245 * @element: the element with the state transition
246 * @transition: the state transition
248 * Tell the scheduler that an element changed its state.
250 * Returns: a GstElementStateReturn indicating success or failure
251 * of the state transition.
253 GstElementStateReturn
254 gst_scheduler_state_transition (GstScheduler *sched, GstElement *element, gint transition)
256 g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_STATE_FAILURE);
257 g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
259 if (element == sched->parent && sched->parent_sched == NULL) {
261 switch (transition) {
262 case GST_STATE_READY_TO_PAUSED:
264 GstClock *clock = gst_scheduler_get_clock (sched);
267 gst_clock_reset (clock);
269 GST_DEBUG (GST_CAT_CLOCK, "scheduler READY to PAUSED clock is %p (%s)", clock,
270 (clock ? GST_OBJECT_NAME (clock) : "nil"));
272 sched->current_clock = clock;
275 case GST_STATE_PAUSED_TO_PLAYING:
277 GstClock *clock = gst_scheduler_get_clock (sched);
279 GST_DEBUG (GST_CAT_CLOCK, "scheduler PAUSED to PLAYING clock is %p (%s)", clock,
280 (clock ? GST_OBJECT_NAME (clock) : "nil"));
282 sched->current_clock = clock;
284 gst_scheduler_set_clock (sched, sched->current_clock);
285 if (sched->current_clock) {
286 GST_DEBUG (GST_CAT_CLOCK, "enabling clock %p (%s)", sched->current_clock,
287 GST_OBJECT_NAME (sched->current_clock));
288 gst_clock_set_active (sched->current_clock, TRUE);
292 case GST_STATE_PLAYING_TO_PAUSED:
293 if (sched->current_clock) {
294 GST_DEBUG (GST_CAT_CLOCK, "disabling clock %p (%s)", sched->current_clock,
295 GST_OBJECT_NAME (sched->current_clock));
296 gst_clock_set_active (sched->current_clock, FALSE);
302 if (CLASS (sched)->state_transition)
303 return CLASS (sched)->state_transition (sched, element, transition);
305 return GST_STATE_SUCCESS;
309 * gst_scheduler_add_scheduler:
310 * @sched: a #GstScheduler to add to
311 * @sched2: the #GstScheduler to add
313 * Notifies the scheduler that it has to monitor this scheduler.
316 gst_scheduler_add_scheduler (GstScheduler *sched, GstScheduler *sched2)
318 g_return_if_fail (GST_IS_SCHEDULER (sched));
319 g_return_if_fail (GST_IS_SCHEDULER (sched2));
321 sched->schedulers = g_list_prepend (sched->schedulers, sched2);
322 sched2->parent_sched = sched;
324 if (CLASS (sched)->add_scheduler)
325 CLASS (sched)->add_scheduler (sched, sched2);
329 * gst_scheduler_remove_scheduler:
330 * @sched: the scheduler
331 * @sched2: the scheduler to remove
333 a Notifies the scheduler that it can stop monitoring this scheduler.
336 gst_scheduler_remove_scheduler (GstScheduler *sched, GstScheduler *sched2)
338 g_return_if_fail (GST_IS_SCHEDULER (sched));
339 g_return_if_fail (GST_IS_SCHEDULER (sched2));
341 sched->schedulers = g_list_remove (sched->schedulers, sched2);
342 sched2->parent_sched = NULL;
344 if (CLASS (sched)->remove_scheduler)
345 CLASS (sched)->remove_scheduler (sched, sched2);
349 * gst_scheduler_lock_element:
350 * @sched: the scheduler
351 * @element: the element to lock
353 * Acquire a lock on the given element in the given scheduler.
356 gst_scheduler_lock_element (GstScheduler *sched, GstElement *element)
358 g_return_if_fail (GST_IS_SCHEDULER (sched));
359 g_return_if_fail (GST_IS_ELEMENT (element));
361 if (CLASS (sched)->lock_element)
362 CLASS (sched)->lock_element (sched, element);
366 * gst_scheduler_unlock_element:
367 * @sched: the scheduler
368 * @element: the element to unlock
370 * Release the lock on the given element in the given scheduler.
373 gst_scheduler_unlock_element (GstScheduler *sched, GstElement *element)
375 g_return_if_fail (GST_IS_SCHEDULER (sched));
376 g_return_if_fail (GST_IS_ELEMENT (element));
378 if (CLASS (sched)->unlock_element)
379 CLASS (sched)->unlock_element (sched, element);
383 * gst_scheduler_error:
384 * @sched: the scheduler
385 * @element: the element with the error
387 * Tell the scheduler an element was in error
390 gst_scheduler_error (GstScheduler *sched, GstElement *element)
392 g_return_if_fail (GST_IS_SCHEDULER (sched));
393 g_return_if_fail (GST_IS_ELEMENT (element));
395 if (CLASS (sched)->error)
396 CLASS (sched)->error (sched, element);
400 * gst_scheduler_yield:
401 * @sched: the scheduler
402 * @element: the element requesting a yield
404 * Tell the scheduler to schedule another element.
407 gst_scheduler_yield (GstScheduler *sched, GstElement *element)
409 g_return_if_fail (GST_IS_SCHEDULER (sched));
410 g_return_if_fail (GST_IS_ELEMENT (element));
412 if (CLASS (sched)->yield)
413 CLASS (sched)->yield (sched, element);
417 * gst_scheduler_interrupt:
418 * @sched: the scheduler
419 * @element: the element requesting an interrupt
421 * Tell the scheduler to interrupt execution of this element.
423 * Returns: TRUE if the element should return NULL from the chain/get
427 gst_scheduler_interrupt (GstScheduler *sched, GstElement *element)
429 g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
430 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
432 if (CLASS (sched)->interrupt)
433 return CLASS (sched)->interrupt (sched, element);
439 * gst_scheduler_get_clock:
440 * @sched: the scheduler
442 * Get the current clock used by the scheduler
444 * Returns: a GstClock
447 gst_scheduler_get_clock (GstScheduler *sched)
449 GstClock *clock = NULL;
451 /* if we have a fixed clock, use that one */
452 if (GST_FLAG_IS_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK)) {
453 clock = sched->clock;
455 GST_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock,
456 (clock ? GST_OBJECT_NAME (clock) : "nil"));
459 GList *schedulers = sched->schedulers;
460 GList *providers = sched->clock_providers;
462 /* try to get a clock from one of the schedulers we manage first */
464 GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
466 clock = gst_scheduler_get_clock (scheduler);
470 schedulers = g_list_next (schedulers);
472 /* still no clock, try to find one in the providers */
473 while (!clock && providers) {
474 clock = gst_element_get_clock (GST_ELEMENT (providers->data));
476 providers = g_list_next (providers);
478 /* still no clock, use a system clock */
479 if (!clock && sched->parent_sched == NULL) {
480 clock = gst_system_clock_obtain ();
483 GST_DEBUG (GST_CAT_CLOCK, "scheduler selected clock %p (%s)", clock,
484 (clock ? GST_OBJECT_NAME (clock) : "nil"));
490 * gst_scheduler_use_clock:
491 * @sched: the scheduler
492 * @clock: the clock to use
494 * Force the scheduler to use the given clock. The scheduler will
495 * always use the given clock even if new clock providers are added
499 gst_scheduler_use_clock (GstScheduler *sched, GstClock *clock)
501 g_return_if_fail (sched != NULL);
502 g_return_if_fail (GST_IS_SCHEDULER (sched));
504 GST_FLAG_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
505 sched->clock = clock;
507 GST_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock,
508 (clock ? GST_OBJECT_NAME (clock) : "nil"));
512 * gst_scheduler_set_clock:
513 * @sched: the scheduler
514 * @clock: the clock to set
516 * Set the clock for the scheduler. The clock will be distributed
517 * to all the elements managed by the scheduler.
520 gst_scheduler_set_clock (GstScheduler *sched, GstClock *clock)
525 g_return_if_fail (sched != NULL);
526 g_return_if_fail (GST_IS_SCHEDULER (sched));
528 receivers = sched->clock_receivers;
529 schedulers = sched->schedulers;
531 sched->current_clock = clock;
534 GstElement *element = GST_ELEMENT (receivers->data);
536 GST_DEBUG (GST_CAT_CLOCK, "scheduler setting clock %p (%s) on element %s", clock,
537 (clock ? GST_OBJECT_NAME (clock) : "nil"), GST_ELEMENT_NAME (element));
538 gst_element_set_clock (element, clock);
539 receivers = g_list_next (receivers);
542 GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
544 GST_DEBUG (GST_CAT_CLOCK, "scheduler setting clock %p (%s) on scheduler %p", clock,
545 (clock ? GST_OBJECT_NAME (clock) : "nil"), scheduler);
546 gst_scheduler_set_clock (scheduler, clock);
547 schedulers = g_list_next (schedulers);
552 * gst_scheduler_auto_clock:
553 * @sched: the scheduler
555 * Let the scheduler select a clock automatically.
558 gst_scheduler_auto_clock (GstScheduler *sched)
560 g_return_if_fail (sched != NULL);
561 g_return_if_fail (GST_IS_SCHEDULER (sched));
563 GST_FLAG_UNSET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
566 GST_DEBUG (GST_CAT_CLOCK, "scheduler using automatic clock");
570 * gst_scheduler_clock_wait:
571 * @sched: the scheduler
572 * @element: the element that wants to wait
573 * @clock: the clock to use
574 * @time: the time to wait for
575 * @jitter: the time difference between requested time and actual time
577 * Wait till the clock reaches a specific time
579 * Returns: the status of the operation
582 gst_scheduler_clock_wait (GstScheduler *sched, GstElement *element, GstClock *clock, GstClockTime time,
583 GstClockTimeDiff *jitter)
585 g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_CLOCK_ERROR);
587 if (CLASS (sched)->clock_wait)
588 return CLASS (sched)->clock_wait (sched, element, clock, time, jitter);
590 return gst_clock_wait (clock, time, jitter);
592 return GST_CLOCK_TIMEOUT;
596 * gst_scheduler_iterate:
597 * @sched: the scheduler
599 * Perform one iteration on the scheduler.
601 * Returns: a boolean indicating something usefull has happened.
604 gst_scheduler_iterate (GstScheduler *sched)
606 g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
608 if (CLASS (sched)->iterate)
609 return CLASS (sched)->iterate (sched);
616 * gst_scheduler_show:
617 * @sched: the scheduler
619 * Dump the state of the scheduler
622 gst_scheduler_show (GstScheduler *sched)
624 g_return_if_fail (GST_IS_SCHEDULER (sched));
626 if (CLASS (sched)->show)
627 CLASS (sched)->show (sched);
631 * Factory stuff starts here
634 static void gst_scheduler_factory_class_init (GstSchedulerFactoryClass *klass);
635 static void gst_scheduler_factory_init (GstSchedulerFactory *factory);
637 static GstPluginFeatureClass *factory_parent_class = NULL;
638 /* static guint gst_scheduler_factory_signals[LAST_SIGNAL] = { 0 }; */
641 gst_scheduler_factory_get_type (void)
643 static GType schedulerfactory_type = 0;
645 if (!schedulerfactory_type) {
646 static const GTypeInfo schedulerfactory_info = {
647 sizeof (GstSchedulerFactoryClass),
650 (GClassInitFunc) gst_scheduler_factory_class_init,
653 sizeof(GstSchedulerFactory),
655 (GInstanceInitFunc) gst_scheduler_factory_init,
658 schedulerfactory_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE,
659 "GstSchedulerFactory", &schedulerfactory_info, 0);
661 return schedulerfactory_type;
665 gst_scheduler_factory_class_init (GstSchedulerFactoryClass *klass)
667 GObjectClass *gobject_class;
668 GstObjectClass *gstobject_class;
669 GstPluginFeatureClass *gstpluginfeature_class;
671 gobject_class = (GObjectClass*)klass;
672 gstobject_class = (GstObjectClass*)klass;
673 gstpluginfeature_class = (GstPluginFeatureClass*) klass;
675 factory_parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE);
678 _default_name = g_strdup ("basicomega");
682 gst_scheduler_factory_init (GstSchedulerFactory *factory)
688 * gst_scheduler_factory_new:
689 * @name: name of schedulerfactory to create
690 * @longdesc: long description of schedulerfactory to create
691 * @type: the gtk type of the GstScheduler element of this factory
693 * Create a new schedulerfactory with the given parameters
695 * Returns: a new #GstSchedulerFactory.
698 gst_scheduler_factory_new (const gchar *name, const gchar *longdesc, GType type)
700 GstSchedulerFactory *factory;
702 g_return_val_if_fail(name != NULL, NULL);
703 factory = gst_scheduler_factory_find (name);
705 factory = GST_SCHEDULER_FACTORY (g_object_new (GST_TYPE_SCHEDULER_FACTORY, NULL));
708 GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (name);
709 if (factory->longdesc)
710 g_free (factory->longdesc);
711 factory->longdesc = g_strdup (longdesc);
712 factory->type = type;
718 * gst_scheduler_factory_destroy:
719 * @factory: factory to destroy
721 * Removes the scheduler from the global list.
724 gst_scheduler_factory_destroy (GstSchedulerFactory *factory)
726 g_return_if_fail (factory != NULL);
728 /* we don't free the struct bacause someone might have a handle to it.. */
732 * gst_scheduler_factory_find:
733 * @name: name of schedulerfactory to find
735 * Search for an schedulerfactory of the given name.
737 * Returns: #GstSchedulerFactory if found, NULL otherwise
740 gst_scheduler_factory_find (const gchar *name)
742 GstPluginFeature *feature;
744 g_return_val_if_fail(name != NULL, NULL);
746 GST_DEBUG (0,"gstscheduler: find \"%s\"", name);
748 feature = gst_registry_pool_find_feature (name, GST_TYPE_SCHEDULER_FACTORY);
750 return GST_SCHEDULER_FACTORY (feature);
756 * gst_scheduler_factory_create:
757 * @factory: the factory used to create the instance
758 * @parent: the parent element of this scheduler
760 * Create a new #GstScheduler instance from the
761 * given schedulerfactory with the given parent. @parent will
762 * have its scheduler set to the returned #GstScheduler instance.
764 * Returns: A new #GstScheduler instance with a reference count of %1.
767 gst_scheduler_factory_create (GstSchedulerFactory *factory, GstElement *parent)
769 GstScheduler *new = NULL;
771 g_return_val_if_fail (factory != NULL, NULL);
772 g_return_val_if_fail (parent != NULL, NULL);
774 if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
775 g_return_val_if_fail (factory->type != 0, NULL);
777 new = GST_SCHEDULER (g_object_new (factory->type, NULL));
778 new->parent = parent;
780 GST_ELEMENT_SCHED (parent) = new;
782 /* let's refcount the scheduler */
783 gst_object_ref (GST_OBJECT (new));
784 gst_object_sink (GST_OBJECT (new));
791 * gst_scheduler_factory_make:
792 * @name: the name of the factory used to create the instance
793 * @parent: the parent element of this scheduler
795 * Create a new #GstScheduler instance from the
796 * schedulerfactory with the given name and parent. @parent will
797 * have its scheduler set to the returned #GstScheduler instance.
798 * If %NULL is passed as @name, the default scheduler name will
801 * Returns: A new #GstScheduler instance with a reference count of %1.
804 gst_scheduler_factory_make (const gchar *name, GstElement *parent)
806 GstSchedulerFactory *factory;
807 const gchar *default_name = gst_scheduler_factory_get_default_name ();
810 factory = gst_scheduler_factory_find (name);
813 /* FIXME: do better error handling */
814 if (default_name == NULL)
815 g_error ("No default scheduler name - do you have a registry ?");
816 factory = gst_scheduler_factory_find (default_name);
822 return gst_scheduler_factory_create (factory, parent);
826 * gst_scheduler_factory_set_default_name:
827 * @name: the name of the factory used as a default
829 * Set the default schedulerfactory name.
832 gst_scheduler_factory_set_default_name (const gchar* name)
835 g_free (_default_name);
837 _default_name = g_strdup (name);
841 * gst_scheduler_factory_get_default_name:
843 * Get the default schedulerfactory name.
845 * Returns: the name of the default scheduler.
848 gst_scheduler_factory_get_default_name (void)
850 return _default_name;