a7a45520dcaa187297ea128f29f21ed869ec9b85
[platform/upstream/gstreamer.git] / plugins / elements / gsttee.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gsttee.c: Tee element, one in N out
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 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "gsttee.h"
28
29 #include <string.h>
30
31 GST_DEBUG_CATEGORY_STATIC (gst_tee_debug);
32 #define GST_CAT_DEFAULT gst_tee_debug
33
34 GstElementDetails gst_tee_details = GST_ELEMENT_DETAILS (
35   "Tee pipe fitting",
36   "Generic",
37   "1-to-N pipe fitting",
38   "Erik Walthinsen <omega@cse.ogi.edu>, "
39   "Wim Taymans <wim.taymans@chello.be>"
40 );
41
42 /* Tee signals and args */
43 enum {
44   /* FILL ME */
45   LAST_SIGNAL
46 };
47
48 enum {
49   ARG_0,
50   ARG_SILENT,
51   ARG_NUM_PADS,
52   ARG_LAST_MESSAGE,
53   /* FILL ME */
54 };
55
56 GstStaticPadTemplate tee_src_template = GST_STATIC_PAD_TEMPLATE (
57   "src%d",
58   GST_PAD_SRC,
59   GST_PAD_REQUEST,
60   GST_STATIC_CAPS_ANY
61 );
62
63 #define _do_init(bla) \
64     GST_DEBUG_CATEGORY_INIT (gst_tee_debug, "tee", 0, "tee element");
65
66 GST_BOILERPLATE_FULL (GstTee, gst_tee, GstElement, GST_TYPE_ELEMENT, _do_init);
67
68 static GstPad*  gst_tee_request_new_pad (GstElement *element, GstPadTemplate *temp, const gchar *unused);
69
70 static void     gst_tee_set_property    (GObject *object, guint prop_id, 
71                                          const GValue *value, GParamSpec *pspec);
72 static void     gst_tee_get_property    (GObject *object, guint prop_id, 
73                                          GValue *value, GParamSpec *pspec);
74
75 static void     gst_tee_chain           (GstPad *pad, GstData *_data);
76
77
78 static void
79 gst_tee_base_init (gpointer g_class)
80 {
81   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
82   
83   gst_element_class_set_details (gstelement_class, &gst_tee_details);
84   gst_element_class_add_pad_template (gstelement_class,
85       gst_static_pad_template_get (&tee_src_template));
86 }
87 static void
88 gst_tee_class_init (GstTeeClass *klass) 
89 {
90   GObjectClass *gobject_class;
91   GstElementClass *gstelement_class;
92
93   gobject_class = (GObjectClass*)klass;
94   gstelement_class = (GstElementClass*)klass;
95
96
97   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_PADS,
98     g_param_spec_int ("num_pads", "num_pads", "num_pads",
99                       0, G_MAXINT, 0, G_PARAM_READABLE)); 
100   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
101     g_param_spec_boolean ("silent", "silent", "silent",
102                       FALSE, G_PARAM_READWRITE));
103   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
104     g_param_spec_string ("last_message", "last_message", "last_message",
105                          NULL, G_PARAM_READABLE));
106   
107
108   gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_tee_set_property);
109   gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_tee_get_property);
110
111   gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_tee_request_new_pad);
112 }
113
114 static void 
115 gst_tee_init (GstTee *tee) 
116 {
117   tee->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
118   gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad);
119   gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain));
120   gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
121   gst_pad_set_getcaps_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
122
123   tee->silent = FALSE;
124   tee->last_message = NULL;
125 }
126
127 /* helper compare function */
128 gint name_pad_compare (gconstpointer a, gconstpointer b)
129 {
130   GstPad* pad = (GstPad*) a;
131   gchar *name = (gchar *) b;
132   
133   g_assert (GST_IS_PAD (pad));
134
135   return strcmp (name, gst_pad_get_name (pad)); /* returns 0 if match */
136 }
137
138 static GstPad*
139 gst_tee_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) 
140 {
141   gchar *name;
142   GstPad *srcpad;
143   GstTee *tee;
144   gint i = 0;
145   const GList *pads;
146
147   g_return_val_if_fail (GST_IS_TEE (element), NULL);
148   
149   if (templ->direction != GST_PAD_SRC) {
150     g_warning ("gsttee: request new pad that is not a SRC pad\n");
151     return NULL;
152   }
153
154   tee = GST_TEE (element);
155
156   /* try names in order and find one that's not in use atm */
157   pads = gst_element_get_pad_list (element);
158     
159   name = NULL;
160   while (!name)
161   {
162     name = g_strdup_printf ("src%d", i);
163     if (g_list_find_custom ((GList *)pads, (gconstpointer) name, name_pad_compare) != NULL)
164     {
165       /* this name is taken, use the next one */
166       ++i;
167       g_free (name);
168       name = NULL;
169     }
170   }
171   if (!tee->silent) {
172     g_free (tee->last_message);
173     tee->last_message = g_strdup_printf ("new pad %s", name);
174     g_object_notify (G_OBJECT (tee), "last_message");
175   }
176   
177   srcpad = gst_pad_new_from_template (templ, name);
178   g_free (name);
179   gst_pad_set_link_function (srcpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
180   gst_pad_set_getcaps_function (srcpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
181   gst_element_add_pad (GST_ELEMENT (tee), srcpad);
182   GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL;
183
184   if (GST_PAD_CAPS (tee->sinkpad)) {
185     gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad));
186   }
187
188   return srcpad;
189 }
190
191 static void
192 gst_tee_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
193 {
194   GstTee *tee;
195
196   /* it's not null if we got it, but it might not be ours */
197   g_return_if_fail (GST_IS_TEE (object));
198
199   tee = GST_TEE (object);
200
201   switch (prop_id) {
202     case ARG_SILENT:
203       tee->silent = g_value_get_boolean (value);
204       g_object_notify (G_OBJECT (tee), "silent");
205       break;
206     default:
207       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
208       break;
209   }
210 }
211
212 static void
213 gst_tee_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
214 {
215   GstTee *tee;
216
217   /* it's not null if we got it, but it might not be ours */
218   g_return_if_fail (GST_IS_TEE (object));
219
220   tee = GST_TEE (object);
221
222   switch (prop_id) {
223     case ARG_NUM_PADS:
224       g_value_set_int (value, GST_ELEMENT (tee)->numsrcpads);
225       break;
226     case ARG_SILENT:
227       g_value_set_boolean (value, tee->silent);
228       break;
229     case ARG_LAST_MESSAGE:
230       g_value_set_string ((GValue *) value, tee->last_message);
231       break;
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235   }
236 }
237
238 /**
239  * gst_tee_chain:
240  * @pad: the pad to follow
241  * @buf: the buffer to pass
242  *
243  * Chain a buffer on a pad.
244  */
245 static void 
246 gst_tee_chain (GstPad *pad, GstData *_data) 
247 {
248   GstBuffer *buf = GST_BUFFER (_data);
249   GstTee *tee;
250   const GList *pads;
251
252   g_return_if_fail (pad != NULL);
253   g_return_if_fail (GST_IS_PAD (pad));
254   g_return_if_fail (buf != NULL);
255
256   tee = GST_TEE (gst_pad_get_parent (pad));
257
258   gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
259   
260   pads = gst_element_get_pad_list (GST_ELEMENT (tee));
261
262   while (pads) {
263     GstPad *outpad = GST_PAD (pads->data);
264     pads = g_list_next (pads);
265
266     if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC)
267       continue;
268
269     if (!tee->silent) {
270       g_free (tee->last_message);
271       tee->last_message = g_strdup_printf ("chain        ******* (%s:%s)t (%d bytes, %"
272                                            G_GUINT64_FORMAT ") %p",
273               GST_DEBUG_PAD_NAME (outpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf);
274       g_object_notify (G_OBJECT (tee), "last_message");
275     }
276
277     if (GST_PAD_IS_USABLE (outpad))
278       gst_pad_push (outpad, GST_DATA (buf));
279     else
280       gst_buffer_unref (buf);
281   }
282 }
283