First THREADED backport attempt, focusing on adding locks and making sure the API...
[platform/upstream/gstreamer.git] / gst / 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 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
32     GST_PAD_SINK,
33     GST_PAD_ALWAYS,
34     GST_STATIC_CAPS_ANY);
35
36 GST_DEBUG_CATEGORY_STATIC (gst_tee_debug);
37 #define GST_CAT_DEFAULT gst_tee_debug
38
39 GstElementDetails gst_tee_details = GST_ELEMENT_DETAILS ("Tee pipe fitting",
40     "Generic",
41     "1-to-N pipe fitting",
42     "Erik Walthinsen <omega@cse.ogi.edu>, "
43     "Wim Taymans <wim.taymans@chello.be>");
44
45 /* Tee signals and args */
46 enum
47 {
48   /* FILL ME */
49   LAST_SIGNAL
50 };
51
52 enum
53 {
54   ARG_0,
55   ARG_SILENT,
56   ARG_NUM_PADS,
57   ARG_LAST_MESSAGE
58       /* FILL ME */
59 };
60
61 GstStaticPadTemplate tee_src_template = GST_STATIC_PAD_TEMPLATE ("src%d",
62     GST_PAD_SRC,
63     GST_PAD_REQUEST,
64     GST_STATIC_CAPS_ANY);
65
66 #define _do_init(bla) \
67     GST_DEBUG_CATEGORY_INIT (gst_tee_debug, "tee", 0, "tee element");
68
69 GST_BOILERPLATE_FULL (GstTee, gst_tee, GstElement, GST_TYPE_ELEMENT, _do_init);
70
71 static GstPad *gst_tee_request_new_pad (GstElement * element,
72     GstPadTemplate * temp, const gchar * unused);
73
74 static void gst_tee_finalize (GObject * object);
75 static void gst_tee_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77 static void gst_tee_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79
80 static void gst_tee_chain (GstPad * pad, GstData * _data);
81
82
83 static void
84 gst_tee_base_init (gpointer g_class)
85 {
86   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
87
88   gst_element_class_add_pad_template (gstelement_class,
89       gst_static_pad_template_get (&sinktemplate));
90   gst_element_class_set_details (gstelement_class, &gst_tee_details);
91   gst_element_class_add_pad_template (gstelement_class,
92       gst_static_pad_template_get (&tee_src_template));
93 }
94
95 static void
96 gst_tee_finalize (GObject * object)
97 {
98   GstTee *tee;
99
100   tee = GST_TEE (object);
101
102   g_free (tee->last_message);
103
104   G_OBJECT_CLASS (parent_class)->finalize (object);
105 }
106
107 static void
108 gst_tee_class_init (GstTeeClass * klass)
109 {
110   GObjectClass *gobject_class;
111   GstElementClass *gstelement_class;
112
113   gobject_class = (GObjectClass *) klass;
114   gstelement_class = (GstElementClass *) klass;
115
116
117   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_PADS,
118       g_param_spec_int ("num_pads", "num_pads", "num_pads",
119           0, G_MAXINT, 0, G_PARAM_READABLE));
120   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
121       g_param_spec_boolean ("silent", "silent", "silent",
122           TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
123   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
124       g_param_spec_string ("last_message", "last_message", "last_message",
125           NULL, G_PARAM_READABLE));
126
127
128   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tee_finalize);
129   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_tee_set_property);
130   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_tee_get_property);
131
132   gstelement_class->request_new_pad =
133       GST_DEBUG_FUNCPTR (gst_tee_request_new_pad);
134 }
135
136 static void
137 gst_tee_init (GstTee * tee)
138 {
139   tee->sinkpad =
140       gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
141       "sink");
142   gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad);
143   gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain));
144   //gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
145   gst_pad_set_getcaps_function (tee->sinkpad,
146       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
147
148   tee->last_message = NULL;
149 }
150
151 /* helper compare function */
152 gint
153 name_pad_compare (gconstpointer a, gconstpointer b)
154 {
155   GstPad *pad = (GstPad *) a;
156   gchar *name = (gchar *) b;
157
158   g_assert (GST_IS_PAD (pad));
159
160   return strcmp (name, gst_pad_get_name (pad)); /* returns 0 if match */
161 }
162
163 static GstCaps *
164 gst_tee_getcaps (GstPad * _pad)
165 {
166   GstTee *tee = GST_TEE (gst_pad_get_parent (_pad));
167   GstCaps *caps = gst_caps_new_any (), *tmp, *res;
168   GstPad *pad;
169   const GList *pads;
170
171   for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) {
172     pad = GST_PAD (pads->data);
173     if (pad == _pad)
174       continue;
175
176     tmp = gst_pad_get_allowed_caps (pad);
177     res = gst_caps_intersect (caps, tmp);
178     gst_caps_unref (tmp);
179     gst_caps_unref (caps);
180     caps = res;
181   }
182
183   return caps;
184 }
185
186 static GstPadLinkReturn
187 gst_tee_link (GstPad * _pad, const GstCaps * caps)
188 {
189   GstTee *tee = GST_TEE (gst_pad_get_parent (_pad));
190   GstPadLinkReturn res;
191   GstPad *pad;
192   const GList *pads;
193
194   GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads");
195
196   for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) {
197     pad = GST_PAD (pads->data);
198     if (pad == _pad)
199       continue;
200
201     res = gst_pad_try_set_caps (pad, caps);
202     GST_DEBUG_OBJECT (tee, "Pad %s:%s gave response %d",
203         GST_DEBUG_PAD_NAME (pad), res);
204     if (GST_PAD_LINK_FAILED (res))
205       return res;
206   }
207
208   return GST_PAD_LINK_OK;
209 }
210
211 static GstPad *
212 gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ,
213     const gchar * unused)
214 {
215   gchar *name;
216   GstPad *srcpad;
217   GstTee *tee;
218   gint i = 0;
219   const GList *pads;
220
221   g_return_val_if_fail (GST_IS_TEE (element), NULL);
222
223   if (templ->direction != GST_PAD_SRC) {
224     g_warning ("gsttee: request new pad that is not a SRC pad\n");
225     return NULL;
226   }
227
228   tee = GST_TEE (element);
229
230   /* try names in order and find one that's not in use atm */
231   pads = element->pads;
232
233   name = NULL;
234   while (!name) {
235     name = g_strdup_printf ("src%d", i);
236     if (g_list_find_custom ((GList *) pads, (gconstpointer) name,
237             name_pad_compare) != NULL) {
238       /* this name is taken, use the next one */
239       ++i;
240       g_free (name);
241       name = NULL;
242     }
243   }
244   if (!tee->silent) {
245     g_free (tee->last_message);
246     tee->last_message = g_strdup_printf ("new pad %s", name);
247     g_object_notify (G_OBJECT (tee), "last_message");
248   }
249
250   srcpad = gst_pad_new_from_template (templ, name);
251   g_free (name);
252   gst_pad_set_link_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_link));
253   gst_pad_set_getcaps_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_getcaps));
254   gst_element_add_pad (GST_ELEMENT (tee), srcpad);
255   GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL;
256
257   if (GST_PAD_CAPS (tee->sinkpad)) {
258     gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad));
259   }
260
261   return srcpad;
262 }
263
264 static void
265 gst_tee_set_property (GObject * object, guint prop_id, const GValue * value,
266     GParamSpec * pspec)
267 {
268   GstTee *tee;
269
270   /* it's not null if we got it, but it might not be ours */
271   g_return_if_fail (GST_IS_TEE (object));
272
273   tee = GST_TEE (object);
274
275   switch (prop_id) {
276     case ARG_SILENT:
277       tee->silent = g_value_get_boolean (value);
278       g_object_notify (G_OBJECT (tee), "silent");
279       break;
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
282       break;
283   }
284 }
285
286 static void
287 gst_tee_get_property (GObject * object, guint prop_id, GValue * value,
288     GParamSpec * pspec)
289 {
290   GstTee *tee;
291
292   /* it's not null if we got it, but it might not be ours */
293   g_return_if_fail (GST_IS_TEE (object));
294
295   tee = GST_TEE (object);
296
297   switch (prop_id) {
298     case ARG_NUM_PADS:
299       g_value_set_int (value, GST_ELEMENT (tee)->numsrcpads);
300       break;
301     case ARG_SILENT:
302       g_value_set_boolean (value, tee->silent);
303       break;
304     case ARG_LAST_MESSAGE:
305       g_value_set_string (value, tee->last_message);
306       break;
307     default:
308       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
309       break;
310   }
311 }
312
313 /**
314  * gst_tee_chain:
315  * @pad: the pad to follow
316  * @buf: the buffer to pass
317  *
318  * Chain a buffer on a pad.
319  */
320 static void
321 gst_tee_chain (GstPad * pad, GstData * _data)
322 {
323   GstBuffer *buf = GST_BUFFER (_data);
324   GstTee *tee;
325   const GList *pads;
326
327   g_return_if_fail (pad != NULL);
328   g_return_if_fail (GST_IS_PAD (pad));
329   g_return_if_fail (buf != NULL);
330
331   tee = GST_TEE (gst_pad_get_parent (pad));
332
333   gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
334
335   pads = GST_ELEMENT (tee)->pads;
336
337   while (pads) {
338     GstPad *outpad = GST_PAD (pads->data);
339
340     pads = g_list_next (pads);
341
342     if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC)
343       continue;
344
345     if (!tee->silent) {
346       g_free (tee->last_message);
347       tee->last_message =
348           g_strdup_printf ("chain        ******* (%s:%s)t (%d bytes, %"
349           G_GUINT64_FORMAT ") %p", GST_DEBUG_PAD_NAME (outpad),
350           GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf);
351       g_object_notify (G_OBJECT (tee), "last_message");
352     }
353
354     if (GST_PAD_IS_USABLE (outpad))
355       gst_pad_push (outpad, GST_DATA (buf));
356     else
357       gst_buffer_unref (buf);
358   }
359 }