a54022fc6a797f31189b69f73bfc2b922d0650c5
[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 #include "gsttee.h"
24
25
26 GstElementDetails gst_tee_details = {
27   "Tee pipe fitting",
28   "Generic",
29   "LGPL",
30   "1-to-N pipe fitting",
31   VERSION,
32   "Erik Walthinsen <omega@cse.ogi.edu>\n"
33   "Wim Taymans <wim.taymans@chello.be>",
34   "(C) 1999, 2000",
35 };
36
37 /* Tee signals and args */
38 enum {
39   /* FILL ME */
40   LAST_SIGNAL
41 };
42
43 enum {
44   ARG_0,
45   ARG_SILENT,
46   ARG_NUM_PADS,
47   ARG_LAST_MESSAGE,
48   /* FILL ME */
49 };
50
51 GST_PAD_TEMPLATE_FACTORY (tee_src_factory,
52   "src%d",
53   GST_PAD_SRC,
54   GST_PAD_REQUEST,
55   NULL                  /* no caps */
56 );
57
58 static void     gst_tee_class_init      (GstTeeClass *klass);
59 static void     gst_tee_init            (GstTee *tee);
60
61 static GstPad*  gst_tee_request_new_pad (GstElement *element, GstPadTemplate *temp, const gchar *unused);
62
63 static void     gst_tee_set_property    (GObject *object, guint prop_id, 
64                                          const GValue *value, GParamSpec *pspec);
65 static void     gst_tee_get_property    (GObject *object, guint prop_id, 
66                                          GValue *value, GParamSpec *pspec);
67
68 static void     gst_tee_chain           (GstPad *pad, GstBuffer *buf);
69
70
71 static GstElementClass *parent_class = NULL;
72 /*static guint gst_tee_signals[LAST_SIGNAL] = { 0 };*/
73
74 GType
75 gst_tee_get_type(void) {
76   static GType tee_type = 0;
77
78   if (!tee_type) {
79     static const GTypeInfo tee_info = {
80       sizeof(GstTeeClass),      NULL,
81       NULL,
82       (GClassInitFunc)gst_tee_class_init,
83       NULL,
84       NULL,
85       sizeof(GstTee),
86       0,
87       (GInstanceInitFunc)gst_tee_init,
88     };
89     tee_type = g_type_register_static (GST_TYPE_ELEMENT, "GstTee", &tee_info, 0);
90   }
91   return tee_type;
92 }
93
94 static void
95 gst_tee_class_init (GstTeeClass *klass) 
96 {
97   GObjectClass *gobject_class;
98   GstElementClass *gstelement_class;
99
100   gobject_class = (GObjectClass*)klass;
101   gstelement_class = (GstElementClass*)klass;
102
103   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
104
105   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_PADS,
106     g_param_spec_int ("num_pads", "num_pads", "num_pads",
107                       0, G_MAXINT, 0, G_PARAM_READABLE)); 
108   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
109     g_param_spec_boolean ("silent", "silent", "silent",
110                       FALSE, G_PARAM_READWRITE));
111   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
112     g_param_spec_string ("last_message", "last_message", "last_message",
113                          NULL, G_PARAM_READABLE));
114   
115
116   gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_tee_set_property);
117   gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_tee_get_property);
118
119   gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_tee_request_new_pad);
120 }
121
122 static GstPadLinkReturn 
123 gst_tee_sinklink (GstPad *pad, GstCaps *caps) 
124 {
125   GstTee *tee;
126   const GList *pads;
127   GstPadLinkReturn set_retval;
128   
129   tee = GST_TEE (gst_pad_get_parent (pad));
130
131   if (!GST_CAPS_IS_FIXED (caps)) {
132     return GST_PAD_LINK_DELAYED;
133   }
134
135   /* go through all the src pads */
136   pads = gst_element_get_pad_list (GST_ELEMENT (tee));
137
138   while (pads) {
139     GstPad *outpad = GST_PAD (pads->data);
140     pads = g_list_next (pads);
141                      
142     if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC || !GST_PAD_IS_USABLE (outpad))
143       continue;
144
145     if ((set_retval = gst_pad_try_set_caps (outpad, caps)) <= 0) {
146       return set_retval;
147     }
148   }
149   return GST_PAD_LINK_OK;
150 }
151
152 static void 
153 gst_tee_init (GstTee *tee) 
154 {
155   tee->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
156   gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad);
157   gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain));
158   gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_sinklink));
159
160   tee->silent = FALSE;
161   tee->last_message = NULL;
162 }
163
164 /* helper compare function */
165 gint name_pad_compare (gconstpointer a, gconstpointer b)
166 {
167   GstPad* pad = (GstPad*) a;
168   gchar *name = (gchar *) b;
169   
170   g_assert (GST_IS_PAD (pad));
171
172   return g_strcasecmp (name, gst_pad_get_name (pad)); /* returns 0 if match */
173 }
174
175 static GstPad*
176 gst_tee_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) 
177 {
178   gchar *name;
179   GstPad *srcpad;
180   GstTee *tee;
181   gint i = 0;
182   const GList *pads;
183
184   g_return_val_if_fail (GST_IS_TEE (element), NULL);
185   
186   if (templ->direction != GST_PAD_SRC) {
187     g_warning ("gsttee: request new pad that is not a SRC pad\n");
188     return NULL;
189   }
190
191   tee = GST_TEE (element);
192
193   /* try names in order and find one that's not in use atm */
194   pads = gst_element_get_pad_list (element);
195     
196   name = NULL;
197   while (!name)
198   {
199     name = g_strdup_printf ("src%d", i);
200     if (g_list_find_custom ((GList *)pads, (gconstpointer) name, name_pad_compare) != NULL)
201     {
202       /* this name is taken, use the next one */
203       ++i;
204       g_free (name);
205       name = NULL;
206     }
207   }
208   if (!tee->silent) {
209     g_free (tee->last_message);
210     tee->last_message = g_strdup_printf ("new pad %s", name);
211     g_object_notify (G_OBJECT (tee), "last_message");
212   }
213   
214   srcpad = gst_pad_new_from_template (templ, name);
215   g_free (name);
216   gst_element_add_pad (GST_ELEMENT (tee), srcpad);
217   GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL;
218
219   if (GST_PAD_CAPS (tee->sinkpad)) {
220     gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad));
221   }
222
223   return srcpad;
224 }
225
226 static void
227 gst_tee_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
228 {
229   GstTee *tee;
230
231   /* it's not null if we got it, but it might not be ours */
232   g_return_if_fail (GST_IS_TEE (object));
233
234   tee = GST_TEE (object);
235
236   switch (prop_id) {
237     case ARG_SILENT:
238       tee->silent = g_value_get_boolean (value);
239       break;
240     default:
241       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
242       break;
243   }
244 }
245
246 static void
247 gst_tee_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
248 {
249   GstTee *tee;
250
251   /* it's not null if we got it, but it might not be ours */
252   g_return_if_fail (GST_IS_TEE (object));
253
254   tee = GST_TEE (object);
255
256   switch (prop_id) {
257     case ARG_NUM_PADS:
258       g_value_set_int (value, GST_ELEMENT (tee)->numsrcpads);
259       break;
260     case ARG_SILENT:
261       g_value_set_boolean (value, tee->silent);
262       break;
263     case ARG_LAST_MESSAGE:
264       g_value_set_string ((GValue *) value, tee->last_message);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269   }
270 }
271
272 /**
273  * gst_tee_chain:
274  * @pad: the pad to follow
275  * @buf: the buffer to pass
276  *
277  * Chain a buffer on a pad.
278  */
279 static void 
280 gst_tee_chain (GstPad *pad, GstBuffer *buf) 
281 {
282   GstTee *tee;
283   const GList *pads;
284
285   g_return_if_fail (pad != NULL);
286   g_return_if_fail (GST_IS_PAD (pad));
287   g_return_if_fail (buf != NULL);
288
289   tee = GST_TEE (gst_pad_get_parent (pad));
290
291   gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
292   
293   pads = gst_element_get_pad_list (GST_ELEMENT (tee));
294
295   while (pads) {
296     GstPad *outpad = GST_PAD (pads->data);
297     pads = g_list_next (pads);
298
299     if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC)
300       continue;
301
302     if (!tee->silent) {
303       g_free (tee->last_message);
304       tee->last_message = g_strdup_printf ("chain        ******* (%s:%s)t (%d bytes, %llu) %p",
305               GST_DEBUG_PAD_NAME (outpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf);
306       g_object_notify (G_OBJECT (tee), "last_message");
307     }
308
309     if (GST_PAD_IS_USABLE (outpad))
310       gst_pad_push (outpad, buf);
311     else
312       gst_buffer_unref (buf);
313   }
314 }
315
316 gboolean
317 gst_tee_factory_init (GstElementFactory *factory)
318 {
319   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (tee_src_factory));
320
321   return TRUE;
322 }
323