gst/: The deprecated pad loop function is removed now.
[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   GstFlowReturn result;
156   GstPad *target = GST_PROXY_PAD_TARGET (pad);
157   GstPad *peer;
158
159   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
160
161   peer = gst_pad_get_peer (target);
162   if (peer) {
163     GST_DEBUG ("buffer alloc on %s:%s", GST_DEBUG_PAD_NAME (target));
164
165     result = gst_pad_alloc_buffer (peer, offset, size, caps, buf);
166
167     gst_object_unref (peer);
168   } else {
169     result = GST_FLOW_NOT_LINKED;
170   }
171
172   return result;
173 }
174
175 static gboolean
176 gst_proxy_pad_do_activate (GstPad * pad)
177 {
178   GstPad *target = GST_PROXY_PAD_TARGET (pad);
179
180   g_return_val_if_fail (target != NULL, FALSE);
181
182   return gst_pad_activate_push (pad, TRUE);
183 }
184
185 static gboolean
186 gst_proxy_pad_do_activatepull (GstPad * pad, gboolean active)
187 {
188   GstActivateMode old;
189   GstPad *target = GST_PROXY_PAD_TARGET (pad);
190
191   g_return_val_if_fail (target != NULL, FALSE);
192
193   GST_LOCK (target);
194   old = GST_PAD_ACTIVATE_MODE (target);
195   GST_UNLOCK (target);
196
197   if ((active && old == GST_ACTIVATE_PULL)
198       || (!active && old == GST_ACTIVATE_NONE))
199     return TRUE;
200   else
201     return gst_pad_activate_pull (target, active);
202 }
203
204 static gboolean
205 gst_proxy_pad_do_activatepush (GstPad * pad, gboolean active)
206 {
207   GstActivateMode old;
208   GstPad *target = GST_PROXY_PAD_TARGET (pad);
209
210   g_return_val_if_fail (target != NULL, FALSE);
211
212   GST_LOCK (target);
213   old = GST_PAD_ACTIVATE_MODE (target);
214   GST_UNLOCK (target);
215
216   if ((active && old == GST_ACTIVATE_PUSH)
217       || (!active && old == GST_ACTIVATE_NONE))
218     return TRUE;
219   else
220     return gst_pad_activate_push (target, active);
221 }
222
223 static GstFlowReturn
224 gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer)
225 {
226   GstPad *target = GST_PROXY_PAD_TARGET (pad);
227
228   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
229
230   return gst_pad_chain (target, buffer);
231 }
232
233 static GstFlowReturn
234 gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size,
235     GstBuffer ** buffer)
236 {
237   GstPad *target = GST_PROXY_PAD_TARGET (pad);
238
239   g_return_val_if_fail (target != NULL, GST_FLOW_UNEXPECTED);
240
241   return gst_pad_get_range (target, offset, size, buffer);
242 }
243
244 static gboolean
245 gst_proxy_pad_do_checkgetrange (GstPad * pad)
246 {
247   GstPad *target = GST_PROXY_PAD_TARGET (pad);
248
249   g_return_val_if_fail (target != NULL, FALSE);
250
251   return gst_pad_check_pull_range (target);
252 }
253
254 static GstCaps *
255 gst_proxy_pad_do_getcaps (GstPad * pad)
256 {
257   GstPad *target = GST_PROXY_PAD_TARGET (pad);
258
259   g_return_val_if_fail (target != NULL, NULL);
260
261   return gst_pad_get_caps (target);
262 }
263
264 static gboolean
265 gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps)
266 {
267   GstPad *target = GST_PROXY_PAD_TARGET (pad);
268
269   g_return_val_if_fail (target != NULL, FALSE);
270
271   return gst_pad_accept_caps (target, caps);
272 }
273
274 static GstCaps *
275 gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps)
276 {
277   GstPad *target = GST_PROXY_PAD_TARGET (pad);
278
279   g_return_val_if_fail (target != NULL, NULL);
280
281   return gst_pad_fixate_caps (target, caps);
282 }
283
284 static gboolean
285 gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps)
286 {
287   GstPad *target = GST_PROXY_PAD_TARGET (pad);
288
289   g_return_val_if_fail (target != NULL, FALSE);
290
291   return gst_pad_set_caps (target, caps);
292 }
293
294 #define SETFUNC(member, kind) \
295   if (target->member) \
296     gst_pad_set_##kind##_function (pad, gst_proxy_pad_do_##kind)
297
298 static void
299 gst_proxy_pad_set_property (GObject * object, guint prop_id,
300     const GValue * value, GParamSpec * pspec)
301 {
302   GstPad *pad = GST_PAD (object);
303
304   switch (prop_id) {
305     case PROXY_PROP_TARGET:{
306       GstPad *target;
307
308       target = GST_PAD_CAST (gst_object_ref
309           (GST_OBJECT_CAST (g_value_get_object (value))));
310       GST_PROXY_PAD_TARGET (object) = target;
311
312       /* really, all these should have default implementations so I can set them
313        * in the _init() instead of here */
314       SETFUNC (querytypefunc, query_type);
315       SETFUNC (eventfunc, event);
316       SETFUNC (queryfunc, query);
317       SETFUNC (intlinkfunc, internal_link);
318       SETFUNC (activatefunc, activate);
319       SETFUNC (activatepullfunc, activatepull);
320       SETFUNC (activatepushfunc, activatepush);
321       SETFUNC (getcapsfunc, getcaps);
322       SETFUNC (acceptcapsfunc, acceptcaps);
323       SETFUNC (fixatecapsfunc, fixatecaps);
324       SETFUNC (setcapsfunc, setcaps);
325
326       if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
327         SETFUNC (bufferallocfunc, bufferalloc);
328         SETFUNC (chainfunc, chain);
329       } else {
330         SETFUNC (getrangefunc, getrange);
331         SETFUNC (checkgetrangefunc, checkgetrange);
332       }
333
334       break;
335     }
336     default:
337       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
338       break;
339   }
340 }
341
342 static void
343 gst_proxy_pad_get_property (GObject * object, guint prop_id,
344     GValue * value, GParamSpec * pspec)
345 {
346   switch (prop_id) {
347     case PROXY_PROP_TARGET:
348       g_value_set_object (value, GST_PROXY_PAD_TARGET (object));
349       break;
350     default:
351       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
352       break;
353   }
354 }
355
356 static void
357 gst_proxy_pad_init (GstProxyPad * pad)
358 {
359   pad->property_lock = g_mutex_new ();
360 }
361
362 static void
363 gst_proxy_pad_dispose (GObject * object)
364 {
365   GstPad *pad = GST_PAD (object);
366
367   if (GST_PROXY_PAD_TARGET (pad)) {
368     gst_object_replace ((GstObject **) & GST_PROXY_PAD_TARGET (pad), NULL);
369   }
370
371   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object);
372 }
373
374 static void
375 gst_proxy_pad_finalize (GObject * object)
376 {
377   GstProxyPad *pad = GST_PROXY_PAD (object);
378
379   g_mutex_free (pad->property_lock);
380   pad->property_lock = NULL;
381
382   G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object);
383 }
384
385 #ifndef GST_DISABLE_LOADSAVE
386 /**
387  * gst_proxy_pad_save_thyself:
388  * @pad: a ghost #GstPad to save.
389  * @parent: the parent #xmlNodePtr to save the description in.
390  *
391  * Saves the ghost pad into an xml representation.
392  *
393  * Returns: the #xmlNodePtr representation of the pad.
394  */
395 static xmlNodePtr
396 gst_proxy_pad_save_thyself (GstObject * object, xmlNodePtr parent)
397 {
398   xmlNodePtr self;
399
400   g_return_val_if_fail (GST_IS_PROXY_PAD (object), NULL);
401
402   self = xmlNewChild (parent, NULL, (xmlChar *) "ghostpad", NULL);
403   xmlNewChild (self, NULL, (xmlChar *) "name",
404       (xmlChar *) GST_OBJECT_NAME (object));
405   xmlNewChild (self, NULL, (xmlChar *) "parent",
406       (xmlChar *) GST_OBJECT_NAME (GST_OBJECT_PARENT (object)));
407
408   /* FIXME FIXME FIXME! */
409
410   return self;
411 }
412 #endif /* GST_DISABLE_LOADSAVE */
413
414
415 /***********************************************************************
416  * Ghost pads, implemented as a pair of proxy pads (sort of)
417  */
418
419
420 enum
421 {
422   GHOST_PROP_0,
423   GHOST_PROP_INTERNAL
424 };
425
426 struct _GstGhostPad
427 {
428   GstProxyPad pad;
429
430   GstPad *internal;
431   gulong notify_id;
432
433   /*< private > */
434   gpointer _gst_reserved[GST_PADDING];
435 };
436
437 struct _GstGhostPadClass
438 {
439   GstProxyPadClass parent_class;
440
441   /*< private > */
442   gpointer _gst_reserved[GST_PADDING];
443 };
444
445
446 G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD);
447
448
449 static void gst_ghost_pad_dispose (GObject * object);
450 static void gst_ghost_pad_set_property (GObject * object, guint prop_id,
451     const GValue * value, GParamSpec * pspec);
452 static void gst_ghost_pad_get_property (GObject * object, guint prop_id,
453     GValue * value, GParamSpec * pspec);
454
455
456 /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we
457  * use for GST_PTR_FORMAT. */
458 static void
459 gst_critical (const gchar * format, ...)
460 {
461   va_list args;
462
463   va_start (args, format);
464   g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, format, args);
465   va_end (args);
466 }
467
468 static void
469 gst_ghost_pad_class_init (GstGhostPadClass * klass)
470 {
471   GObjectClass *gobject_class = (GObjectClass *) klass;
472
473   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose);
474   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_set_property);
475   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_ghost_pad_get_property);
476
477   g_object_class_install_property (G_OBJECT_CLASS (klass), GHOST_PROP_INTERNAL,
478       g_param_spec_object ("internal", "Internal",
479           "The ghost pad's internal pad", GST_TYPE_PAD, G_PARAM_READWRITE));
480 }
481
482 /* will only be called for src pads (afaict) */
483 static gboolean
484 gst_ghost_proxy_pad_do_activate_pull (GstPad * pad, gboolean active)
485 {
486   GstObject *parent;
487   gboolean ret = FALSE;
488
489   parent = gst_object_get_parent (GST_OBJECT (pad));
490   if (parent) {
491     /* hacky hacky!!! */
492     if (GST_IS_GHOST_PAD (parent))
493       ret = gst_pad_activate_pull (GST_PAD (parent), active);
494
495     gst_object_unref (parent);
496   }
497
498   return ret;
499 }
500
501 static GstPadLinkReturn
502 gst_ghost_pad_do_link (GstPad * pad, GstPad * peer)
503 {
504   GstPad *internal, *target;
505
506   target = GST_PROXY_PAD_TARGET (pad);
507   g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED);
508
509   /* proxy the peer into the bin */
510   internal = g_object_new (GST_TYPE_PROXY_PAD,
511       "name", NULL,
512       "direction", GST_PAD_DIRECTION (peer),
513       "template", GST_PAD_PAD_TEMPLATE (peer), "target", peer, NULL);
514   g_object_set (pad, "internal", internal, NULL);
515
516   if ((GST_PAD_IS_SRC (internal) &&
517           gst_pad_link (internal, target) == GST_PAD_LINK_OK) ||
518       (GST_PAD_IS_SINK (internal) &&
519           (gst_pad_link (target, internal) == GST_PAD_LINK_OK))) {
520     gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad));
521     return GST_PAD_LINK_OK;
522   } else {
523     g_object_set (pad, "internal", NULL, NULL);
524     return GST_PAD_LINK_REFUSED;
525   }
526 }
527
528 static void
529 gst_ghost_pad_do_unlink (GstPad * pad)
530 {
531   GstPad *target = GST_PROXY_PAD_TARGET (pad);
532
533   g_return_if_fail (target != NULL);
534
535   if (target->unlinkfunc)
536     target->unlinkfunc (target);
537
538   /* FIXME: should do this here, but locks in deep_notify prevent it */
539   /* g_object_set (pad, "internal", NULL, NULL); */
540 }
541
542 static void
543 on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad)
544 {
545   GstCaps *caps;
546
547   g_object_get (internal, "caps", &caps, NULL);
548
549   GST_LOCK (pad);
550   gst_caps_replace (&(GST_PAD_CAPS (pad)), caps);
551   GST_UNLOCK (pad);
552
553   g_object_notify (G_OBJECT (pad), "caps");
554   if (caps)
555     gst_caps_unref (caps);
556 }
557
558 static void
559 gst_ghost_pad_set_property (GObject * object, guint prop_id,
560     const GValue * value, GParamSpec * pspec)
561 {
562   GstGhostPad *pad = GST_GHOST_PAD (object);
563
564   switch (prop_id) {
565     case GHOST_PROP_INTERNAL:{
566       GstPad *internal;
567
568       g_mutex_lock (GST_PROXY_PAD (pad)->property_lock);
569
570       if (pad->internal) {
571         GstPad *intpeer;
572
573         gst_pad_set_activatepull_function (pad->internal, NULL);
574
575         g_signal_handler_disconnect (pad->internal, pad->notify_id);
576
577         intpeer = gst_pad_get_peer (pad->internal);
578         if (intpeer) {
579           if (GST_PAD_IS_SRC (pad->internal)) {
580             gst_pad_unlink (pad->internal, intpeer);
581           } else {
582             gst_pad_unlink (intpeer, pad->internal);
583           }
584           gst_object_unref (intpeer);
585         }
586
587         /* should dispose it */
588         gst_object_unparent (GST_OBJECT_CAST (pad->internal));
589       }
590
591       internal = g_value_get_object (value);    /* no extra refcount... */
592
593       if (internal) {
594         if (!gst_object_set_parent (GST_OBJECT_CAST (internal),
595                 GST_OBJECT_CAST (pad))) {
596           gst_critical ("Could not set internal pad %" GST_PTR_FORMAT,
597               internal);
598           g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
599           return;
600         }
601
602         /* could be more general here, iterating over all writable properties...
603          * taking the short road for now tho */
604         pad->notify_id = g_signal_connect (internal, "notify::caps",
605             G_CALLBACK (on_int_notify), pad);
606         on_int_notify (internal, NULL, pad);
607         gst_pad_set_activatepull_function (internal,
608             gst_ghost_proxy_pad_do_activate_pull);
609
610         /* a ref was taken by set_parent */
611       }
612
613       pad->internal = internal;
614
615       g_mutex_unlock (GST_PROXY_PAD (pad)->property_lock);
616
617       break;
618     }
619     default:
620       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
621       break;
622   }
623 }
624
625 static void
626 gst_ghost_pad_get_property (GObject * object, guint prop_id,
627     GValue * value, GParamSpec * pspec)
628 {
629   switch (prop_id) {
630     case GHOST_PROP_INTERNAL:
631       g_value_set_object (value, GST_GHOST_PAD (object)->internal);
632       break;
633     default:
634       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
635       break;
636   }
637 }
638
639 static void
640 gst_ghost_pad_init (GstGhostPad * pad)
641 {
642   /* noop */
643 }
644
645 static void
646 gst_ghost_pad_dispose (GObject * object)
647 {
648   g_object_set (object, "internal", NULL, NULL);
649
650   G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object);
651 }
652
653 /**
654  * gst_ghost_pad_new:
655  * @name: the name of the new pad, or NULL to assign a default name.
656  * @target: the pad to ghost.
657  *
658  * Will ref the target.
659  *
660  * Returns: a new #GstPad, or NULL in case of an error.
661  */
662 GstPad *
663 gst_ghost_pad_new (const gchar * name, GstPad * target)
664 {
665   GstPad *ret;
666
667   g_return_val_if_fail (GST_IS_PAD (target), NULL);
668   g_return_val_if_fail (!GST_PAD_IS_LINKED (target), NULL);
669
670   ret = g_object_new (GST_TYPE_GHOST_PAD,
671       "name", name,
672       "direction", GST_PAD_DIRECTION (target),
673       "template", GST_PAD_PAD_TEMPLATE (target), "target", target, NULL);
674
675   gst_pad_set_link_function (ret, gst_ghost_pad_do_link);
676   gst_pad_set_unlink_function (ret, gst_ghost_pad_do_unlink);
677
678   return ret;
679 }