]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - plugins/elements/gstidentity.c
992953334861fd3c466d52b77ab0ea80f5530354
[glsdk/gstreamer0-10.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  *                    2005 Wim Taymans <wim@fluendo.com>
5  *
6  * gstidentity.c:
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /**
24  * SECTION:element-identity
25  *
26  * Dummy element that passes incomming data through unmodified. It has some
27  * useful diagnostic functions, such as offset and timestamp checking.
28  */
30 #ifdef HAVE_CONFIG_H
31 #  include "config.h"
32 #endif
34 #include <stdlib.h>
36 #include "../../gst/gst-i18n-lib.h"
37 #include "gstidentity.h"
38 #include <gst/gstmarshal.h>
40 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
41     GST_PAD_SINK,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS_ANY);
45 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
46     GST_PAD_SRC,
47     GST_PAD_ALWAYS,
48     GST_STATIC_CAPS_ANY);
50 GST_DEBUG_CATEGORY_STATIC (gst_identity_debug);
51 #define GST_CAT_DEFAULT gst_identity_debug
53 /* Identity signals and args */
54 enum
55 {
56   SIGNAL_HANDOFF,
57   /* FILL ME */
58   LAST_SIGNAL
59 };
61 #define DEFAULT_SLEEP_TIME              0
62 #define DEFAULT_DUPLICATE               1
63 #define DEFAULT_ERROR_AFTER             -1
64 #define DEFAULT_DROP_PROBABILITY        0.0
65 #define DEFAULT_DATARATE                0
66 #define DEFAULT_SILENT                  FALSE
67 #define DEFAULT_SINGLE_SEGMENT          FALSE
68 #define DEFAULT_DUMP                    FALSE
69 #define DEFAULT_SYNC                    FALSE
70 #define DEFAULT_CHECK_PERFECT           FALSE
71 #define DEFAULT_CHECK_IMPERFECT_TIMESTAMP FALSE
72 #define DEFAULT_CHECK_IMPERFECT_OFFSET    FALSE
73 #define DEFAULT_SIGNAL_HANDOFFS           TRUE
75 enum
76 {
77   PROP_0,
78   PROP_SLEEP_TIME,
79   PROP_ERROR_AFTER,
80   PROP_DROP_PROBABILITY,
81   PROP_DATARATE,
82   PROP_SILENT,
83   PROP_SINGLE_SEGMENT,
84   PROP_LAST_MESSAGE,
85   PROP_DUMP,
86   PROP_SYNC,
87   PROP_CHECK_PERFECT,
88   PROP_CHECK_IMPERFECT_TIMESTAMP,
89   PROP_CHECK_IMPERFECT_OFFSET,
90   PROP_SIGNAL_HANDOFFS
91 };
94 #define _do_init(bla) \
95     GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element");
97 GST_BOILERPLATE_FULL (GstIdentity, gst_identity, GstBaseTransform,
98     GST_TYPE_BASE_TRANSFORM, _do_init);
100 static void gst_identity_finalize (GObject * object);
101 static void gst_identity_set_property (GObject * object, guint prop_id,
102     const GValue * value, GParamSpec * pspec);
103 static void gst_identity_get_property (GObject * object, guint prop_id,
104     GValue * value, GParamSpec * pspec);
106 static gboolean gst_identity_event (GstBaseTransform * trans, GstEvent * event);
107 static GstFlowReturn gst_identity_transform_ip (GstBaseTransform * trans,
108     GstBuffer * buf);
109 static GstFlowReturn gst_identity_prepare_output_buffer (GstBaseTransform
110     * trans, GstBuffer * in_buf, gint out_size, GstCaps * out_caps,
111     GstBuffer ** out_buf);
112 static gboolean gst_identity_start (GstBaseTransform * trans);
113 static gboolean gst_identity_stop (GstBaseTransform * trans);
115 static guint gst_identity_signals[LAST_SIGNAL] = { 0 };
117 static GParamSpec *pspec_last_message = NULL;
119 static void
120 gst_identity_base_init (gpointer g_class)
122   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
124   gst_element_class_set_details_simple (gstelement_class,
125       "Identity",
126       "Generic",
127       "Pass data without modification", "Erik Walthinsen <omega@cse.ogi.edu>");
128   gst_element_class_add_pad_template (gstelement_class,
129       gst_static_pad_template_get (&srctemplate));
130   gst_element_class_add_pad_template (gstelement_class,
131       gst_static_pad_template_get (&sinktemplate));
134 static void
135 gst_identity_finalize (GObject * object)
137   GstIdentity *identity;
139   identity = GST_IDENTITY (object);
141   g_free (identity->last_message);
143 #if !GLIB_CHECK_VERSION(2,26,0)
144   g_static_rec_mutex_free (&identity->notify_lock);
145 #endif
147   G_OBJECT_CLASS (parent_class)->finalize (object);
150 /* fixme: do something about this */
151 static void
152 marshal_VOID__MINIOBJECT (GClosure * closure, GValue * return_value,
153     guint n_param_values, const GValue * param_values, gpointer invocation_hint,
154     gpointer marshal_data)
156   typedef void (*marshalfunc_VOID__MINIOBJECT) (gpointer obj, gpointer arg1,
157       gpointer data2);
158   register marshalfunc_VOID__MINIOBJECT callback;
159   register GCClosure *cc = (GCClosure *) closure;
160   register gpointer data1, data2;
162   g_return_if_fail (n_param_values == 2);
164   if (G_CCLOSURE_SWAP_DATA (closure)) {
165     data1 = closure->data;
166     data2 = g_value_peek_pointer (param_values + 0);
167   } else {
168     data1 = g_value_peek_pointer (param_values + 0);
169     data2 = closure->data;
170   }
171   callback =
172       (marshalfunc_VOID__MINIOBJECT) (marshal_data ? marshal_data :
173       cc->callback);
175   callback (data1, gst_value_get_mini_object (param_values + 1), data2);
178 static void
179 gst_identity_class_init (GstIdentityClass * klass)
181   GObjectClass *gobject_class;
182   GstBaseTransformClass *gstbasetrans_class;
184   gobject_class = G_OBJECT_CLASS (klass);
185   gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
187   gobject_class->set_property = gst_identity_set_property;
188   gobject_class->get_property = gst_identity_get_property;
190   g_object_class_install_property (gobject_class, PROP_SLEEP_TIME,
191       g_param_spec_uint ("sleep-time", "Sleep time",
192           "Microseconds to sleep between processing", 0, G_MAXUINT,
193           DEFAULT_SLEEP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194   g_object_class_install_property (gobject_class, PROP_ERROR_AFTER,
195       g_param_spec_int ("error-after", "Error After", "Error after N buffers",
196           G_MININT, G_MAXINT, DEFAULT_ERROR_AFTER,
197           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198   g_object_class_install_property (gobject_class, PROP_DROP_PROBABILITY,
199       g_param_spec_float ("drop-probability", "Drop Probability",
200           "The Probability a buffer is dropped", 0.0, 1.0,
201           DEFAULT_DROP_PROBABILITY,
202           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203   g_object_class_install_property (gobject_class, PROP_DATARATE,
204       g_param_spec_int ("datarate", "Datarate",
205           "(Re)timestamps buffers with number of bytes per second (0 = inactive)",
206           0, G_MAXINT, DEFAULT_DATARATE,
207           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208   g_object_class_install_property (gobject_class, PROP_SILENT,
209       g_param_spec_boolean ("silent", "silent", "silent", DEFAULT_SILENT,
210           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
211   g_object_class_install_property (gobject_class, PROP_SINGLE_SEGMENT,
212       g_param_spec_boolean ("single-segment", "Single Segment",
213           "Timestamp buffers and eat newsegments so as to appear as one segment",
214           DEFAULT_SINGLE_SEGMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215   pspec_last_message = g_param_spec_string ("last-message", "last-message",
216       "last-message", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
217   g_object_class_install_property (gobject_class, PROP_LAST_MESSAGE,
218       pspec_last_message);
219   g_object_class_install_property (gobject_class, PROP_DUMP,
220       g_param_spec_boolean ("dump", "Dump", "Dump buffer contents to stdout",
221           DEFAULT_DUMP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222   g_object_class_install_property (gobject_class, PROP_SYNC,
223       g_param_spec_boolean ("sync", "Synchronize",
224           "Synchronize to pipeline clock", DEFAULT_SYNC,
225           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
226   g_object_class_install_property (gobject_class, PROP_CHECK_PERFECT,
227       g_param_spec_boolean ("check-perfect", "Check For Perfect Stream",
228           "Verify that the stream is time- and data-contiguous. "
229           "This only logs in the debug log.  This will be deprecated in favor "
230           "of the check-imperfect-timestamp/offset properties.",
231           DEFAULT_CHECK_PERFECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232   g_object_class_install_property (gobject_class,
233       PROP_CHECK_IMPERFECT_TIMESTAMP,
234       g_param_spec_boolean ("check-imperfect-timestamp",
235           "Check for discontiguous timestamps",
236           "Send element messages if timestamps and durations do not match up",
237           DEFAULT_CHECK_IMPERFECT_TIMESTAMP,
238           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
239   g_object_class_install_property (gobject_class, PROP_CHECK_IMPERFECT_OFFSET,
240       g_param_spec_boolean ("check-imperfect-offset",
241           "Check for discontiguous offset",
242           "Send element messages if offset and offset_end do not match up",
243           DEFAULT_CHECK_IMPERFECT_OFFSET,
244           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
246   /**
247    * GstIdentity:signal-handoffs
248    *
249    * If set to #TRUE, the identity will emit a handoff signal when handling a buffer.
250    * When set to #FALSE, no signal will be emited, which might improve performance.
251    *
252    * Since: 0.10.16
253    */
254   g_object_class_install_property (gobject_class, PROP_SIGNAL_HANDOFFS,
255       g_param_spec_boolean ("signal-handoffs",
256           "Signal handoffs", "Send a signal before pushing the buffer",
257           DEFAULT_SIGNAL_HANDOFFS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259   /**
260    * GstIdentity::handoff:
261    * @identity: the identity instance
262    * @buffer: the buffer that just has been received
263    * @pad: the pad that received it
264    *
265    * This signal gets emitted before passing the buffer downstream.
266    */
267   gst_identity_signals[SIGNAL_HANDOFF] =
268       g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
269       G_STRUCT_OFFSET (GstIdentityClass, handoff), NULL, NULL,
270       marshal_VOID__MINIOBJECT, G_TYPE_NONE, 1, GST_TYPE_BUFFER);
272   gobject_class->finalize = gst_identity_finalize;
274   gstbasetrans_class->event = GST_DEBUG_FUNCPTR (gst_identity_event);
275   gstbasetrans_class->transform_ip =
276       GST_DEBUG_FUNCPTR (gst_identity_transform_ip);
277   gstbasetrans_class->prepare_output_buffer =
278       GST_DEBUG_FUNCPTR (gst_identity_prepare_output_buffer);
279   gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_identity_start);
280   gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_identity_stop);
283 static void
284 gst_identity_init (GstIdentity * identity, GstIdentityClass * g_class)
286   identity->sleep_time = DEFAULT_SLEEP_TIME;
287   identity->error_after = DEFAULT_ERROR_AFTER;
288   identity->drop_probability = DEFAULT_DROP_PROBABILITY;
289   identity->datarate = DEFAULT_DATARATE;
290   identity->silent = DEFAULT_SILENT;
291   identity->single_segment = DEFAULT_SINGLE_SEGMENT;
292   identity->sync = DEFAULT_SYNC;
293   identity->check_perfect = DEFAULT_CHECK_PERFECT;
294   identity->check_imperfect_timestamp = DEFAULT_CHECK_IMPERFECT_TIMESTAMP;
295   identity->check_imperfect_offset = DEFAULT_CHECK_IMPERFECT_OFFSET;
296   identity->dump = DEFAULT_DUMP;
297   identity->last_message = NULL;
298   identity->signal_handoffs = DEFAULT_SIGNAL_HANDOFFS;
300 #if !GLIB_CHECK_VERSION(2,26,0)
301   g_static_rec_mutex_init (&identity->notify_lock);
302 #endif
304   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM_CAST (identity), TRUE);
307 static void
308 gst_identity_notify_last_message (GstIdentity * identity)
310   /* FIXME: this hacks around a bug in GLib/GObject: doing concurrent
311    * g_object_notify() on the same object might lead to crashes, see
312    * http://bugzilla.gnome.org/show_bug.cgi?id=166020#c60 and follow-ups.
313    * So we really don't want to do a g_object_notify() here for out-of-band
314    * events with the streaming thread possibly also doing a g_object_notify()
315    * for an in-band buffer or event. This is fixed in GLib >= 2.26 */
316 #if !GLIB_CHECK_VERSION(2,26,0)
317   g_static_rec_mutex_lock (&identity->notify_lock);
318   g_object_notify ((GObject *) identity, "last-message");
319   g_static_rec_mutex_unlock (&identity->notify_lock);
320 #else
321   g_object_notify_by_pspec ((GObject *) identity, pspec_last_message);
322 #endif
325 static gboolean
326 gst_identity_event (GstBaseTransform * trans, GstEvent * event)
328   GstIdentity *identity;
329   gboolean ret = TRUE;
331   identity = GST_IDENTITY (trans);
333   if (!identity->silent) {
334     const GstStructure *s;
335     gchar *sstr;
337     GST_OBJECT_LOCK (identity);
338     g_free (identity->last_message);
340     if ((s = gst_event_get_structure (event)))
341       sstr = gst_structure_to_string (s);
342     else
343       sstr = g_strdup ("");
345     identity->last_message =
346         g_strdup_printf ("event   ******* (%s:%s) E (type: %d, %s) %p",
347         GST_DEBUG_PAD_NAME (trans->sinkpad), GST_EVENT_TYPE (event), sstr,
348         event);
349     g_free (sstr);
350     GST_OBJECT_UNLOCK (identity);
352     gst_identity_notify_last_message (identity);
353   }
355   if (identity->single_segment
356       && (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)) {
357     if (trans->have_newsegment == FALSE) {
358       GstEvent *news;
359       GstFormat format;
361       gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
362           NULL);
364       /* This is the first newsegment, send out a (0, -1) newsegment */
365       news = gst_event_new_new_segment (TRUE, 1.0, format, 0, -1, 0);
367       gst_pad_event_default (trans->sinkpad, news);
368     }
369   }
371   /* Reset previous timestamp, duration and offsets on NEWSEGMENT
372    * to prevent false warnings when checking for perfect streams */
373   if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
374     identity->prev_timestamp = identity->prev_duration = GST_CLOCK_TIME_NONE;
375     identity->prev_offset = identity->prev_offset_end = GST_BUFFER_OFFSET_NONE;
376   }
378   ret = parent_class->event (trans, event);
380   if (identity->single_segment
381       && (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)) {
382     /* eat up segments */
383     ret = FALSE;
384   }
386   return ret;
389 static GstFlowReturn
390 gst_identity_prepare_output_buffer (GstBaseTransform * trans,
391     GstBuffer * in_buf, gint out_size, GstCaps * out_caps, GstBuffer ** out_buf)
393   GstIdentity *identity = GST_IDENTITY (trans);
395   /* only bother if we may have to alter metadata */
396   if (identity->datarate > 0 || identity->single_segment) {
397     if (gst_buffer_is_metadata_writable (in_buf))
398       *out_buf = gst_buffer_ref (in_buf);
399     else {
400       /* make even less writable */
401       gst_buffer_ref (in_buf);
402       /* extra ref is dropped going through the official process */
403       *out_buf = gst_buffer_make_metadata_writable (in_buf);
404     }
405   } else
406     *out_buf = gst_buffer_ref (in_buf);
408   return GST_FLOW_OK;
411 static void
412 gst_identity_check_perfect (GstIdentity * identity, GstBuffer * buf)
414   GstClockTime timestamp;
416   timestamp = GST_BUFFER_TIMESTAMP (buf);
418   /* see if we need to do perfect stream checking */
419   /* invalid timestamp drops us out of check.  FIXME: maybe warn ? */
420   if (timestamp != GST_CLOCK_TIME_NONE) {
421     /* check if we had a previous buffer to compare to */
422     if (identity->prev_timestamp != GST_CLOCK_TIME_NONE &&
423         identity->prev_duration != GST_CLOCK_TIME_NONE) {
424       guint64 offset, t_expected;
425       gint64 dt;
427       t_expected = identity->prev_timestamp + identity->prev_duration;
428       dt = timestamp - t_expected;
429       if (dt != 0) {
430         GST_WARNING_OBJECT (identity,
431             "Buffer not time-contiguous with previous one: " "prev ts %"
432             GST_TIME_FORMAT ", prev dur %" GST_TIME_FORMAT ", new ts %"
433             GST_TIME_FORMAT " (expected ts %" GST_TIME_FORMAT ", delta=%c%"
434             GST_TIME_FORMAT ")", GST_TIME_ARGS (identity->prev_timestamp),
435             GST_TIME_ARGS (identity->prev_duration), GST_TIME_ARGS (timestamp),
436             GST_TIME_ARGS (t_expected), (dt < 0) ? '-' : '+',
437             GST_TIME_ARGS ((dt < 0) ? (GstClockTime) (-dt) : dt));
438       }
440       offset = GST_BUFFER_OFFSET (buf);
441       if (identity->prev_offset_end != offset &&
442           identity->prev_offset_end != GST_BUFFER_OFFSET_NONE &&
443           offset != GST_BUFFER_OFFSET_NONE) {
444         GST_WARNING_OBJECT (identity,
445             "Buffer not data-contiguous with previous one: "
446             "prev offset_end %" G_GINT64_FORMAT ", new offset %"
447             G_GINT64_FORMAT, identity->prev_offset_end, offset);
448       }
449     } else {
450       GST_DEBUG_OBJECT (identity, "can't check time-contiguity, no timestamp "
451           "and/or duration were set on previous buffer");
452     }
453   }
456 static void
457 gst_identity_check_imperfect_timestamp (GstIdentity * identity, GstBuffer * buf)
459   GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buf);
461   /* invalid timestamp drops us out of check.  FIXME: maybe warn ? */
462   if (timestamp != GST_CLOCK_TIME_NONE) {
463     /* check if we had a previous buffer to compare to */
464     if (identity->prev_timestamp != GST_CLOCK_TIME_NONE &&
465         identity->prev_duration != GST_CLOCK_TIME_NONE) {
466       GstClockTime t_expected;
467       GstClockTimeDiff dt;
469       t_expected = identity->prev_timestamp + identity->prev_duration;
470       dt = GST_CLOCK_DIFF (t_expected, timestamp);
471       if (dt != 0) {
472         /*
473          * "imperfect-timestamp" bus message:
474          * @identity:        the identity instance
475          * @prev-timestamp:  the previous buffer timestamp
476          * @prev-duration:   the previous buffer duration
477          * @prev-offset:     the previous buffer offset
478          * @prev-offset-end: the previous buffer offset end
479          * @cur-timestamp:   the current buffer timestamp
480          * @cur-duration:    the current buffer duration
481          * @cur-offset:      the current buffer offset
482          * @cur-offset-end:  the current buffer offset end
483          *
484          * This bus message gets emitted if the check-imperfect-timestamp
485          * property is set and there is a gap in time between the
486          * last buffer and the newly received buffer.
487          */
488         gst_element_post_message (GST_ELEMENT (identity),
489             gst_message_new_element (GST_OBJECT (identity),
490                 gst_structure_new ("imperfect-timestamp",
491                     "prev-timestamp", G_TYPE_UINT64,
492                     identity->prev_timestamp, "prev-duration", G_TYPE_UINT64,
493                     identity->prev_duration, "prev-offset", G_TYPE_UINT64,
494                     identity->prev_offset, "prev-offset-end", G_TYPE_UINT64,
495                     identity->prev_offset_end, "cur-timestamp", G_TYPE_UINT64,
496                     timestamp, "cur-duration", G_TYPE_UINT64,
497                     GST_BUFFER_DURATION (buf), "cur-offset", G_TYPE_UINT64,
498                     GST_BUFFER_OFFSET (buf), "cur-offset-end", G_TYPE_UINT64,
499                     GST_BUFFER_OFFSET_END (buf), NULL)));
500       }
501     } else {
502       GST_DEBUG_OBJECT (identity, "can't check data-contiguity, no "
503           "offset_end was set on previous buffer");
504     }
505   }
508 static void
509 gst_identity_check_imperfect_offset (GstIdentity * identity, GstBuffer * buf)
511   guint64 offset;
513   offset = GST_BUFFER_OFFSET (buf);
515   if (identity->prev_offset_end != offset &&
516       identity->prev_offset_end != GST_BUFFER_OFFSET_NONE &&
517       offset != GST_BUFFER_OFFSET_NONE) {
518     /*
519      * "imperfect-offset" bus message:
520      * @identity:        the identity instance
521      * @prev-timestamp:  the previous buffer timestamp
522      * @prev-duration:   the previous buffer duration
523      * @prev-offset:     the previous buffer offset
524      * @prev-offset-end: the previous buffer offset end
525      * @cur-timestamp:   the current buffer timestamp
526      * @cur-duration:    the current buffer duration
527      * @cur-offset:      the current buffer offset
528      * @cur-offset-end:  the current buffer offset end
529      *
530      * This bus message gets emitted if the check-imperfect-offset
531      * property is set and there is a gap in offsets between the
532      * last buffer and the newly received buffer.
533      */
534     gst_element_post_message (GST_ELEMENT (identity),
535         gst_message_new_element (GST_OBJECT (identity),
536             gst_structure_new ("imperfect-offset", "prev-timestamp",
537                 G_TYPE_UINT64, identity->prev_timestamp, "prev-duration",
538                 G_TYPE_UINT64, identity->prev_duration, "prev-offset",
539                 G_TYPE_UINT64, identity->prev_offset, "prev-offset-end",
540                 G_TYPE_UINT64, identity->prev_offset_end, "cur-timestamp",
541                 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (buf), "cur-duration",
542                 G_TYPE_UINT64, GST_BUFFER_DURATION (buf), "cur-offset",
543                 G_TYPE_UINT64, GST_BUFFER_OFFSET (buf), "cur-offset-end",
544                 G_TYPE_UINT64, GST_BUFFER_OFFSET_END (buf), NULL)));
545   } else {
546     GST_DEBUG_OBJECT (identity, "can't check offset contiguity, no offset "
547         "and/or offset_end were set on previous buffer");
548   }
551 static const gchar *
552 print_pretty_time (gchar * ts_str, gsize ts_str_len, GstClockTime ts)
554   if (ts == GST_CLOCK_TIME_NONE)
555     return "none";
557   g_snprintf (ts_str, ts_str_len, "%" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
558   return ts_str;
561 static void
562 gst_identity_update_last_message_for_buffer (GstIdentity * identity,
563     const gchar * action, GstBuffer * buf)
565   gchar ts_str[64], dur_str[64];
567   GST_OBJECT_LOCK (identity);
569   g_free (identity->last_message);
570   identity->last_message = g_strdup_printf ("%s   ******* (%s:%s)i "
571       "(%u bytes, timestamp: %s, duration: %s, offset: %" G_GINT64_FORMAT ", "
572       "offset_end: % " G_GINT64_FORMAT ", flags: %d) %p", action,
573       GST_DEBUG_PAD_NAME (GST_BASE_TRANSFORM_CAST (identity)->sinkpad),
574       GST_BUFFER_SIZE (buf),
575       print_pretty_time (ts_str, sizeof (ts_str), GST_BUFFER_TIMESTAMP (buf)),
576       print_pretty_time (dur_str, sizeof (dur_str), GST_BUFFER_DURATION (buf)),
577       GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf),
578       GST_BUFFER_FLAGS (buf), buf);
580   GST_OBJECT_UNLOCK (identity);
582   gst_identity_notify_last_message (identity);
585 static GstFlowReturn
586 gst_identity_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
588   GstFlowReturn ret = GST_FLOW_OK;
589   GstIdentity *identity = GST_IDENTITY (trans);
590   GstClockTime runtimestamp = G_GINT64_CONSTANT (0);
592   if (identity->check_perfect)
593     gst_identity_check_perfect (identity, buf);
594   if (identity->check_imperfect_timestamp)
595     gst_identity_check_imperfect_timestamp (identity, buf);
596   if (identity->check_imperfect_offset)
597     gst_identity_check_imperfect_offset (identity, buf);
599   /* update prev values */
600   identity->prev_timestamp = GST_BUFFER_TIMESTAMP (buf);
601   identity->prev_duration = GST_BUFFER_DURATION (buf);
602   identity->prev_offset_end = GST_BUFFER_OFFSET_END (buf);
603   identity->prev_offset = GST_BUFFER_OFFSET (buf);
605   if (identity->error_after >= 0) {
606     identity->error_after--;
607     if (identity->error_after == 0) {
608       GST_ELEMENT_ERROR (identity, CORE, FAILED,
609           (_("Failed after iterations as requested.")), (NULL));
610       return GST_FLOW_ERROR;
611     }
612   }
614   if (identity->drop_probability > 0.0) {
615     if ((gfloat) (1.0 * rand () / (RAND_MAX)) < identity->drop_probability) {
616       if (!identity->silent) {
617         gst_identity_update_last_message_for_buffer (identity, "dropping", buf);
618       }
619       /* return DROPPED to basetransform. */
620       return GST_BASE_TRANSFORM_FLOW_DROPPED;
621     }
622   }
624   if (identity->dump) {
625     gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
626   }
628   if (!identity->silent) {
629     gst_identity_update_last_message_for_buffer (identity, "chain", buf);
630   }
632   if (identity->datarate > 0) {
633     GstClockTime time = gst_util_uint64_scale_int (identity->offset,
634         GST_SECOND, identity->datarate);
636     GST_BUFFER_TIMESTAMP (buf) = time;
637     GST_BUFFER_DURATION (buf) =
638         GST_BUFFER_SIZE (buf) * GST_SECOND / identity->datarate;
639   }
641   if (identity->signal_handoffs)
642     g_signal_emit (identity, gst_identity_signals[SIGNAL_HANDOFF], 0, buf);
644   if (trans->segment.format == GST_FORMAT_TIME)
645     runtimestamp = gst_segment_to_running_time (&trans->segment,
646         GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf));
648   if ((identity->sync) && (trans->segment.format == GST_FORMAT_TIME)) {
649     GstClock *clock;
651     GST_OBJECT_LOCK (identity);
652     if ((clock = GST_ELEMENT (identity)->clock)) {
653       GstClockReturn cret;
654       GstClockTime timestamp;
656       timestamp = runtimestamp + GST_ELEMENT (identity)->base_time;
658       /* save id if we need to unlock */
659       /* FIXME: actually unlock this somewhere in the state changes */
660       identity->clock_id = gst_clock_new_single_shot_id (clock, timestamp);
661       GST_OBJECT_UNLOCK (identity);
663       cret = gst_clock_id_wait (identity->clock_id, NULL);
665       GST_OBJECT_LOCK (identity);
666       if (identity->clock_id) {
667         gst_clock_id_unref (identity->clock_id);
668         identity->clock_id = NULL;
669       }
670       if (cret == GST_CLOCK_UNSCHEDULED)
671         ret = GST_FLOW_UNEXPECTED;
672     }
673     GST_OBJECT_UNLOCK (identity);
674   }
676   identity->offset += GST_BUFFER_SIZE (buf);
678   if (identity->sleep_time && ret == GST_FLOW_OK)
679     g_usleep (identity->sleep_time);
681   if (identity->single_segment && (trans->segment.format == GST_FORMAT_TIME)
682       && (ret == GST_FLOW_OK)) {
683     GST_BUFFER_TIMESTAMP (buf) = runtimestamp;
684     GST_BUFFER_OFFSET (buf) = GST_CLOCK_TIME_NONE;
685     GST_BUFFER_OFFSET_END (buf) = GST_CLOCK_TIME_NONE;
686   }
688   return ret;
691 static void
692 gst_identity_set_property (GObject * object, guint prop_id,
693     const GValue * value, GParamSpec * pspec)
695   GstIdentity *identity;
697   identity = GST_IDENTITY (object);
699   switch (prop_id) {
700     case PROP_SLEEP_TIME:
701       identity->sleep_time = g_value_get_uint (value);
702       break;
703     case PROP_SILENT:
704       identity->silent = g_value_get_boolean (value);
705       break;
706     case PROP_SINGLE_SEGMENT:
707       identity->single_segment = g_value_get_boolean (value);
708       break;
709     case PROP_DUMP:
710       identity->dump = g_value_get_boolean (value);
711       break;
712     case PROP_ERROR_AFTER:
713       identity->error_after = g_value_get_int (value);
714       break;
715     case PROP_DROP_PROBABILITY:
716       identity->drop_probability = g_value_get_float (value);
717       break;
718     case PROP_DATARATE:
719       identity->datarate = g_value_get_int (value);
720       break;
721     case PROP_SYNC:
722       identity->sync = g_value_get_boolean (value);
723       break;
724     case PROP_CHECK_PERFECT:
725       identity->check_perfect = g_value_get_boolean (value);
726       break;
727     case PROP_CHECK_IMPERFECT_TIMESTAMP:
728       identity->check_imperfect_timestamp = g_value_get_boolean (value);
729       break;
730     case PROP_CHECK_IMPERFECT_OFFSET:
731       identity->check_imperfect_offset = g_value_get_boolean (value);
732       break;
733     case PROP_SIGNAL_HANDOFFS:
734       identity->signal_handoffs = g_value_get_boolean (value);
735       break;
736     default:
737       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
738       break;
739   }
742 static void
743 gst_identity_get_property (GObject * object, guint prop_id, GValue * value,
744     GParamSpec * pspec)
746   GstIdentity *identity;
748   identity = GST_IDENTITY (object);
750   switch (prop_id) {
751     case PROP_SLEEP_TIME:
752       g_value_set_uint (value, identity->sleep_time);
753       break;
754     case PROP_ERROR_AFTER:
755       g_value_set_int (value, identity->error_after);
756       break;
757     case PROP_DROP_PROBABILITY:
758       g_value_set_float (value, identity->drop_probability);
759       break;
760     case PROP_DATARATE:
761       g_value_set_int (value, identity->datarate);
762       break;
763     case PROP_SILENT:
764       g_value_set_boolean (value, identity->silent);
765       break;
766     case PROP_SINGLE_SEGMENT:
767       g_value_set_boolean (value, identity->single_segment);
768       break;
769     case PROP_DUMP:
770       g_value_set_boolean (value, identity->dump);
771       break;
772     case PROP_LAST_MESSAGE:
773       GST_OBJECT_LOCK (identity);
774       g_value_set_string (value, identity->last_message);
775       GST_OBJECT_UNLOCK (identity);
776       break;
777     case PROP_SYNC:
778       g_value_set_boolean (value, identity->sync);
779       break;
780     case PROP_CHECK_PERFECT:
781       g_value_set_boolean (value, identity->check_perfect);
782       break;
783     case PROP_CHECK_IMPERFECT_TIMESTAMP:
784       g_value_set_boolean (value, identity->check_imperfect_timestamp);
785       break;
786     case PROP_CHECK_IMPERFECT_OFFSET:
787       g_value_set_boolean (value, identity->check_imperfect_offset);
788       break;
789     case PROP_SIGNAL_HANDOFFS:
790       g_value_set_boolean (value, identity->signal_handoffs);
791       break;
792     default:
793       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
794       break;
795   }
798 static gboolean
799 gst_identity_start (GstBaseTransform * trans)
801   GstIdentity *identity;
803   identity = GST_IDENTITY (trans);
805   identity->offset = 0;
806   identity->prev_timestamp = GST_CLOCK_TIME_NONE;
807   identity->prev_duration = GST_CLOCK_TIME_NONE;
808   identity->prev_offset_end = GST_BUFFER_OFFSET_NONE;
809   identity->prev_offset = GST_BUFFER_OFFSET_NONE;
811   return TRUE;
814 static gboolean
815 gst_identity_stop (GstBaseTransform * trans)
817   GstIdentity *identity;
819   identity = GST_IDENTITY (trans);
821   GST_OBJECT_LOCK (identity);
822   g_free (identity->last_message);
823   identity->last_message = NULL;
824   GST_OBJECT_UNLOCK (identity);
826   return TRUE;