Merged from INCSCHED on 200505251!!!
[platform/upstream/gstreamer.git] / gst / gstelement.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstelement.c: The base element, all elements derive from this
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 //#define GST_DEBUG_ENABLED
24 #include "gst_private.h"
25
26 #include "gstelement.h"
27 #include "gstextratypes.h"
28 #include "gstbin.h"
29 #include "gstscheduler.h"
30 #include "gstutils.h"
31
32 /* Element signals and args */
33 enum {
34   STATE_CHANGE,
35   NEW_PAD,
36   PAD_REMOVED,
37   NEW_GHOST_PAD,
38   GHOST_PAD_REMOVED,
39   ERROR,
40   EOS,
41   LAST_SIGNAL
42 };
43
44 enum {
45   ARG_0,
46   /* FILL ME */
47 };
48
49
50 static void                     gst_element_class_init          (GstElementClass *klass);
51 static void                     gst_element_init                (GstElement *element);
52
53 static void                     gst_element_set_arg             (GtkObject *object, GtkArg *arg, guint id);
54 static void                     gst_element_get_arg             (GtkObject *object, GtkArg *arg, guint id);
55
56 static void                     gst_element_shutdown            (GtkObject *object);
57 static void                     gst_element_real_destroy        (GtkObject *object);
58
59 static GstElementStateReturn    gst_element_change_state        (GstElement *element);
60
61 static xmlNodePtr               gst_element_save_thyself        (GstObject *object, xmlNodePtr parent);
62
63 static GstObjectClass *parent_class = NULL;
64 static guint gst_element_signals[LAST_SIGNAL] = { 0 };
65
66 GtkType gst_element_get_type(void) {
67   static GtkType element_type = 0;
68
69   if (!element_type) {
70     static const GtkTypeInfo element_info = {
71       "GstElement",
72       sizeof(GstElement),
73       sizeof(GstElementClass),
74       (GtkClassInitFunc)gst_element_class_init,
75       (GtkObjectInitFunc)gst_element_init,
76       (GtkArgSetFunc)gst_element_set_arg,
77       (GtkArgGetFunc)gst_element_get_arg,
78       (GtkClassInitFunc)NULL,
79     };
80     element_type = gtk_type_unique(GST_TYPE_OBJECT,&element_info);
81   }
82   return element_type;
83 }
84
85 static void
86 gst_element_class_init (GstElementClass *klass)
87 {
88   GtkObjectClass *gtkobject_class;
89   GstObjectClass *gstobject_class;
90
91   gtkobject_class = (GtkObjectClass*) klass;
92   gstobject_class = (GstObjectClass*) klass;
93
94   parent_class = gtk_type_class(GST_TYPE_OBJECT);
95
96   gst_element_signals[STATE_CHANGE] =
97     gtk_signal_new ("state_change", GTK_RUN_LAST, gtkobject_class->type,
98                     GTK_SIGNAL_OFFSET (GstElementClass, state_change),
99                     gtk_marshal_NONE__INT, GTK_TYPE_NONE, 1,
100                     GTK_TYPE_INT);
101   gst_element_signals[NEW_PAD] =
102     gtk_signal_new ("new_pad", GTK_RUN_LAST, gtkobject_class->type,
103                     GTK_SIGNAL_OFFSET (GstElementClass, new_pad),
104                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
105                     GST_TYPE_PAD);
106   gst_element_signals[PAD_REMOVED] =
107     gtk_signal_new ("pad_removed", GTK_RUN_LAST, gtkobject_class->type,
108                     GTK_SIGNAL_OFFSET (GstElementClass, pad_removed),
109                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
110                     GST_TYPE_PAD);
111   gst_element_signals[NEW_GHOST_PAD] =
112     gtk_signal_new ("new_ghost_pad", GTK_RUN_LAST, gtkobject_class->type,
113                     GTK_SIGNAL_OFFSET (GstElementClass, new_ghost_pad),
114                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
115                     GST_TYPE_PAD);
116   gst_element_signals[GHOST_PAD_REMOVED] =
117     gtk_signal_new ("ghost_pad_removed", GTK_RUN_LAST, gtkobject_class->type,
118                     GTK_SIGNAL_OFFSET (GstElementClass, ghost_pad_removed),
119                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
120                     GST_TYPE_PAD);
121   gst_element_signals[ERROR] =
122     gtk_signal_new ("error", GTK_RUN_LAST, gtkobject_class->type,
123                     GTK_SIGNAL_OFFSET (GstElementClass, error),
124                     gtk_marshal_NONE__STRING, GTK_TYPE_NONE,1,
125                     GTK_TYPE_STRING);
126   gst_element_signals[EOS] =
127     gtk_signal_new ("eos", GTK_RUN_LAST, gtkobject_class->type,
128                     GTK_SIGNAL_OFFSET (GstElementClass,eos),
129                     gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
130
131
132   gtk_object_class_add_signals (gtkobject_class, gst_element_signals, LAST_SIGNAL);
133
134   gtkobject_class->set_arg =            GST_DEBUG_FUNCPTR(gst_element_set_arg);
135   gtkobject_class->get_arg =            GST_DEBUG_FUNCPTR(gst_element_get_arg);
136   gtkobject_class->shutdown =           GST_DEBUG_FUNCPTR(gst_element_shutdown);
137   gtkobject_class->destroy =            GST_DEBUG_FUNCPTR(gst_element_real_destroy);
138
139   gstobject_class->save_thyself =       GST_DEBUG_FUNCPTR(gst_element_save_thyself);
140   gstobject_class->restore_thyself =    GST_DEBUG_FUNCPTR(gst_element_restore_thyself);
141
142   klass->change_state =                 GST_DEBUG_FUNCPTR(gst_element_change_state);
143   klass->elementfactory = NULL;
144 }
145
146 static void
147 gst_element_init (GstElement *element)
148 {
149   element->current_state = GST_STATE_NULL;
150   element->pending_state = -1;
151   element->numpads = 0;
152   element->numsrcpads = 0;
153   element->numsinkpads = 0;
154   element->pads = NULL;
155   element->loopfunc = NULL;
156   element->threadstate = NULL;
157   element->sched = NULL;
158 }
159
160
161 static void
162 gst_element_set_arg (GtkObject *object, GtkArg *arg, guint id)
163 {
164   GstElementClass *oclass = GST_ELEMENT_CLASS (object->klass);
165
166   GST_SCHEDULE_LOCK_ELEMENT ( GST_ELEMENT_SCHED(object), GST_ELEMENT(object) );
167
168   if (oclass->set_arg)
169     (oclass->set_arg)(object,arg,id);
170
171   GST_SCHEDULE_UNLOCK_ELEMENT ( GST_ELEMENT_SCHED(object), GST_ELEMENT(object) );
172 }
173
174
175 static void
176 gst_element_get_arg (GtkObject *object, GtkArg *arg, guint id)
177 {
178   GstElementClass *oclass = GST_ELEMENT_CLASS (object->klass);
179
180   GST_SCHEDULE_LOCK_ELEMENT (GST_ELEMENT_SCHED(object), GST_ELEMENT(object) );
181
182   if (oclass->get_arg)
183     (oclass->get_arg)(object,arg,id);
184
185   GST_SCHEDULE_UNLOCK_ELEMENT (GST_ELEMENT_SCHED(object), GST_ELEMENT(object) );
186 }
187
188
189 /**
190  * gst_element_new:
191  *
192  * Create a new element.  Should never be used, as it does no good.
193  *
194  * Returns: new element
195  */
196 GstElement*
197 gst_element_new(void)
198 {
199   return GST_ELEMENT (gtk_type_new (GST_TYPE_ELEMENT));
200 }
201
202 /**
203  * gst_element_set_name:
204  * @element: GstElement to set name of
205  * @name: new name of element
206  *
207  * Set the name of the element, getting rid of the old name if there was
208  * one.
209  */
210 void
211 gst_element_set_name (GstElement *element, const gchar *name)
212 {
213   g_return_if_fail (element != NULL);
214   g_return_if_fail (GST_IS_ELEMENT (element));
215   g_return_if_fail (name != NULL);
216
217   gst_object_set_name (GST_OBJECT (element), name);
218 }
219
220 /**
221  * gst_element_get_name:
222  * @element: GstElement to get name of
223  *
224  * Get the name of the element.
225  *
226  * Returns: name of the element
227  */
228 const gchar*
229 gst_element_get_name (GstElement *element)
230 {
231   g_return_val_if_fail (element != NULL, NULL);
232   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
233
234   return GST_OBJECT_NAME (element);
235 }
236
237 /**
238  * gst_element_set_parent:
239  * @element: GstElement to set parent of
240  * @parent: new parent of the object
241  *
242  * Set the parent of the element.
243  */
244 void
245 gst_element_set_parent (GstElement *element, GstObject *parent)
246 {
247   g_return_if_fail (element != NULL);
248   g_return_if_fail (GST_IS_ELEMENT (element));
249   g_return_if_fail (GST_OBJECT_PARENT (element) == NULL);
250   g_return_if_fail (parent != NULL);
251   g_return_if_fail (GTK_IS_OBJECT (parent));
252   g_return_if_fail ((gpointer)element != (gpointer)parent);
253
254   gst_object_set_parent (GST_OBJECT (element), parent);
255 }
256
257 /**
258  * gst_element_get_parent:
259  * @element: GstElement to get the parent of
260  *
261  * Get the parent of the element.
262  *
263  * Returns: parent of the element
264  */
265 GstObject*
266 gst_element_get_parent (GstElement *element)
267 {
268   g_return_val_if_fail (element != NULL, NULL);
269   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
270
271   return GST_OBJECT_PARENT (element);
272 }
273
274 /**
275  * gst_element_add_pad:
276  * @element: element to add pad to
277  * @pad: pad to add
278  *
279  * Add a pad (connection point) to the element, setting the parent of the
280  * pad to the element (and thus adding a reference).
281  */
282 void
283 gst_element_add_pad (GstElement *element, GstPad *pad)
284 {
285   g_return_if_fail (element != NULL);
286   g_return_if_fail (GST_IS_ELEMENT (element));
287   g_return_if_fail (pad != NULL);
288   g_return_if_fail (GST_IS_PAD (pad));
289
290   // first check to make sure the pad's parent is already set
291   g_return_if_fail (GST_PAD_PARENT (pad) == NULL);
292
293   // then check to see if there's already a pad by that name here
294   g_return_if_fail (gst_object_check_uniqueness (element->pads, GST_PAD_NAME(pad)) == TRUE);
295
296   /* set the pad's parent */
297   GST_DEBUG (GST_CAT_ELEMENT_PADS,"setting parent of pad '%s' to '%s'\n",
298         GST_PAD_NAME (pad), GST_ELEMENT_NAME (element));
299   gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element));
300
301   /* add it to the list */
302   element->pads = g_list_append (element->pads, pad);
303   element->numpads++;
304   if (gst_pad_get_direction (pad) == GST_PAD_SRC)
305     element->numsrcpads++;
306   else
307     element->numsinkpads++;
308
309   /* emit the NEW_PAD signal */
310   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[NEW_PAD], pad);
311 }
312
313 /**
314  * gst_element_remove_pad:
315  * @element: element to remove pad from
316  * @pad: pad to remove
317  *
318  * Remove a pad (connection point) from the element, 
319  */
320 void
321 gst_element_remove_pad (GstElement *element, GstPad *pad)
322 {
323   g_return_if_fail (element != NULL);
324   g_return_if_fail (GST_IS_ELEMENT (element));
325   g_return_if_fail (pad != NULL);
326   g_return_if_fail (GST_IS_PAD (pad));
327
328   g_return_if_fail (GST_PAD_PARENT (pad) == element);
329
330   /* add it to the list */
331   element->pads = g_list_remove (element->pads, pad);
332   element->numpads--;
333   if (gst_pad_get_direction (pad) == GST_PAD_SRC)
334     element->numsrcpads--;
335   else
336     element->numsinkpads--;
337
338   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[PAD_REMOVED], pad);
339
340   gst_object_unparent (GST_OBJECT (pad));
341 }
342
343 /**
344  * gst_element_add_ghost_pad:
345  * @element: element to add ghost pad to
346  * @pad: pad from which the new ghost pad will be created
347  * @name: name of the new ghost pad
348  *
349  * Create a ghost pad from the given pad, and add it to the list of pads
350  * for this element.
351  */
352 void
353 gst_element_add_ghost_pad (GstElement *element, GstPad *pad, gchar *name)
354 {
355   GstPad *ghostpad;
356
357   g_return_if_fail (element != NULL);
358   g_return_if_fail (GST_IS_ELEMENT (element));
359   g_return_if_fail (pad != NULL);
360   g_return_if_fail (GST_IS_PAD (pad));
361
362   // then check to see if there's already a pad by that name here
363   g_return_if_fail (gst_object_check_uniqueness (element->pads, name) == TRUE);
364
365   GST_DEBUG(GST_CAT_ELEMENT_PADS,"creating new ghost pad called %s, from pad %s:%s\n",
366             name,GST_DEBUG_PAD_NAME(pad));
367   ghostpad = gst_ghost_pad_new (name, pad);
368
369   /* add it to the list */
370   GST_DEBUG(GST_CAT_ELEMENT_PADS,"adding ghost pad %s to element %s\n",
371             name, GST_ELEMENT_NAME (element));
372   element->pads = g_list_append (element->pads, ghostpad);
373   element->numpads++;
374   // set the parent of the ghostpad
375   gst_object_set_parent (GST_OBJECT (ghostpad), GST_OBJECT (element));
376
377   GST_DEBUG(GST_CAT_ELEMENT_PADS,"added ghostpad %s:%s\n",GST_DEBUG_PAD_NAME(ghostpad));
378
379   /* emit the NEW_GHOST_PAD signal */
380   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[NEW_GHOST_PAD], ghostpad);
381 }
382
383 /**
384  * gst_element_remove_ghost_pad:
385  * @element: element to remove the ghost pad from
386  * @pad: ghost pad to remove
387  *
388  * removes a ghost pad from an element
389  *
390  */
391 void
392 gst_element_remove_ghost_pad (GstElement *element, GstPad *pad)
393 {
394   g_return_if_fail (element != NULL);
395   g_return_if_fail (GST_IS_ELEMENT (element));
396   g_return_if_fail (pad != NULL);
397   g_return_if_fail (GST_IS_PAD (pad));
398
399   // FIXME this is redundant?
400 }
401
402
403 /**
404  * gst_element_get_pad:
405  * @element: element to find pad of
406  * @name: name of pad to retrieve
407  *
408  * Retrieve a pad from the element by name.
409  *
410  * Returns: requested pad if found, otherwise NULL.
411  */
412 GstPad*
413 gst_element_get_pad (GstElement *element, const gchar *name)
414 {
415   GList *walk;
416
417   g_return_val_if_fail (element != NULL, NULL);
418   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
419   g_return_val_if_fail (name != NULL, NULL);
420
421   // if there aren't any pads, well, we're not likely to find one
422   if (!element->numpads)
423     return NULL;
424
425   // look through the list, matching by name
426   walk = element->pads;
427   while (walk) {
428     GstPad *pad = GST_PAD(walk->data);
429     if (!strcmp (GST_PAD_NAME(pad), name)) {
430       GST_INFO(GST_CAT_ELEMENT_PADS,"found pad %s:%s",GST_DEBUG_PAD_NAME(pad));
431       return pad;
432     }
433     walk = g_list_next (walk);
434   }
435
436   GST_INFO(GST_CAT_ELEMENT_PADS,"no such pad '%s' in element \"%s\"",name,GST_ELEMENT_NAME(element));
437   return NULL;
438 }
439
440 /**
441  * gst_element_get_pad_list:
442  * @element: element to get pads of
443  *
444  * Retrieve a list of the pads associated with the element.
445  *
446  * Returns: GList of pads
447  */
448 GList*
449 gst_element_get_pad_list (GstElement *element)
450 {
451   g_return_val_if_fail (element != NULL, NULL);
452   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
453
454   /* return the list of pads */
455   return element->pads;
456 }
457
458 /**
459  * gst_element_get_padtemplate_list:
460  * @element: element to get padtemplates of
461  *
462  * Retrieve a list of the padtemplates associated with the element.
463  *
464  * Returns: GList of padtemplates
465  */
466 GList*
467 gst_element_get_padtemplate_list (GstElement *element)
468 {
469   GstElementClass *oclass;
470
471   g_return_val_if_fail (element != NULL, NULL);
472   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
473
474   oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
475
476   if (oclass->elementfactory == NULL) return NULL;
477
478   /* return the list of pads */
479   return oclass->elementfactory->padtemplates;
480 }
481
482 /**
483  * gst_element_get_padtemplate_by_name:
484  * @element: element to get padtemplate of
485  * @name: the name of the padtemplate to get.
486  *
487  * Retrieve a padtemplate from this element with the
488  * given name.
489  *
490  * Returns: the padtemplate with the given name
491  */
492 GstPadTemplate*
493 gst_element_get_padtemplate_by_name (GstElement *element, const guchar *name)
494 {
495   GList *padlist;
496
497   g_return_val_if_fail (element != NULL, NULL);
498   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
499   g_return_val_if_fail (name != NULL, NULL);
500
501   padlist = gst_element_get_padtemplate_list (element);
502
503   while (padlist) {
504     GstPadTemplate *padtempl = (GstPadTemplate*) padlist->data;
505
506     if (!strcmp (padtempl->name_template, name))
507       return padtempl;
508
509     padlist = g_list_next (padlist);
510   }
511
512   return NULL;
513 }
514
515 /**
516  * gst_element_get_padtemplate_by_compatible:
517  * @element: element to get padtemplate of
518  * @templ: a template to find a compatible template for
519  *
520  * Generate a padtemplate for this element compatible with the given
521  * template, ie able to link to it.
522  *
523  * Returns: the padtemplate
524  */
525 static GstPadTemplate*
526 gst_element_get_padtemplate_by_compatible (GstElement *element, GstPadTemplate *compattempl)
527 {
528   GstPadTemplate *newtempl = NULL;
529   GList *padlist;
530
531   GST_DEBUG(GST_CAT_ELEMENT_PADS,"gst_element_get_padtemplate_by_compatible()\n");
532
533   g_return_val_if_fail (element != NULL, NULL);
534   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
535   g_return_val_if_fail (compattempl != NULL, NULL);
536
537   padlist = gst_element_get_padtemplate_list (element);
538
539   while (padlist) {
540     GstPadTemplate *padtempl = (GstPadTemplate*) padlist->data;
541     gboolean compat = FALSE;
542
543     // Ignore name
544     // Ignore presence
545     // Check direction (must be opposite)
546     // Check caps
547
548     GST_DEBUG(GST_CAT_CAPS,"checking direction and caps\n");
549     if (padtempl->direction == GST_PAD_SRC &&
550       compattempl->direction == GST_PAD_SINK) {
551       GST_DEBUG(GST_CAT_CAPS,"compatible direction: found src pad template\n");
552       compat = gst_caps_check_compatibility(GST_PADTEMPLATE_CAPS (padtempl),
553                                             GST_PADTEMPLATE_CAPS (compattempl));
554       GST_DEBUG(GST_CAT_CAPS,"caps are %scompatible\n", (compat?"":"not "));
555     } else if (padtempl->direction == GST_PAD_SINK &&
556                compattempl->direction == GST_PAD_SRC) {
557       GST_DEBUG(GST_CAT_CAPS,"compatible direction: found sink pad template\n");
558       compat = gst_caps_check_compatibility(GST_PADTEMPLATE_CAPS (compattempl),
559                                             GST_PADTEMPLATE_CAPS (padtempl));
560       GST_DEBUG(GST_CAT_CAPS,"caps are %scompatible\n", (compat?"":"not "));
561     }
562
563     if (compat) {
564       newtempl = padtempl;
565       break;
566     }
567
568     padlist = g_list_next (padlist);
569   }
570
571   return newtempl;
572 }
573
574 static GstPad*
575 gst_element_request_pad (GstElement *element, GstPadTemplate *templ)
576 {
577   GstPad *newpad = NULL;
578   GstElementClass *oclass;
579
580   oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
581   if (oclass->request_new_pad)
582     newpad = (oclass->request_new_pad)(element, templ);
583
584   return newpad;
585 }
586
587 /**
588  * gst_element_request_compatible_pad:
589  * @element: element to request a new pad from
590  * @templ: a pad template to which the new pad should be able to connect
591  *
592  * Request a new pad from the element. The template will
593  * be used to decide what type of pad to create. This function
594  * is typically used for elements with a padtemplate with presence
595  * GST_PAD_REQUEST.
596  *
597  * Returns: the new pad that was created.
598  */
599 GstPad*
600 gst_element_request_compatible_pad (GstElement *element, GstPadTemplate *templ)
601 {
602   GstPadTemplate *templ_new;
603   GstPad *pad = NULL;
604
605   g_return_val_if_fail (element != NULL, NULL);
606   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
607   g_return_val_if_fail (templ != NULL, NULL);
608
609   templ_new = gst_element_get_padtemplate_by_compatible (element, templ);
610   if (templ_new != NULL)
611       pad = gst_element_request_pad (element, templ_new);
612
613   return pad;
614 }
615
616 /**
617  * gst_element_request_pad_by_name:
618  * @element: element to request a new pad from
619  * @name: the name of the padtemplate to use.
620  *
621  * Request a new pad from the element. The name argument will
622  * be used to decide what padtemplate to use. This function
623  * is typically used for elements with a padtemplate with presence
624  * GST_PAD_REQUEST.
625  *
626  * Returns: the new pad that was created.
627  */
628 GstPad*
629 gst_element_request_pad_by_name (GstElement *element, const gchar *name)
630 {
631   GstPadTemplate *templ;
632   GstPad *pad;
633
634   g_return_val_if_fail (element != NULL, NULL);
635   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
636   g_return_val_if_fail (name != NULL, NULL);
637
638   templ = gst_element_get_padtemplate_by_name (element, name);
639   g_return_val_if_fail (templ != NULL, NULL);
640
641   pad = gst_element_request_pad (element, templ);
642
643   return pad;
644 }
645
646 /**
647  * gst_element_connect:
648  * @src: element containing source pad
649  * @srcpadname: name of pad in source element
650  * @dest: element containing destination pad
651  * @destpadname: name of pad in destination element
652  *
653  * Connect the two named pads of the source and destination elements.
654  * Side effect is that if one of the pads has no parent, it becomes a
655  * child of the parent of the other element.  If they have different
656  * parents, the connection fails.
657  */
658 void
659 gst_element_connect (GstElement *src, const gchar *srcpadname,
660                      GstElement *dest, const gchar *destpadname)
661 {
662   GstPad *srcpad,*destpad;
663   GstObject *srcparent,*destparent;
664
665   g_return_if_fail (src != NULL);
666   g_return_if_fail (GST_IS_ELEMENT(src));
667   g_return_if_fail (srcpadname != NULL);
668   g_return_if_fail (dest != NULL);
669   g_return_if_fail (GST_IS_ELEMENT(dest));
670   g_return_if_fail (destpadname != NULL);
671
672   /* obtain the pads requested */
673   srcpad = gst_element_get_pad (src, srcpadname);
674   if (srcpad == NULL) {
675     GST_ERROR(src,"source element has no pad \"%s\"",srcpadname);
676     return;
677   }
678   destpad = gst_element_get_pad (dest, destpadname);
679   if (srcpad == NULL) {
680     GST_ERROR(dest,"destination element has no pad \"%s\"",destpadname);
681     return;
682   }
683
684   /* find the parent elements of each element */
685   srcparent = gst_object_get_parent (GST_OBJECT (src));
686   destparent = gst_object_get_parent (GST_OBJECT (dest));
687
688   /* have to make sure that they have the same parents... */
689   /*
690   if (srcparent != destparent) {
691     GST_ERROR_OBJECT(srcparent,destparent,"%s and %s have different parents",
692                  GST_ELEMENT_NAME (src),GST_ELEMENT_NAME (dest));
693     return;
694   }
695   */
696
697   /* we're satisified they can be connected, let's do it */
698   gst_pad_connect(srcpad,destpad);
699 }
700
701 /**
702  * gst_element_disconnect:
703  * @src: element containing source pad
704  * @srcpadname: name of pad in source element
705  * @dest: element containing destination pad
706  * @destpadname: name of pad in destination element
707  *
708  * Disconnect the two named pads of the source and destination elements.
709  */
710 void
711 gst_element_disconnect (GstElement *src, const gchar *srcpadname,
712                         GstElement *dest, const gchar *destpadname)
713 {
714   GstPad *srcpad,*destpad;
715
716   g_return_if_fail (src != NULL);
717   g_return_if_fail (GST_IS_ELEMENT(src));
718   g_return_if_fail (srcpadname != NULL);
719   g_return_if_fail (dest != NULL);
720   g_return_if_fail (GST_IS_ELEMENT(dest));
721   g_return_if_fail (destpadname != NULL);
722
723   /* obtain the pads requested */
724   srcpad = gst_element_get_pad (src, srcpadname);
725   if (srcpad == NULL) {
726     GST_ERROR(src,"source element has no pad \"%s\"",srcpadname);
727     return;
728   }
729   destpad = gst_element_get_pad (dest, destpadname);
730   if (srcpad == NULL) {
731     GST_ERROR(dest,"destination element has no pad \"%s\"",destpadname);
732     return;
733   }
734
735   /* we're satisified they can be disconnected, let's do it */
736   gst_pad_disconnect(srcpad,destpad);
737 }
738
739 /**
740  * gst_element_error:
741  * @element: Element with the error
742  * @error: String describing the error
743  *
744  * This function is used internally by elements to signal an error
745  * condition.  It results in the "error" signal.
746  */
747 void
748 gst_element_error (GstElement *element, const gchar *error)
749 {
750   g_error("GstElement: error in element '%s': %s\n", GST_ELEMENT_NAME(element), error);
751
752   /* FIXME: this is not finished!!! */
753
754   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[ERROR], error);
755 }
756
757
758 /**
759  * gst_element_set_state:
760  * @element: element to change state of
761  * @state: new element state
762  *
763  * Sets the state of the element. This function will only set
764  * the elements pending state.
765  *
766  * Returns: whether or not the state was successfully set.
767  */
768 gint
769 gst_element_set_state (GstElement *element, GstElementState state)
770 {
771   GstElementClass *oclass;
772   GstElementState curpending;
773   GstElementStateReturn return_val = GST_STATE_SUCCESS;
774
775 //  g_print("gst_element_set_state(\"%s\",%08lx)\n",
776 //          element->name,state);
777
778   g_return_val_if_fail (element != NULL, GST_STATE_FAILURE);
779   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
780   g_return_val_if_fail (element->sched != NULL, GST_STATE_FAILURE);
781
782   GST_DEBUG_ELEMENT (GST_CAT_STATES,element, "setting state from %s to %s\n",
783                      gst_element_statename(GST_STATE(element)),
784                      gst_element_statename(state));
785
786   /* start with the current state */
787   curpending = GST_STATE(element);
788
789   /* loop until the final requested state is set */
790   while (GST_STATE(element) != state) {
791     /* move the curpending state in the correct direction */
792     if (curpending < state) curpending<<=1;
793     else curpending>>=1;
794
795     /* set the pending state variable */
796     // FIXME: should probably check to see that we don't already have one
797     GST_STATE_PENDING (element) = curpending;
798     if (curpending != state)
799       GST_DEBUG_ELEMENT (GST_CAT_STATES,element,"intermediate: setting state to %s\n",
800                          gst_element_statename(curpending));
801
802     /* call the state change function so it can set the state */
803     oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
804     if (oclass->change_state)
805       return_val = (oclass->change_state)(element);
806
807     /* if that outright didn't work, we need to bail right away */
808     /* NOTE: this will bail on ASYNC as well! */
809     if (return_val == GST_STATE_FAILURE) {
810       GST_DEBUG_ELEMENT (GST_CAT_STATES,element,"have failed change_state return\n");
811       return return_val;
812     }
813   }
814
815   /* this is redundant, really, it will always return SUCCESS */
816   return return_val;
817 }
818
819 /**
820  * gst_element_get_factory:
821  * @element: element to request the factory
822  *
823  * Retrieves the factory that was used to create this element
824  *
825  * Returns: the factory used for creating this element
826  */
827 GstElementFactory*
828 gst_element_get_factory (GstElement *element)
829 {
830   GstElementClass *oclass;
831
832   g_return_val_if_fail (element != NULL, NULL);
833   g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
834
835   oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
836
837   return oclass->elementfactory;
838 }
839
840
841 /**
842  * gst_element_change_state:
843  * @element: element to change state of
844  *
845  * Changes the state of the element, but more importantly fires off a signal
846  * indicating the new state.
847  * The element will have no pending states anymore.
848  *
849  * Returns: whether or not the state change was successfully set.
850  */
851 GstElementStateReturn
852 gst_element_change_state (GstElement *element)
853 {
854   g_return_val_if_fail (element != NULL, GST_STATE_FAILURE);
855   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
856
857 //  GST_DEBUG_ELEMENT (GST_CAT_STATES, element, "default handler sets state to %s\n",
858 //                     gst_element_statename(GST_STATE_PENDING(element)));
859
860   if (GST_STATE_TRANSITION(element) == GST_STATE_PAUSED_TO_PLAYING) {
861     g_return_val_if_fail(GST_ELEMENT_SCHED(element), GST_STATE_FAILURE);
862     if (GST_ELEMENT_PARENT(element))
863       fprintf(stderr,"PAUSED->PLAYING: element \"%s\" has parent \"%s\" and sched %p\n",
864 GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(GST_ELEMENT_PARENT(element)),GST_ELEMENT_SCHED(element));
865     GST_SCHEDULE_ENABLE_ELEMENT (element->sched,element);
866   }
867   else if (GST_STATE_TRANSITION(element) == GST_STATE_PLAYING_TO_PAUSED) {
868     if (GST_ELEMENT_PARENT(element))
869       fprintf(stderr,"PLAYING->PAUSED: element \"%s\" has parent \"%s\" and sched %p\n",
870 GST_ELEMENT_NAME(element),GST_ELEMENT_NAME(GST_ELEMENT_PARENT(element)),GST_ELEMENT_SCHED(element));
871     GST_SCHEDULE_DISABLE_ELEMENT (element->sched,element);
872   }
873
874   GST_STATE (element) = GST_STATE_PENDING (element);
875   GST_STATE_PENDING (element) = GST_STATE_NONE_PENDING;
876
877   // note: queues' state_change is a special case because it needs to lock
878   // for synchronization (from another thread).  since this signal may block
879   // or (worse) make another state change, the queue needs to unlock before
880   // calling.  thus, gstqueue.c::gst_queue_state_change() blocks, unblocks,
881   // unlocks, then emits this. 
882   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[STATE_CHANGE],
883                    GST_STATE (element));
884   return GST_STATE_SUCCESS;
885 }
886
887 static void
888 gst_element_shutdown (GtkObject *object)
889 {
890   GstElement *element = GST_ELEMENT (object);
891
892   GST_DEBUG_ELEMENT (GST_CAT_REFCOUNTING, element, "shutdown\n");
893
894   if (GST_IS_BIN (GST_OBJECT_PARENT (element)))
895     gst_bin_remove (GST_BIN (GST_OBJECT_PARENT (element)), element);
896
897   if (GTK_OBJECT_CLASS (parent_class)->shutdown)
898     GTK_OBJECT_CLASS (parent_class)->shutdown (object);
899 }
900
901 static void
902 gst_element_real_destroy (GtkObject *object)
903 {
904   GstElement *element = GST_ELEMENT (object);
905   GList *pads;
906   GstPad *pad;
907
908   GST_DEBUG_ELEMENT (GST_CAT_REFCOUNTING, element, "destroy\n");
909
910   if (element->pads) {
911     GList *orig;
912     orig = pads = g_list_copy (element->pads);
913     while (pads) {
914       pad = GST_PAD (pads->data);
915       //gst_object_destroy (GST_OBJECT (pad));
916       gst_object_ref (GST_OBJECT (pad));
917       gst_element_remove_pad (element, pad);
918       gst_object_unref (GST_OBJECT (pad));
919       pads = g_list_next (pads);
920     }
921     g_list_free (orig);
922     g_list_free (element->pads);
923     element->pads = NULL;
924   }
925
926   element->numsrcpads = 0;
927   element->numsinkpads = 0;
928
929   if (GTK_OBJECT_CLASS (parent_class)->destroy)
930     GTK_OBJECT_CLASS (parent_class)->destroy (object);
931 }
932
933 /*
934 static gchar *_gst_element_type_names[] = {
935   "invalid",
936   "none",
937   "char",
938   "uchar",
939   "bool",
940   "int",
941   "uint",
942   "long",
943   "ulong",
944   "float",
945   "double",
946   "string",
947 };
948 */
949
950 /**
951  * gst_element_save_thyself:
952  * @element: GstElement to save
953  * @parent: the xml parent node
954  *
955  * Saves the element as part of the given XML structure
956  *
957  * Returns: the new xml node
958  */
959 static xmlNodePtr
960 gst_element_save_thyself (GstObject *object,
961                           xmlNodePtr parent)
962 {
963   GList *pads;
964   GstElementClass *oclass;
965   GtkType type;
966   GstElement *element;
967
968   g_return_val_if_fail (object != NULL, parent);
969   g_return_val_if_fail (GST_IS_ELEMENT (object), parent);
970   g_return_val_if_fail (parent != NULL, parent);
971
972   element = GST_ELEMENT (object);
973
974   oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
975
976   xmlNewChild(parent, NULL, "name", GST_ELEMENT_NAME(element));
977
978   if (oclass->elementfactory != NULL) {
979     GstElementFactory *factory = (GstElementFactory *)oclass->elementfactory;
980
981     xmlNewChild (parent, NULL, "type", factory->name);
982     xmlNewChild (parent, NULL, "version", factory->details->version);
983   }
984
985 //  if (element->manager)
986 //    xmlNewChild(parent, NULL, "manager", GST_ELEMENT_NAME(element->manager));
987
988   // output all args to the element
989   type = GTK_OBJECT_TYPE (element);
990   while (type != GTK_TYPE_INVALID) {
991     GtkArg *args;
992     guint32 *flags;
993     guint num_args,i;
994
995     args = gtk_object_query_args (type, &flags, &num_args);
996
997     for (i=0; i<num_args; i++) {
998       if ((args[i].type > GTK_TYPE_NONE) &&
999           (flags[i] & GTK_ARG_READABLE)) {
1000         xmlNodePtr arg;
1001         gtk_object_getv (GTK_OBJECT (element), 1, &args[i]);
1002         arg = xmlNewChild (parent, NULL, "arg", NULL);
1003         xmlNewChild (arg, NULL, "name", args[i].name);
1004         switch (args[i].type) {
1005           case GTK_TYPE_CHAR:
1006             xmlNewChild (arg, NULL, "value",
1007                          g_strdup_printf ("%c", GTK_VALUE_CHAR (args[i])));
1008             break;
1009           case GTK_TYPE_UCHAR:
1010             xmlNewChild (arg, NULL, "value",
1011                          g_strdup_printf ("%d", GTK_VALUE_UCHAR (args[i])));
1012             break;
1013           case GTK_TYPE_BOOL:
1014             xmlNewChild (arg, NULL, "value",
1015                         GTK_VALUE_BOOL (args[i]) ? "true" : "false");
1016             break;
1017           case GTK_TYPE_INT:
1018             xmlNewChild (arg, NULL, "value",
1019                          g_strdup_printf ("%d", GTK_VALUE_INT (args[i])));
1020             break;
1021           case GTK_TYPE_LONG:
1022             xmlNewChild (arg, NULL, "value",
1023                          g_strdup_printf ("%ld", GTK_VALUE_LONG (args[i])));
1024             break;
1025           case GTK_TYPE_ULONG:
1026             xmlNewChild (arg, NULL, "value",
1027                          g_strdup_printf ("%lu", GTK_VALUE_ULONG (args[i])));
1028             break;
1029           case GTK_TYPE_FLOAT:
1030             xmlNewChild (arg, NULL, "value",
1031                          g_strdup_printf ("%f", GTK_VALUE_FLOAT (args[i])));
1032             break;
1033           case GTK_TYPE_DOUBLE:
1034             xmlNewChild (arg, NULL, "value",
1035                          g_strdup_printf ("%g", GTK_VALUE_DOUBLE (args[i])));
1036             break;
1037           case GTK_TYPE_STRING:
1038             xmlNewChild (arg, NULL, "value", GTK_VALUE_STRING (args[i]));
1039             break;
1040           default:
1041             if (args[i].type == GST_TYPE_FILENAME) {
1042               xmlNewChild (arg, NULL, "value", GTK_VALUE_STRING (args[i]));
1043             }
1044             break;
1045         }
1046       }
1047     }
1048     type = gtk_type_parent (type);
1049   }
1050
1051   pads = GST_ELEMENT_PADS (element);
1052
1053   while (pads) {
1054     GstPad *pad = GST_PAD (pads->data);
1055     // figure out if it's a direct pad or a ghostpad
1056     if (GST_ELEMENT (GST_OBJECT_PARENT (pad)) == element) {
1057       xmlNodePtr padtag = xmlNewChild (parent, NULL, "pad", NULL);
1058       gst_object_save_thyself (GST_OBJECT (pad), padtag);
1059     }
1060     pads = g_list_next (pads);
1061   }
1062
1063   return parent;
1064 }
1065
1066 /**
1067  * gst_element_restore_thyself:
1068  * @self: the xml node
1069  * @parent: the parent of this object when it's loaded
1070  *
1071  * Load the element from the XML description
1072  *
1073  * Returns: the new element
1074  */
1075 GstElement*
1076 gst_element_restore_thyself (xmlNodePtr self, GstObject *parent)
1077 {
1078   xmlNodePtr children = self->xmlChildrenNode;
1079   GstElement *element;
1080   GstObjectClass *oclass;
1081   guchar *name = NULL;
1082   guchar *value = NULL;
1083   guchar *type = NULL;
1084
1085   // first get the needed tags to construct the element
1086   while (children) {
1087     if (!strcmp (children->name, "name")) {
1088       name = g_strdup (xmlNodeGetContent (children));
1089     } else if (!strcmp (children->name, "type")) {
1090       type = g_strdup (xmlNodeGetContent (children));
1091     }
1092     children = children->next;
1093   }
1094   g_return_val_if_fail (name != NULL, NULL);
1095   g_return_val_if_fail (type != NULL, NULL);
1096
1097   GST_INFO (GST_CAT_XML,"loading \"%s\" of type \"%s\"", name, type);
1098
1099   element = gst_elementfactory_make (type, name);
1100
1101   g_return_val_if_fail (element != NULL, NULL);
1102
1103   // ne need to set the parent on this object bacause the pads
1104   // will go through the hierarchy to connect to thier peers
1105   if (parent)
1106     gst_object_set_parent (GST_OBJECT (element), parent);
1107
1108   // we have the element now, set the arguments
1109   children = self->xmlChildrenNode;
1110
1111   while (children) {
1112     if (!strcmp (children->name, "arg")) {
1113       xmlNodePtr child = children->xmlChildrenNode;
1114
1115       while (child) {
1116         if (!strcmp (child->name, "name")) {
1117           name = g_strdup (xmlNodeGetContent (child));
1118         }
1119         else if (!strcmp (child->name, "value")) {
1120           value = g_strdup (xmlNodeGetContent (child));
1121         }
1122         child = child->next;
1123       }
1124       gst_util_set_object_arg (GTK_OBJECT (element), name, value);
1125     }
1126     children = children->next;
1127   }
1128   // we have the element now, set the pads
1129   children = self->xmlChildrenNode;
1130
1131   while (children) {
1132     if (!strcmp (children->name, "pad")) {
1133       gst_pad_load_and_connect (children, GST_OBJECT (element));
1134     }
1135     children = children->next;
1136   }
1137
1138   oclass = GST_OBJECT_CLASS (GTK_OBJECT (element)->klass);
1139   if (oclass->restore_thyself)
1140     (oclass->restore_thyself) (GST_OBJECT (element), self);
1141
1142   if (parent)
1143     gst_object_unparent (GST_OBJECT (element));
1144
1145   gst_class_signal_emit_by_name (GST_OBJECT (element), "object_loaded", self);
1146
1147   return element;
1148 }
1149
1150 /**
1151  * gst_element_set_sched:
1152  * @element: Element to set manager of.
1153  * @sched: @GstSchedule to set.
1154  *
1155  * Sets the scheduler of the element.  For internal use only, unless you're
1156  * writing a new bin subclass.
1157  */
1158 void
1159 gst_element_set_sched (GstElement *element,
1160                          GstSchedule *sched)
1161 {
1162   GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting scheduler to %p",sched);
1163   element->sched = sched;
1164 }
1165
1166 /**
1167  * gst_element_get_sched:
1168  * @element: Element to get manager of.
1169  *
1170  * Returns the scheduler of the element.
1171  *
1172  * Returns: Element's scheduler
1173  */
1174 GstSchedule*
1175 gst_element_get_sched (GstElement *element)
1176 {
1177   return element->sched;
1178 }
1179
1180 /**
1181  * gst_element_set_loop_function:
1182  * @element: Element to set loop function of.
1183  * @loop: Pointer to loop function.
1184  *
1185  * This sets the loop function for the element.  The function pointed to
1186  * can deviate from the GstElementLoopFunction definition in type of
1187  * pointer only.
1188  *
1189  * NOTE: in order for this to take effect, the current loop function *must*
1190  * exit.  Assuming the loop function itself is the only one who will cause
1191  * a new loopfunc to be assigned, this should be no problem.
1192  */
1193 void
1194 gst_element_set_loop_function(GstElement *element,
1195                               GstElementLoopFunction loop)
1196 {
1197   /* set the loop function */
1198   element->loopfunc = loop;
1199
1200   /* set the NEW_LOOPFUNC flag so everyone knows to go try again */
1201   GST_FLAG_SET(element,GST_ELEMENT_NEW_LOOPFUNC);
1202 }
1203
1204 /**
1205  * gst_element_signal_eos:
1206  * @element: element to trigger the eos signal of
1207  *
1208  * Throws the eos signal to indicate that the end of the stream is reached.
1209  */
1210 void
1211 gst_element_signal_eos (GstElement *element)
1212 {
1213   g_return_if_fail (element != NULL);
1214   g_return_if_fail (GST_IS_ELEMENT (element));
1215
1216   gtk_signal_emit (GTK_OBJECT (element), gst_element_signals[EOS]);
1217   GST_FLAG_SET(element,GST_ELEMENT_COTHREAD_STOPPING);
1218 }
1219
1220
1221 const gchar *gst_element_statename(int state) {
1222   switch (state) {
1223 #ifdef GST_DEBUG_COLOR
1224     case GST_STATE_NONE_PENDING: return "NONE_PENDING";break;
1225     case GST_STATE_NULL: return "\033[01;37mNULL\033[00m";break;
1226     case GST_STATE_READY: return "\033[01;31mREADY\033[00m";break;
1227     case GST_STATE_PLAYING: return "\033[01;32mPLAYING\033[00m";break;
1228     case GST_STATE_PAUSED: return "\033[01;33mPAUSED\033[00m";break;
1229     default: return "\033[01;37;41mUNKNOWN!\033[00m";
1230 #else
1231     case GST_STATE_NONE_PENDING: return "NONE_PENDING";break;
1232     case GST_STATE_NULL: return "NULL";break;
1233     case GST_STATE_READY: return "READY";break;
1234     case GST_STATE_PLAYING: return "PLAYING";break;
1235     case GST_STATE_PAUSED: return "PAUSED";break;
1236     default: return "UNKNOWN!";
1237 #endif
1238   }
1239   return "";
1240 }