c8021800bb1e9e33991c4aa54fcc75455f74171b
[platform/upstream/gstreamer.git] / gst / gstghostpad.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 Andy Wingo <wingo@pobox.com>
5  *
6  * gstghostpad.c: Proxy pads
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24
25 #include "gst_private.h"
26
27 #include "gstghostpad.h"
28 #include "gstelement.h"
29 #include "gstbin.h"
30
31
32 #define GST_TYPE_PROXY_PAD              (gst_proxy_pad_get_type ())
33 #define GST_IS_PROXY_PAD(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PROXY_PAD))
34 #define GST_IS_PROXY_PAD_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PROXY_PAD))
35 #define GST_PROXY_PAD(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad))
36 #define GST_PROXY_PAD_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass))
37 #define GST_PROXY_PAD_TARGET(pad)       (GST_PROXY_PAD (pad)->target)
38
39
40 typedef struct _GstProxyPad GstProxyPad;
41 typedef struct _GstProxyPadClass GstProxyPadClass;
42
43
44 enum
45 {
46   PROXY_PROP_0,
47   PROXY_PROP_TARGET
48 };
49
50 struct _GstProxyPad
51 {
52   GstPad pad;
53
54   GstPad *target;
55
56   GMutex *property_lock;
57
58   /*< private > */
59   gpointer _gst_reserved[1];
60 };
61
62 struct _GstProxyPadClass
63 {
64   GstPadClass parent_class;
65
66   /*< private > */
67   gpointer _gst_reserved[1];
68 };
69
70
71 G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD);
72
73
74 static void gst_proxy_pad_dispose (GObject * object);
75 static void gst_proxy_pad_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77 static void gst_proxy_pad_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79 static void gst_proxy_pad_finalize (GObject * object);
80
81 #ifndef GST_DISABLE_LOADSAVE
82 static xmlNodePtr gst_proxy_pad_save_thyself (GstObject * object,
83     xmlNodePtr parent);
84 #endif
85
86
87 static void
88 gst_proxy_pad_class_init (GstProxyPadClass * klass)
89 {
90   GObjectClass *gobject_class = (GObjectClass *) klass;
91
92   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_proxy_pad_dispose);
93   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_proxy_pad_finalize);
94   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_proxy_pad_set_property);
95   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_proxy_pad_get_property);
96
97   g_object_class_install_property (G_OBJECT_CLASS (klass), PROXY_PROP_TARGET,
98       g_param_spec_object ("target", "Target", "The proxy pad's target",
99           GST_TYPE_PAD, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
100
101 #ifndef GST_DISABLE_LOADSAVE
102   {
103     GstObjectClass *gstobject_class = (GstObjectClass *) klass;
104
105     gstobject_class->save_thyself =
106         GST_DEBUG_FUNCPTR (gst_proxy_pad_save_thyself);
107   }
108 #endif
109 }
110
111 const GstQueryType *
112 gst_proxy_pad_do_query_type (GstPad * pad)
113 {
114   GstPad *target = GST_PROXY_PAD_TARGET (pad);
115
116   g_return_val_if_fail (target != NULL, NULL);
117
118   return gst_pad_get_query_types (target);
119 }
120
121 static gboolean
122 gst_proxy_pad_do_event (GstPad * pad, GstEvent * event)
123 {
124   GstPad *target = GST_PROXY_PAD_TARGET (pad);
125
126   g_return_val_if_fail (target != NULL, FALSE);
127
128   return gst_pad_send_event (target, event);
129 }
130
131 static gboolean
132 gst_proxy_pad_do_query (GstPad * pad, GstQuery * query)
133 {
134   GstPad *target = GST_PROXY_PAD_TARGET (pad);
135
136   g_return_val_if_fail (target != NULL, FALSE);
137
138   return gst_pad_query (target, query);
139 }
140
141 static GList *
142 gst_proxy_pad_do_internal_link (GstPad * pad)
143 {
144   GstPad *target = GST_PROXY_PAD_TARGET (pad);
145
146   g_return_val_if_fail (target != NULL, NULL);
147
148   return gst_pad_get_internal_links (target);
149 }
150
151 static GstFlowReturn
152 gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size,
153     GstCaps * caps, GstBuffer ** buf)
154 {
155   GstPad *target = GST_PROXY_PAD_TARGET (pad);
156
157   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
158
159   return target->bufferallocfunc (target, offset, size, caps, buf);
160 }
161
162 static gboolean
163 gst_proxy_pad_do_activate (GstPad * pad)
164 {
165   GstPad *target = GST_PROXY_PAD_TARGET (pad);
166
167   g_return_val_if_fail (target != NULL, FALSE);
168
169   return gst_pad_activate_push (pad, TRUE);
170 }
171
172 static gboolean
173 gst_proxy_pad_do_activatepull (GstPad * pad, gboolean active)
174 {
175   GstActivateMode old;
176   GstPad *target = GST_PROXY_PAD_TARGET (pad);
177
178   g_return_val_if_fail (target != NULL, FALSE);
179
180   GST_LOCK (target);
181   old = GST_PAD_ACTIVATE_MODE (target);
182   GST_UNLOCK (target);
183
184   if ((active && old == GST_ACTIVATE_PULL)
185       || (!active && old == GST_ACTIVATE_NONE))
186     return TRUE;
187   else
188     return gst_pad_activate_pull (target, active);
189 }
190
191 static gboolean
192 gst_proxy_pad_do_activatepush (GstPad * pad, gboolean active)
193 {
194   GstActivateMode old;
195   GstPad *target = GST_PROXY_PAD_TARGET (pad);
196
197   g_return_val_if_fail (target != NULL, FALSE);
198
199   GST_LOCK (target);
200   old = GST_PAD_ACTIVATE_MODE (target);
201   GST_UNLOCK (target);
202
203   if ((active && old == GST_ACTIVATE_PUSH)
204       || (!active && old == GST_ACTIVATE_NONE))
205     return TRUE;
206   else
207     return gst_pad_activate_push (target, active);
208 }
209
210 static void
211 gst_proxy_pad_do_loop (GstPad * pad)
212 {
213   GstPad *target = GST_PROXY_PAD_TARGET (pad);
214
215   g_return_if_fail (target != NULL);
216
217   target->loopfunc (target);
218 }
219
220 static GstFlowReturn
221 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
222 {
223   GstPad *target = GST_PROXY_PAD_TARGET (pad);
224
225   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
226
227   return gst_pad_chain (target, buffer);
228 }
229
230 static GstFlowReturn
231 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
232     GstBuffer ** buffer)
233 {
234   GstPad *target = GST_PROXY_PAD_TARGET (pad);
235
236   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
237
238   return target->getrangefunc (target, offset, size, buffer);
239 }
240
241 static gboolean
242 gst_proxy_pad_do_checkgetrange (GstPad * pad)
243 {
244   GstPad *target = GST_PROXY_PAD_TARGET (pad);
245
246   g_return_val_if_fail (target != NULL, FALSE);
247
248   return target->checkgetrangefunc (target);
249 }
250
251 static GstCaps *
252 gst_proxy_pad_do_getcaps (GstPad * pad)
253 {
254   GstPad *target = GST_PROXY_PAD_TARGET (pad);
255
256   g_return_val_if_fail (target != NULL, NULL);
257
258   return target->getcapsfunc (target);
259 }
260
261 static gboolean
262 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
263 {
264   GstPad *target = GST_PROXY_PAD_TARGET (pad);
265
266   g_return_val_if_fail (target != NULL, FALSE);
267
268   return target->acceptcapsfunc (target, caps);
269 }
270
271 static GstCaps *
272 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
273 {
274   GstPad *target = GST_PROXY_PAD_TARGET (pad);
275
276   g_return_val_if_fail (target != NULL, NULL);
277
278   return target->fixatecapsfunc (target, caps);
279 }
280
281 static gboolean
282 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
283 {
284   GstPad *target = GST_PROXY_PAD_TARGET (pad);
285
286   g_return_val_if_fail (target != NULL, FALSE);
287
288   return gst_pad_set_caps (target, caps);
289 }
290
291 #define SETFUNC(member, kind) \
292   if (target->member) \
293     gst_pad_set_##kind##_function (pad, gst_proxy_pad_do_##kind)
294
295 static void
296 gst_proxy_pad_set_property (GObject * object, guint prop_id,
297     const GValue * value, GParamSpec * pspec)
298 {
299   GstPad *pad = GST_PAD (object);
300
301   switch (prop_id) {
302     case PROXY_PROP_TARGET:{
303       GstPad *target;
304
305       target = GST_PAD_CAST (gst_object_ref
306           (GST_OBJECT_CAST (g_value_get_object (value))));
307       GST_PROXY_PAD_TARGET (object) = target;
308
309       /* really, all these should have default implementations so I can set them
310        * in the _init() instead of here */
311       SETFUNC (querytypefunc, query_type);
312       SETFUNC (eventfunc, event);
313       SETFUNC (queryfunc, query);
314       SETFUNC (intlinkfunc, internal_link);
315       SETFUNC (activatefunc, activate);
316       SETFUNC (activatepullfunc, activatepull);
317       SETFUNC (activatepushfunc, activatepush);
318       SETFUNC (loopfunc, loop);
319       SETFUNC (getcapsfunc, getcaps);
320       SETFUNC (acceptcapsfunc, acceptcaps);
321       SETFUNC (fixatecapsfunc, fixatecaps);
322       SETFUNC (setcapsfunc, setcaps);
323
324       if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
325         SETFUNC (bufferallocfunc, bufferalloc);
326         SETFUNC (chainfunc, chain);
327       } else {
328         SETFUNC (getrangefunc, getrange);
329         SETFUNC (checkgetrangefunc, checkgetrange);
330       }
331
332       break;
333     }
334     default:
335       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336       break;
337   }
338 }
339
340 static void
341 gst_proxy_pad_get_property (GObject * object, guint prop_id,
342     GValue * value, GParamSpec * pspec)
343 {
344   switch (prop_id) {
345     case PROXY_PROP_TARGET:
346       g_value_set_object (value, GST_PROXY_PAD_TARGET (object));
347       break;
348     default:
349       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
350       break;
351   }
352 }
353
354 static void
355 gst_proxy_pad_init (GstProxyPad * pad)
356 {
357   pad->property_lock = g_mutex_new ();
358 }
359
360 static void
361 gst_proxy_pad_dispose (GObject * object)
362 {
363   GstPad *pad = GST_PAD (object);
364
365   if (GST_PROXY_PAD_TARGET (pad)) {
366     gst_object_replace ((GstObject **) & GST_PROXY_PAD_TARGET (pad), NULL);
367   }
368
369   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
370 }
371
372 static void
373 gst_proxy_pad_finalize (GObject * object)
374 {
375   GstProxyPad *pad = GST_PROXY_PAD (object);
376
377   g_mutex_free (pad->property_lock);
378   pad->property_lock = NULL;
379
380   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
381 }
382
383 #ifndef GST_DISABLE_LOADSAVE
384 /**
385  * gst_proxy_pad_save_thyself:
386  * @pad: a ghost #GstPad to save.
387  * @parent: the parent #xmlNodePtr to save the description in.
388  *
389  * Saves the ghost pad into an xml representation.
390  *
391  * Returns: the #xmlNodePtr representation of the pad.
392  */
393 static xmlNodePtr
394 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
395 {
396   xmlNodePtr self;
397
398   g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
399
400   self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
401   xmlNewChild (self, NULL, (xmlChar *) "name",
402       (xmlChar *) GST_OBJECT_NAME (object));
403   xmlNewChild (self, NULL, (xmlChar *) "parent",
404       (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
405
406   /* FIXME FIXME FIXME! */
407
408   return self;
409 }
410 #endif /* GST_DISABLE_LOADSAVE */
411
412
413 /***********************************************************************
414  * Ghost pads, implemented as a pair of proxy pads (sort of)
415  */
416
417
418 enum
419 {
420   GHOST_PROP_0,
421   GHOST_PROP_INTERNAL
422 };
423
424 struct _GstGhostPad
425 {
426   GstProxyPad pad;
427
428   GstPad *internal;
429   gulong notify_id;
430
431   /*< private > */
432   gpointer _gst_reserved[GST_PADDING];
433 };
434
435 struct _GstGhostPadClass
436 {
437   GstProxyPadClass parent_class;
438
439   /*< private > */
440   gpointer _gst_reserved[GST_PADDING];
441 };
442
443
444 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
445
446
447 static void gst_ghost_pad_dispose (GObject * object);
448 static void gst_ghost_pad_set_property (GObject * object, guint prop_id,
449     const GValue * value, GParamSpec * pspec);
450 static void gst_ghost_pad_get_property (GObject * object, guint prop_id,
451     GValue * value, GParamSpec * pspec);
452
453
454 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
455  * use for GST_PTR_FORMAT. */
456 static void
457 gst_critical (const gchar * format, ...)
458 {
459   va_list args;
460
461   va_start (args, format);
462   g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
463   va_end (args);
464 }
465
466 static void
467 gst_ghost_pad_class_init (GstGhostPadClass * klass)
468 {
469   GObjectClass *gobject_class = (GObjectClass *) klass;
470
471   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
472   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_set_property);
473   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_get_property);
474
475   g_object_class_install_property (G_OBJECT_CLASS (klass), GHOST_PROP_INTERNAL,
476       g_param_spec_object ("internal", "Internal",
477           "The ghost pad's internal pad", GST_TYPE_PAD, G_PARAM_READWRITE));
478 }
479
480 /* will only be called for src pads (afaict) */
481 static gboolean
482 gst_ghost_proxy_pad_do_activate_pull (GstPad * pad, gboolean active)
483 {
484   GstObject *parent;
485   gboolean ret = FALSE;
486
487   parent = gst_object_get_parent (GST_OBJECT (pad));
488   if (parent) {
489     /* hacky hacky!!! */
490     if (GST_IS_GHOST_PAD (parent))
491       ret = gst_pad_activate_pull (GST_PAD (parent), active);
492
493     gst_object_unref (parent);
494   }
495
496   return ret;
497 }
498
499 static GstPadLinkReturn
500 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
501 {
502   GstPad *internal, *target;
503
504   target = GST_PROXY_PAD_TARGET (pad);
505   g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
506
507   /* proxy the peer into the bin */
508   internal = g_object_new (GST_TYPE_PROXY_PAD,
509       "name", NULL,
510       "direction", GST_PAD_DIRECTION (peer),
511       "template", GST_PAD_PAD_TEMPLATE (peer), "target", peer, NULL);
512   g_object_set (pad, "internal", internal, NULL);
513
514   if ((GST_PAD_IS_SRC (internal) &&
515           gst_pad_link (internal, target) == GST_PAD_LINK_OK) ||
516       (GST_PAD_IS_SINK (internal) &&
517           (gst_pad_link (target, internal) == GST_PAD_LINK_OK))) {
518     gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
519     return GST_PAD_LINK_OK;
520   } else {
521     g_object_set (pad, "internal", NULL, NULL);
522     return GST_PAD_LINK_REFUSED;
523   }
524 }
525
526 static void
527 gst_ghost_pad_do_unlink (GstPad * pad)
528 {
529   GstPad *target = GST_PROXY_PAD_TARGET (pad);
530
531   g_return_if_fail (target != NULL);
532
533   if (target->unlinkfunc)
534     target->unlinkfunc (target);
535
536   /* FIXME: should do this here, but locks in deep_notify prevent it */
537   /* g_object_set (pad, "internal", NULL, NULL); */
538 }
539
540 static void
541 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
542 {
543   GstCaps *caps;
544
545   g_object_get (internal, "caps", &caps, NULL);
546
547   GST_LOCK (pad);
548   gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
549   GST_UNLOCK (pad);
550
551   g_object_notify (G_OBJECT (pad), "caps");
552   if (caps)
553     gst_caps_unref (caps);
554 }
555
556 static void
557 gst_ghost_pad_set_property (GObject * object, guint prop_id,
558     const GValue * value, GParamSpec * pspec)
559 {
560   GstGhostPad *pad = GST_GHOST_PAD (object);
561
562   switch (prop_id) {
563     case GHOST_PROP_INTERNAL:{
564       GstPad *internal;
565
566       g_mutex_lock (GST_PROXY_PAD (pad)->property_lock);
567
568       if (pad->internal) {
569         GstPad *intpeer;
570
571         gst_pad_set_activatepull_function (pad->internal, NULL);
572
573         g_signal_handler_disconnect (pad->internal, pad->notify_id);
574
575         intpeer = gst_pad_get_peer (pad->internal);
576         if (intpeer) {
577           if (GST_PAD_IS_SRC (pad->internal)) {
578             gst_pad_unlink (pad->internal, intpeer);
579           } else {
580             gst_pad_unlink (intpeer, pad->internal);
581           }
582           gst_object_unref (GST_OBJECT (intpeer));
583         }
584
585         /* should dispose it */
586         gst_object_unparent (GST_OBJECT_CAST (pad->internal));
587       }
588
589       internal = g_value_get_object (value);    /* no extra refcount... */
590
591       if (internal) {
592         if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
593                 GST_OBJECT_CAST (pad))) {
594           gst_critical ("Could not set internal pad %" GST_PTR_FORMAT,
595               internal);
596           g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
597           return;
598         }
599
600         /* could be more general here, iterating over all writable properties...
601          * taking the short road for now tho */
602         pad->notify_id = g_signal_connect (internal, "notify::caps",
603             G_CALLBACK (on_int_notify), pad);
604         on_int_notify (internal, NULL, pad);
605         gst_pad_set_activatepull_function (internal,
606             gst_ghost_proxy_pad_do_activate_pull);
607
608         /* a ref was taken by set_parent */
609       }
610
611       pad->internal = internal;
612
613       g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
614
615       break;
616     }
617     default:
618       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
619       break;
620   }
621 }
622
623 static void
624 gst_ghost_pad_get_property (GObject * object, guint prop_id,
625     GValue * value, GParamSpec * pspec)
626 {
627   switch (prop_id) {
628     case GHOST_PROP_INTERNAL:
629       g_value_set_object (value, GST_GHOST_PAD (object)->internal);
630       break;
631     default:
632       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
633       break;
634   }
635 }
636
637 static void
638 gst_ghost_pad_init (GstGhostPad * pad)
639 {
640   /* noop */
641 }
642
643 static void
644 gst_ghost_pad_dispose (GObject * object)
645 {
646   g_object_set (object, "internal", NULL, NULL);
647
648   G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
649 }
650
651 /**
652  * gst_ghost_pad_new:
653  * @name: the name of the new pad, or NULL to assign a default name.
654  * @target: the pad to ghost.
655  *
656  * Will ref the target.
657  *
658  * Returns: a new #GstPad, or NULL in case of an error.
659  */
660 GstPad *
661 gst_ghost_pad_new (const gchar * name, GstPad * target)
662 {
663   GstPad *ret;
664
665   g_return_val_if_fail (GST_IS_PAD (target), NULL);
666   g_return_val_if_fail (!GST_PAD_IS_LINKED (target), NULL);
667
668   ret = g_object_new (GST_TYPE_GHOST_PAD,
669       "name", name,
670       "direction", GST_PAD_DIRECTION (target),
671       "template", GST_PAD_PAD_TEMPLATE (target), "target", target, NULL);
672
673   gst_pad_set_link_function (ret, gst_ghost_pad_do_link);
674   gst_pad_set_unlink_function (ret, gst_ghost_pad_do_unlink);
675
676   return ret;
677 }