First THREADED backport attempt, focusing on adding locks and making sure the API...
[platform/upstream/gstreamer.git] / gst / 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 "../gst-i18n-lib.h"
31 #include "gstidentity.h"
32 #include <gst/gstmarshal.h>
33
34 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS_ANY);
38
39 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
40     GST_PAD_SRC,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS_ANY);
43
44 GST_DEBUG_CATEGORY_STATIC (gst_identity_debug);
45 #define GST_CAT_DEFAULT gst_identity_debug
46
47 GstElementDetails gst_identity_details = GST_ELEMENT_DETAILS ("Identity",
48     "Generic",
49     "Pass data without modification",
50     "Erik Walthinsen <omega@cse.ogi.edu>");
51
52
53 /* Identity signals and args */
54 enum
55 {
56   SIGNAL_HANDOFF,
57   /* FILL ME */
58   LAST_SIGNAL
59 };
60
61 #define DEFAULT_LOOP_BASED              FALSE
62 #define DEFAULT_SLEEP_TIME              0
63 #define DEFAULT_DUPLICATE               1
64 #define DEFAULT_ERROR_AFTER             -1
65 #define DEFAULT_DROP_PROBABILITY        0.0
66 #define DEFAULT_DATARATE                0
67 #define DEFAULT_SILENT                  FALSE
68 #define DEFAULT_DUMP                    FALSE
69 #define DEFAULT_SYNC                    FALSE
70 #define DEFAULT_CHECK_PERFECT           FALSE
71
72 enum
73 {
74   ARG_0,
75   ARG_LOOP_BASED,
76   ARG_SLEEP_TIME,
77   ARG_DUPLICATE,
78   ARG_ERROR_AFTER,
79   ARG_DROP_PROBABILITY,
80   ARG_DATARATE,
81   ARG_SILENT,
82   ARG_LAST_MESSAGE,
83   ARG_DUMP,
84   ARG_SYNC,
85   ARG_CHECK_PERFECT
86 };
87
88
89 #define _do_init(bla) \
90     GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element");
91
92 GST_BOILERPLATE_FULL (GstIdentity, gst_identity, GstElement, GST_TYPE_ELEMENT,
93     _do_init);
94
95 static void gst_identity_finalize (GObject * object);
96 static void gst_identity_set_property (GObject * object, guint prop_id,
97     const GValue * value, GParamSpec * pspec);
98 static void gst_identity_get_property (GObject * object, guint prop_id,
99     GValue * value, GParamSpec * pspec);
100 static GstElementStateReturn gst_identity_change_state (GstElement * element);
101
102 static void gst_identity_chain (GstPad * pad, GstData * _data);
103 static void gst_identity_set_clock (GstElement * element, GstClock * clock);
104
105
106 static guint gst_identity_signals[LAST_SIGNAL] = { 0 };
107
108 static void
109 gst_identity_base_init (gpointer g_class)
110 {
111   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
112
113   gst_element_class_add_pad_template (gstelement_class,
114       gst_static_pad_template_get (&srctemplate));
115   gst_element_class_add_pad_template (gstelement_class,
116       gst_static_pad_template_get (&sinktemplate));
117   gst_element_class_set_details (gstelement_class, &gst_identity_details);
118 }
119
120 static void
121 gst_identity_finalize (GObject * object)
122 {
123   GstIdentity *identity;
124
125   identity = GST_IDENTITY (object);
126
127   g_free (identity->last_message);
128
129   G_OBJECT_CLASS (parent_class)->finalize (object);
130 }
131
132 static void
133 gst_identity_class_init (GstIdentityClass * klass)
134 {
135   GObjectClass *gobject_class;
136   GstElementClass *gstelement_class;
137
138   gobject_class = G_OBJECT_CLASS (klass);
139   gstelement_class = GST_ELEMENT_CLASS (klass);
140
141   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED,
142       g_param_spec_boolean ("loop-based", "Loop-based",
143           "Set to TRUE to use loop-based rather than chain-based scheduling",
144           DEFAULT_LOOP_BASED, G_PARAM_READWRITE));
145   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SLEEP_TIME,
146       g_param_spec_uint ("sleep-time", "Sleep time",
147           "Microseconds to sleep between processing", 0, G_MAXUINT,
148           DEFAULT_SLEEP_TIME, G_PARAM_READWRITE));
149   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUPLICATE,
150       g_param_spec_uint ("duplicate", "Duplicate Buffers",
151           "Push the buffers N times", 0, G_MAXUINT, DEFAULT_DUPLICATE,
152           G_PARAM_READWRITE));
153   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ERROR_AFTER,
154       g_param_spec_int ("error_after", "Error After", "Error after N buffers",
155           G_MININT, G_MAXINT, DEFAULT_ERROR_AFTER, G_PARAM_READWRITE));
156   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DROP_PROBABILITY,
157       g_param_spec_float ("drop_probability", "Drop Probability",
158           "The Probability a buffer is dropped", 0.0, 1.0,
159           DEFAULT_DROP_PROBABILITY, G_PARAM_READWRITE));
160   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DATARATE,
161       g_param_spec_int ("datarate", "Datarate",
162           "(Re)timestamps buffers with number of bytes per second (0 = inactive)",
163           0, G_MAXINT, DEFAULT_DATARATE, G_PARAM_READWRITE));
164   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
165       g_param_spec_boolean ("silent", "silent", "silent", DEFAULT_SILENT,
166           G_PARAM_READWRITE));
167   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
168       g_param_spec_string ("last-message", "last-message", "last-message", NULL,
169           G_PARAM_READABLE));
170   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP,
171       g_param_spec_boolean ("dump", "Dump", "Dump buffer contents",
172           DEFAULT_DUMP, G_PARAM_READWRITE));
173   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
174       g_param_spec_boolean ("sync", "Synchronize",
175           "Synchronize to pipeline clock", DEFAULT_SYNC, G_PARAM_READWRITE));
176   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHECK_PERFECT,
177       g_param_spec_boolean ("check-perfect", "Check For Perfect Stream",
178           "Verify that the stream is time- and data-contiguous",
179           DEFAULT_CHECK_PERFECT, G_PARAM_READWRITE));
180
181   gst_identity_signals[SIGNAL_HANDOFF] =
182       g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
183       G_STRUCT_OFFSET (GstIdentityClass, handoff), NULL, NULL,
184       gst_marshal_VOID__BOXED, G_TYPE_NONE, 1,
185       GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
186
187   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_identity_finalize);
188   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);
189   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
190
191   gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_identity_set_clock);
192   gstelement_class->change_state =
193       GST_DEBUG_FUNCPTR (gst_identity_change_state);
194
195 }
196
197 static void
198 gst_identity_init (GstIdentity * identity)
199 {
200   identity->sinkpad =
201       gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
202       "sink");
203   gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
204   gst_pad_set_chain_function (identity->sinkpad,
205       GST_DEBUG_FUNCPTR (gst_identity_chain));
206   //gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link);
207   gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps);
208
209   identity->srcpad =
210       gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
211       "src");
212   gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
213   //gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link);
214   gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps);
215
216   identity->loop_based = DEFAULT_LOOP_BASED;
217   identity->sleep_time = DEFAULT_SLEEP_TIME;
218   identity->duplicate = DEFAULT_DUPLICATE;
219   identity->error_after = DEFAULT_ERROR_AFTER;
220   identity->drop_probability = DEFAULT_DROP_PROBABILITY;
221   identity->datarate = DEFAULT_DATARATE;
222   identity->silent = DEFAULT_SILENT;
223   identity->sync = DEFAULT_SYNC;
224   identity->check_perfect = DEFAULT_CHECK_PERFECT;
225   identity->dump = DEFAULT_DUMP;
226   identity->last_message = NULL;
227   identity->srccaps = NULL;
228
229   GST_FLAG_SET (identity, GST_ELEMENT_EVENT_AWARE);
230 }
231
232 static void
233 gst_identity_set_clock (GstElement * element, GstClock * clock)
234 {
235   GstIdentity *identity = GST_IDENTITY (element);
236
237   gst_object_replace ((GstObject **) & identity->clock, (GstObject *) clock);
238 }
239
240
241 static void
242 gst_identity_chain (GstPad * pad, GstData * _data)
243 {
244   GstBuffer *buf = GST_BUFFER (_data);
245   GstIdentity *identity;
246   guint i;
247
248   g_return_if_fail (pad != NULL);
249   g_return_if_fail (GST_IS_PAD (pad));
250   g_return_if_fail (buf != NULL);
251
252   identity = GST_IDENTITY (gst_pad_get_parent (pad));
253
254   if (GST_IS_EVENT (buf)) {
255     GstEvent *event = GST_EVENT (buf);
256
257     if (!identity->silent) {
258       g_free (identity->last_message);
259
260       identity->last_message =
261           g_strdup_printf ("chain   ******* (%s:%s)E (type: %d) %p",
262           GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
263
264       g_object_notify (G_OBJECT (identity), "last_message");
265     }
266     gst_pad_event_default (pad, event);
267     return;
268   }
269
270   /* see if we need to do perfect stream checking */
271   /* invalid timestamp drops us out of check.  FIXME: maybe warn ? */
272   if (identity->check_perfect &&
273       GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE) {
274     /* check if we had a previous buffer to compare to */
275     if (identity->prev_timestamp != GST_CLOCK_TIME_NONE) {
276       if (identity->prev_timestamp + identity->prev_duration !=
277           GST_BUFFER_TIMESTAMP (buf)) {
278         GST_WARNING_OBJECT (identity,
279             "Buffer not time-contiguous with previous one: " "prev ts %"
280             GST_TIME_FORMAT ", prev dur %" GST_TIME_FORMAT ", new ts %"
281             GST_TIME_FORMAT, GST_TIME_ARGS (identity->prev_timestamp),
282             GST_TIME_ARGS (identity->prev_duration),
283             GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
284       }
285       if (identity->prev_offset_end != GST_BUFFER_OFFSET (buf)) {
286         GST_WARNING_OBJECT (identity,
287             "Buffer not data-contiguous with previous one: "
288             "prev offset_end %" G_GINT64_FORMAT ", new offset %"
289             G_GINT64_FORMAT, identity->prev_offset_end,
290             GST_BUFFER_OFFSET (buf));
291       }
292     }
293     /* update prev values */
294     identity->prev_timestamp = GST_BUFFER_TIMESTAMP (buf);
295     identity->prev_duration = GST_BUFFER_DURATION (buf);
296     identity->prev_offset_end = GST_BUFFER_OFFSET_END (buf);
297   }
298
299   if (identity->error_after >= 0) {
300     identity->error_after--;
301     if (identity->error_after == 0) {
302       gst_buffer_unref (buf);
303       GST_ELEMENT_ERROR (identity, CORE, FAILED,
304           (_("Failed after iterations as requested.")), (NULL));
305       return;
306     }
307   }
308
309   if (identity->drop_probability > 0.0) {
310     if ((gfloat) (1.0 * rand () / (RAND_MAX)) < identity->drop_probability) {
311       g_free (identity->last_message);
312       identity->last_message =
313           g_strdup_printf ("dropping   ******* (%s:%s)i (%d bytes, timestamp: %"
314           GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
315           G_GINT64_FORMAT ", offset_end: % " G_GINT64_FORMAT ", flags: %d) %p",
316           GST_DEBUG_PAD_NAME (identity->sinkpad), GST_BUFFER_SIZE (buf),
317           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
318           GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
319           GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
320       g_object_notify (G_OBJECT (identity), "last-message");
321       gst_buffer_unref (buf);
322       return;
323     }
324   }
325   if (identity->dump) {
326     gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
327   }
328
329   for (i = identity->duplicate; i; i--) {
330     GstClockTime time;
331
332     if (!identity->silent) {
333       g_free (identity->last_message);
334       identity->last_message =
335           g_strdup_printf ("chain   ******* (%s:%s)i (%d bytes, timestamp: %"
336           GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
337           G_GINT64_FORMAT ", offset_end: % " G_GINT64_FORMAT ", flags: %d) %p",
338           GST_DEBUG_PAD_NAME (identity->sinkpad), GST_BUFFER_SIZE (buf),
339           GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
340           GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
341           GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
342       g_object_notify (G_OBJECT (identity), "last-message");
343     }
344
345     time = GST_BUFFER_TIMESTAMP (buf);
346
347     if (identity->datarate > 0) {
348       time = identity->bytes_handled * GST_SECOND / identity->datarate;
349
350       GST_BUFFER_TIMESTAMP (buf) = time;
351       GST_BUFFER_DURATION (buf) =
352           GST_BUFFER_SIZE (buf) * GST_SECOND / identity->datarate;
353     }
354
355     g_signal_emit (G_OBJECT (identity), gst_identity_signals[SIGNAL_HANDOFF], 0,
356         buf);
357
358     if (i > 1)
359       gst_buffer_ref (buf);
360
361     if (identity->sync) {
362       if (identity->clock) {
363         gst_element_wait (GST_ELEMENT (identity), time);
364       }
365     }
366
367     identity->bytes_handled += GST_BUFFER_SIZE (buf);
368     gst_pad_push (identity->srcpad, GST_DATA (buf));
369
370     if (identity->sleep_time)
371       g_usleep (identity->sleep_time);
372   }
373 }
374
375 static void
376 gst_identity_loop (GstElement * element)
377 {
378   GstIdentity *identity;
379   GstBuffer *buf;
380
381   g_return_if_fail (element != NULL);
382   g_return_if_fail (GST_IS_IDENTITY (element));
383
384   identity = GST_IDENTITY (element);
385
386   buf = GST_BUFFER (gst_pad_pull (identity->sinkpad));
387   if (GST_IS_EVENT (buf)) {
388     GstEvent *event = GST_EVENT (buf);
389
390     if (GST_EVENT_IS_INTERRUPT (event)) {
391       gst_event_unref (event);
392     } else {
393       gst_pad_event_default (identity->sinkpad, event);
394     }
395   } else {
396     gst_identity_chain (identity->sinkpad, GST_DATA (buf));
397   }
398 }
399
400 static void
401 gst_identity_set_property (GObject * object, guint prop_id,
402     const GValue * value, GParamSpec * pspec)
403 {
404   GstIdentity *identity;
405
406   /* it's not null if we got it, but it might not be ours */
407   g_return_if_fail (GST_IS_IDENTITY (object));
408
409   identity = GST_IDENTITY (object);
410
411   switch (prop_id) {
412     case ARG_LOOP_BASED:
413       identity->loop_based = g_value_get_boolean (value);
414       if (identity->loop_based) {
415         gst_element_set_loop_function (GST_ELEMENT (identity),
416             gst_identity_loop);
417         gst_pad_set_chain_function (identity->sinkpad, NULL);
418       } else {
419         gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
420         gst_element_set_loop_function (GST_ELEMENT (identity), NULL);
421       }
422       break;
423     case ARG_SLEEP_TIME:
424       identity->sleep_time = g_value_get_uint (value);
425       break;
426     case ARG_SILENT:
427       identity->silent = g_value_get_boolean (value);
428       break;
429     case ARG_DUPLICATE:
430       identity->duplicate = g_value_get_uint (value);
431       break;
432     case ARG_DUMP:
433       identity->dump = g_value_get_boolean (value);
434       break;
435     case ARG_ERROR_AFTER:
436       identity->error_after = g_value_get_int (value);
437       break;
438     case ARG_DROP_PROBABILITY:
439       identity->drop_probability = g_value_get_float (value);
440       break;
441     case ARG_DATARATE:
442       identity->datarate = g_value_get_int (value);
443       break;
444     case ARG_SYNC:
445       identity->sync = g_value_get_boolean (value);
446       break;
447     case ARG_CHECK_PERFECT:
448       identity->check_perfect = g_value_get_boolean (value);
449       break;
450     default:
451       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
452       break;
453   }
454 }
455
456 static void
457 gst_identity_get_property (GObject * object, guint prop_id, GValue * value,
458     GParamSpec * pspec)
459 {
460   GstIdentity *identity;
461
462   /* it's not null if we got it, but it might not be ours */
463   g_return_if_fail (GST_IS_IDENTITY (object));
464
465   identity = GST_IDENTITY (object);
466
467   switch (prop_id) {
468     case ARG_LOOP_BASED:
469       g_value_set_boolean (value, identity->loop_based);
470       break;
471     case ARG_SLEEP_TIME:
472       g_value_set_uint (value, identity->sleep_time);
473       break;
474     case ARG_DUPLICATE:
475       g_value_set_uint (value, identity->duplicate);
476       break;
477     case ARG_ERROR_AFTER:
478       g_value_set_int (value, identity->error_after);
479       break;
480     case ARG_DROP_PROBABILITY:
481       g_value_set_float (value, identity->drop_probability);
482       break;
483     case ARG_DATARATE:
484       g_value_set_int (value, identity->datarate);
485       break;
486     case ARG_SILENT:
487       g_value_set_boolean (value, identity->silent);
488       break;
489     case ARG_DUMP:
490       g_value_set_boolean (value, identity->dump);
491       break;
492     case ARG_LAST_MESSAGE:
493       g_value_set_string (value, identity->last_message);
494       break;
495     case ARG_SYNC:
496       g_value_set_boolean (value, identity->sync);
497       break;
498     case ARG_CHECK_PERFECT:
499       g_value_set_boolean (value, identity->check_perfect);
500       break;
501     default:
502       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
503       break;
504   }
505 }
506
507 static GstElementStateReturn
508 gst_identity_change_state (GstElement * element)
509 {
510   GstIdentity *identity;
511
512   g_return_val_if_fail (GST_IS_IDENTITY (element), GST_STATE_FAILURE);
513
514   identity = GST_IDENTITY (element);
515
516   switch (GST_STATE_TRANSITION (element)) {
517     case GST_STATE_NULL_TO_READY:
518       break;
519     case GST_STATE_READY_TO_PAUSED:
520       identity->bytes_handled = 0;
521       identity->prev_timestamp = GST_CLOCK_TIME_NONE;
522       identity->prev_duration = GST_CLOCK_TIME_NONE;
523       identity->prev_offset_end = -1;
524       break;
525     case GST_STATE_PAUSED_TO_PLAYING:
526     case GST_STATE_PLAYING_TO_PAUSED:
527       break;
528     case GST_STATE_PAUSED_TO_READY:
529       g_free (identity->last_message);
530       identity->last_message = NULL;
531       break;
532     case GST_STATE_READY_TO_NULL:
533       break;
534     default:
535       break;
536   }
537
538   if (GST_ELEMENT_CLASS (parent_class)->change_state)
539     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
540
541   return GST_STATE_SUCCESS;
542 }