Merge CAPS branch
[platform/upstream/gstreamer.git] / plugins / elements / gstidentity.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstidentity.c: 
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
24 #include <stdlib.h>
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "gstidentity.h"
31
32 GST_DEBUG_CATEGORY_STATIC (gst_identity_debug);
33 #define GST_CAT_DEFAULT gst_identity_debug
34
35 GstElementDetails gst_identity_details = GST_ELEMENT_DETAILS (
36   "Identity",
37   "Generic",
38   "Pass data without modification",
39   "Erik Walthinsen <omega@cse.ogi.edu>"
40 );
41
42
43 /* Identity signals and args */
44 enum {
45   SIGNAL_HANDOFF,
46   /* FILL ME */
47   LAST_SIGNAL
48 };
49
50 enum {
51   ARG_0,
52   ARG_LOOP_BASED,
53   ARG_SLEEP_TIME,
54   ARG_DUPLICATE,
55   ARG_ERROR_AFTER,
56   ARG_DROP_PROBABILITY,
57   ARG_SILENT,
58   ARG_LAST_MESSAGE,
59   ARG_DUMP,
60   ARG_DELAY_CAPSNEGO,
61 };
62
63
64 static void gst_identity_base_init      (gpointer g_class);
65 static void gst_identity_class_init     (GstIdentityClass *klass);
66 static void gst_identity_init           (GstIdentity *identity);
67
68 static void gst_identity_set_property   (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
69 static void gst_identity_get_property   (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
70
71 static void gst_identity_chain          (GstPad *pad, GstData *_data);
72
73 static GstElementClass *parent_class = NULL;
74 static guint gst_identity_signals[LAST_SIGNAL] = { 0 };
75
76 GType
77 gst_identity_get_type (void) 
78 {
79   static GType identity_type = 0;
80
81   if (!identity_type) {
82     static const GTypeInfo identity_info = {
83       sizeof(GstIdentityClass),
84       gst_identity_base_init,
85       NULL,
86       (GClassInitFunc)gst_identity_class_init,
87       NULL,
88       NULL,
89       sizeof(GstIdentity),
90       0,
91       (GInstanceInitFunc)gst_identity_init,
92     };
93     identity_type = g_type_register_static (GST_TYPE_ELEMENT, "GstIdentity", &identity_info, 0);
94   
95     GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element");
96   }
97   return identity_type;
98 }
99
100 static void
101 gst_identity_base_init (gpointer g_class)
102 {
103   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
104   
105   gst_element_class_set_details (gstelement_class, &gst_identity_details);
106 }
107 static void 
108 gst_identity_class_init (GstIdentityClass *klass) 
109 {
110   GObjectClass *gobject_class;
111
112   gobject_class = G_OBJECT_CLASS (klass);
113
114   parent_class = g_type_class_peek_parent (klass);
115
116   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED,
117     g_param_spec_boolean ("loop-based", "Loop-based", 
118                           "Set to TRUE to use loop-based rather than chain-based scheduling",
119                           TRUE, G_PARAM_READWRITE)); 
120   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SLEEP_TIME,
121     g_param_spec_uint ("sleep-time", "Sleep time", "Microseconds to sleep between processing",
122                        0, G_MAXUINT, 0, G_PARAM_READWRITE));
123   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUPLICATE,
124     g_param_spec_uint ("duplicate", "Duplicate Buffers", "Push the buffers N times",
125                        0, G_MAXUINT, 1, G_PARAM_READWRITE));
126   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ERROR_AFTER,
127     g_param_spec_int ("error_after", "Error After", "Error after N buffers",
128                        G_MININT, G_MAXINT, -1, G_PARAM_READWRITE));
129   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DROP_PROBABILITY,
130     g_param_spec_float ("drop_probability", "Drop Probability", "The Probability a buffer is dropped",
131                         0.0, 1.0, 0.0, G_PARAM_READWRITE));
132   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
133     g_param_spec_boolean ("silent", "silent", "silent",
134                           FALSE, G_PARAM_READWRITE)); 
135   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
136     g_param_spec_string ("last-message", "last-message", "last-message",
137                          NULL, G_PARAM_READABLE)); 
138   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP,
139     g_param_spec_boolean("dump", "Dump", "Dump buffer contents",
140                          FALSE, G_PARAM_READWRITE));
141   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELAY_CAPSNEGO,
142     g_param_spec_boolean("delay_capsnego", "Delay Caps Nego", "Delay capsnegotiation to loop/chain function",
143                          FALSE, G_PARAM_READWRITE));
144
145   gst_identity_signals[SIGNAL_HANDOFF] =
146     g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
147                    G_STRUCT_OFFSET (GstIdentityClass, handoff), NULL, NULL,
148                    gst_marshal_VOID__POINTER, G_TYPE_NONE, 1,
149                    GST_TYPE_BUFFER);
150
151   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);  
152   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
153 }
154
155 static GstCaps*
156 gst_identity_getcaps (GstPad *pad)
157 {
158   GstIdentity *identity;
159   GstPad *otherpad;
160   GstPad *peer;
161   
162   identity = GST_IDENTITY (gst_pad_get_parent (pad));
163
164   if (identity->delay_capsnego) {
165     return NULL;
166   }
167
168   otherpad = (pad == identity->srcpad ? identity->sinkpad : identity->srcpad);
169   peer = GST_PAD_PEER (otherpad);
170
171   if (peer) {
172     return gst_pad_get_caps (peer);
173   } else {
174     return gst_caps_new_any ();
175   }
176 }
177
178 static GstPadLinkReturn
179 gst_identity_link (GstPad *pad, const GstCaps *caps)
180 {
181   GstIdentity *identity;
182   
183   identity = GST_IDENTITY (gst_pad_get_parent (pad));
184
185   if (gst_caps_is_fixed (caps)) {
186     if (identity->delay_capsnego && GST_PAD_IS_SINK (pad)) {
187       identity->srccaps = gst_caps_copy (caps);
188
189       return GST_PAD_LINK_OK;
190     }
191     else {
192       GstPad *otherpad;
193       
194       otherpad = (pad == identity->srcpad ? identity->sinkpad : identity->srcpad);
195
196       return gst_pad_try_set_caps (otherpad, caps);
197     }
198   }
199   else
200     return GST_PAD_LINK_DELAYED;
201 }
202
203 static void 
204 gst_identity_init (GstIdentity *identity) 
205 {
206   identity->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
207   gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
208   gst_pad_set_chain_function (identity->sinkpad, GST_DEBUG_FUNCPTR (gst_identity_chain));
209   gst_pad_set_link_function (identity->sinkpad, gst_identity_link);
210   gst_pad_set_getcaps_function (identity->sinkpad, gst_identity_getcaps);
211   
212   identity->srcpad = gst_pad_new ("src", GST_PAD_SRC);
213   gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
214   gst_pad_set_link_function (identity->srcpad, gst_identity_link);
215   gst_pad_set_getcaps_function (identity->srcpad, gst_identity_getcaps);
216
217   identity->loop_based = FALSE;
218   identity->sleep_time = 0;
219   identity->duplicate = 1;
220   identity->error_after = -1;
221   identity->drop_probability = 0.0;
222   identity->silent = FALSE;
223   identity->dump = FALSE;
224   identity->last_message = NULL;
225   identity->delay_capsnego = FALSE;
226   identity->srccaps = NULL;
227 }
228
229 static void 
230 gst_identity_chain (GstPad *pad, GstData *_data) 
231 {
232   GstBuffer *buf = GST_BUFFER (_data);
233   GstIdentity *identity;
234   guint i;
235
236   g_return_if_fail (pad != NULL);
237   g_return_if_fail (GST_IS_PAD (pad));
238   g_return_if_fail (buf != NULL);
239
240   identity = GST_IDENTITY (gst_pad_get_parent (pad));
241
242   if (identity->delay_capsnego && identity->srccaps) {
243     if (gst_pad_try_set_caps (identity->srcpad, identity->srccaps) <= 0) {
244       if (!gst_pad_recover_caps_error (identity->srcpad, identity->srccaps)) {
245         gst_buffer_unref (buf);
246         return;
247       }
248     }
249     identity->srccaps = NULL;
250   }
251
252   if (identity->error_after >= 0) {
253     identity->error_after--;
254     if (identity->error_after == 0) {
255       gst_buffer_unref (buf);
256       gst_element_error (GST_ELEMENT (identity), "errored after iterations as requested");
257       return;
258     }
259   }
260
261   if (identity->drop_probability > 0.0) {
262     if ((gfloat)(1.0*rand()/(RAND_MAX)) < identity->drop_probability) {
263       if (identity->last_message != NULL) {
264         g_free (identity->last_message);
265       }
266       identity->last_message = g_strdup_printf ("dropping   ******* (%s:%s)i (%d bytes, %"
267                                                 G_GINT64_FORMAT ")",
268               GST_DEBUG_PAD_NAME (identity->sinkpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
269       g_object_notify (G_OBJECT (identity), "last-message");
270       gst_buffer_unref (buf);
271       return;
272     }
273   }
274   if (identity->dump) {
275     gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
276   }
277
278   for (i = identity->duplicate; i; i--) {
279     if (!identity->silent) {
280       g_free (identity->last_message);
281       identity->last_message = g_strdup_printf ("chain   ******* (%s:%s)i (%d bytes, %"
282                                                 G_GINT64_FORMAT ")",
283               GST_DEBUG_PAD_NAME (identity->sinkpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
284       g_object_notify (G_OBJECT (identity), "last-message");
285     }
286
287     g_signal_emit (G_OBJECT (identity), gst_identity_signals[SIGNAL_HANDOFF], 0,
288                                buf);
289
290     if (i>1) 
291       gst_buffer_ref (buf);
292
293     gst_pad_push (identity->srcpad, GST_DATA (buf));
294
295     if (identity->sleep_time)
296       g_usleep (identity->sleep_time);
297   }
298 }
299
300 static void 
301 gst_identity_loop (GstElement *element) 
302 {
303   GstIdentity *identity;
304   GstBuffer *buf;
305
306   g_return_if_fail (element != NULL);
307   g_return_if_fail (GST_IS_IDENTITY (element));
308
309   identity = GST_IDENTITY (element);
310   
311   buf = GST_BUFFER (gst_pad_pull (identity->sinkpad));
312   if (GST_IS_EVENT (buf)) {
313     GstEvent *event = GST_EVENT (buf);
314
315     if (GST_EVENT_IS_INTERRUPT (event)) {
316       gst_event_unref (event);
317     }
318     else {
319       gst_pad_event_default (identity->sinkpad, event);
320     }
321   }
322   else {
323     gst_identity_chain (identity->sinkpad, GST_DATA (buf));
324   }
325 }
326
327 static void 
328 gst_identity_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
329 {
330   GstIdentity *identity;
331
332   /* it's not null if we got it, but it might not be ours */
333   g_return_if_fail (GST_IS_IDENTITY (object));
334   
335   identity = GST_IDENTITY (object);
336
337   switch (prop_id) {
338     case ARG_LOOP_BASED:
339       identity->loop_based = g_value_get_boolean (value);
340       if (identity->loop_based) {
341         gst_element_set_loop_function (GST_ELEMENT (identity), gst_identity_loop);
342         gst_pad_set_chain_function (identity->sinkpad, NULL);
343       }
344       else {
345         gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
346         gst_element_set_loop_function (GST_ELEMENT (identity), NULL);
347       }
348       break;
349     case ARG_SLEEP_TIME:
350       identity->sleep_time = g_value_get_uint (value);
351       break;
352     case ARG_SILENT:
353       identity->silent = g_value_get_boolean (value);
354       break;
355     case ARG_DUPLICATE:
356       identity->duplicate = g_value_get_uint (value);
357       break;
358     case ARG_DUMP:
359       identity->dump = g_value_get_boolean (value);
360       break;
361     case ARG_DELAY_CAPSNEGO:
362       identity->delay_capsnego = g_value_get_boolean (value);
363       break;
364     case ARG_ERROR_AFTER:
365       identity->error_after = g_value_get_int (value);
366       break;
367     case ARG_DROP_PROBABILITY:
368       identity->drop_probability = g_value_get_float (value);
369       break;
370     default:
371       break;
372   }
373 }
374
375 static void gst_identity_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
376   GstIdentity *identity;
377
378   /* it's not null if we got it, but it might not be ours */
379   g_return_if_fail (GST_IS_IDENTITY (object));
380   
381   identity = GST_IDENTITY (object);
382
383   switch (prop_id) {
384     case ARG_LOOP_BASED:
385       g_value_set_boolean (value, identity->loop_based);
386       break;
387     case ARG_SLEEP_TIME:
388       g_value_set_uint (value, identity->sleep_time);
389       break;
390     case ARG_DUPLICATE:
391       g_value_set_uint (value, identity->duplicate);
392       break;
393     case ARG_ERROR_AFTER:
394       g_value_set_int (value, identity->error_after);
395       break;
396     case ARG_DROP_PROBABILITY:
397       g_value_set_float (value, identity->drop_probability);
398       break;
399     case ARG_SILENT:
400       g_value_set_boolean (value, identity->silent);
401       break;
402     case ARG_DUMP:
403       g_value_set_boolean (value, identity->dump);
404       break;
405     case ARG_DELAY_CAPSNEGO:
406       g_value_set_boolean (value, identity->delay_capsnego);
407       break;
408     case ARG_LAST_MESSAGE:
409       g_value_set_string (value, identity->last_message);
410       break;
411     default:
412       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
413       break;
414   }
415 }