Port gtk-doc comments to their equivalent markdown syntax
[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  *                    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., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 /**
24  * SECTION:element-identity
25  * @title: identity
26  *
27  * Dummy element that passes incoming data through unmodified. It has some
28  * useful diagnostic functions, such as offset and timestamp checking.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "gstelements_private.h"
39 #include "../../gst/gst-i18n-lib.h"
40 #include "gstidentity.h"
41
42 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS_ANY);
46
47 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
48     GST_PAD_SRC,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS_ANY);
51
52 GST_DEBUG_CATEGORY_STATIC (gst_identity_debug);
53 #define GST_CAT_DEFAULT gst_identity_debug
54
55 /* Identity signals and args */
56 enum
57 {
58   SIGNAL_HANDOFF,
59   /* FILL ME */
60   LAST_SIGNAL
61 };
62
63 #define DEFAULT_SLEEP_TIME              0
64 #define DEFAULT_DUPLICATE               1
65 #define DEFAULT_ERROR_AFTER             -1
66 #define DEFAULT_DROP_PROBABILITY        0.0
67 #define DEFAULT_DROP_BUFFER_FLAGS       0
68 #define DEFAULT_DATARATE                0
69 #define DEFAULT_SILENT                  TRUE
70 #define DEFAULT_SINGLE_SEGMENT          FALSE
71 #define DEFAULT_DUMP                    FALSE
72 #define DEFAULT_SYNC                    FALSE
73 #define DEFAULT_CHECK_IMPERFECT_TIMESTAMP FALSE
74 #define DEFAULT_CHECK_IMPERFECT_OFFSET    FALSE
75 #define DEFAULT_SIGNAL_HANDOFFS           TRUE
76 #define DEFAULT_TS_OFFSET               0
77
78 enum
79 {
80   PROP_0,
81   PROP_SLEEP_TIME,
82   PROP_ERROR_AFTER,
83   PROP_DROP_PROBABILITY,
84   PROP_DROP_BUFFER_FLAGS,
85   PROP_DATARATE,
86   PROP_SILENT,
87   PROP_SINGLE_SEGMENT,
88   PROP_LAST_MESSAGE,
89   PROP_DUMP,
90   PROP_SYNC,
91   PROP_TS_OFFSET,
92   PROP_CHECK_IMPERFECT_TIMESTAMP,
93   PROP_CHECK_IMPERFECT_OFFSET,
94   PROP_SIGNAL_HANDOFFS
95 };
96
97
98 #define _do_init \
99     GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element");
100 #define gst_identity_parent_class parent_class
101 G_DEFINE_TYPE_WITH_CODE (GstIdentity, gst_identity, GST_TYPE_BASE_TRANSFORM,
102     _do_init);
103
104 static void gst_identity_finalize (GObject * object);
105 static void gst_identity_set_property (GObject * object, guint prop_id,
106     const GValue * value, GParamSpec * pspec);
107 static void gst_identity_get_property (GObject * object, guint prop_id,
108     GValue * value, GParamSpec * pspec);
109
110 static gboolean gst_identity_sink_event (GstBaseTransform * trans,
111     GstEvent * event);
112 static GstFlowReturn gst_identity_transform_ip (GstBaseTransform * trans,
113     GstBuffer * buf);
114 static gboolean gst_identity_start (GstBaseTransform * trans);
115 static gboolean gst_identity_stop (GstBaseTransform * trans);
116 static GstStateChangeReturn gst_identity_change_state (GstElement * element,
117     GstStateChange transition);
118 static gboolean gst_identity_accept_caps (GstBaseTransform * base,
119     GstPadDirection direction, GstCaps * caps);
120 static gboolean gst_identity_query (GstBaseTransform * base,
121     GstPadDirection direction, GstQuery * query);
122
123 static guint gst_identity_signals[LAST_SIGNAL] = { 0 };
124
125 static GParamSpec *pspec_last_message = NULL;
126
127 static void
128 gst_identity_finalize (GObject * object)
129 {
130   GstIdentity *identity;
131
132   identity = GST_IDENTITY (object);
133
134   g_free (identity->last_message);
135   g_cond_clear (&identity->blocked_cond);
136
137   G_OBJECT_CLASS (parent_class)->finalize (object);
138 }
139
140 static void
141 gst_identity_class_init (GstIdentityClass * klass)
142 {
143   GObjectClass *gobject_class;
144   GstElementClass *gstelement_class;
145   GstBaseTransformClass *gstbasetrans_class;
146
147   gobject_class = G_OBJECT_CLASS (klass);
148   gstelement_class = GST_ELEMENT_CLASS (klass);
149   gstbasetrans_class = GST_BASE_TRANSFORM_CLASS (klass);
150
151   gobject_class->set_property = gst_identity_set_property;
152   gobject_class->get_property = gst_identity_get_property;
153
154   g_object_class_install_property (gobject_class, PROP_SLEEP_TIME,
155       g_param_spec_uint ("sleep-time", "Sleep time",
156           "Microseconds to sleep between processing", 0, G_MAXUINT,
157           DEFAULT_SLEEP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158   g_object_class_install_property (gobject_class, PROP_ERROR_AFTER,
159       g_param_spec_int ("error-after", "Error After", "Error after N buffers",
160           G_MININT, G_MAXINT, DEFAULT_ERROR_AFTER,
161           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162   g_object_class_install_property (gobject_class, PROP_DROP_PROBABILITY,
163       g_param_spec_float ("drop-probability", "Drop Probability",
164           "The Probability a buffer is dropped", 0.0, 1.0,
165           DEFAULT_DROP_PROBABILITY,
166           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167
168   /**
169    * GstIdentity:drop-buffer-flags:
170    *
171    * Drop buffers with the given flags.
172    *
173    * Since: 1.8
174    **/
175   g_object_class_install_property (gobject_class, PROP_DROP_BUFFER_FLAGS,
176       g_param_spec_flags ("drop-buffer-flags", "Check flags to drop buffers",
177           "Drop buffers with the given flags",
178           GST_TYPE_BUFFER_FLAGS, DEFAULT_DROP_BUFFER_FLAGS,
179           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180   g_object_class_install_property (gobject_class, PROP_DATARATE,
181       g_param_spec_int ("datarate", "Datarate",
182           "(Re)timestamps buffers with number of bytes per second (0 = inactive)",
183           0, G_MAXINT, DEFAULT_DATARATE,
184           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185   g_object_class_install_property (gobject_class, PROP_SILENT,
186       g_param_spec_boolean ("silent", "silent", "silent", DEFAULT_SILENT,
187           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188   g_object_class_install_property (gobject_class, PROP_SINGLE_SEGMENT,
189       g_param_spec_boolean ("single-segment", "Single Segment",
190           "Timestamp buffers and eat segments so as to appear as one segment",
191           DEFAULT_SINGLE_SEGMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192   pspec_last_message = g_param_spec_string ("last-message", "last-message",
193       "last-message", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
194   g_object_class_install_property (gobject_class, PROP_LAST_MESSAGE,
195       pspec_last_message);
196   g_object_class_install_property (gobject_class, PROP_DUMP,
197       g_param_spec_boolean ("dump", "Dump", "Dump buffer contents to stdout",
198           DEFAULT_DUMP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199   g_object_class_install_property (gobject_class, PROP_SYNC,
200       g_param_spec_boolean ("sync", "Synchronize",
201           "Synchronize to pipeline clock", DEFAULT_SYNC,
202           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
204       g_param_spec_int64 ("ts-offset", "Timestamp offset for synchronisation",
205           "Timestamp offset in nanoseconds for synchronisation, negative for earlier sync",
206           G_MININT64, G_MAXINT64, DEFAULT_TS_OFFSET,
207           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208   g_object_class_install_property (gobject_class,
209       PROP_CHECK_IMPERFECT_TIMESTAMP,
210       g_param_spec_boolean ("check-imperfect-timestamp",
211           "Check for discontiguous timestamps",
212           "Send element messages if timestamps and durations do not match up",
213           DEFAULT_CHECK_IMPERFECT_TIMESTAMP,
214           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215   g_object_class_install_property (gobject_class, PROP_CHECK_IMPERFECT_OFFSET,
216       g_param_spec_boolean ("check-imperfect-offset",
217           "Check for discontiguous offset",
218           "Send element messages if offset and offset_end do not match up",
219           DEFAULT_CHECK_IMPERFECT_OFFSET,
220           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221
222   /**
223    * GstIdentity:signal-handoffs
224    *
225    * If set to #TRUE, the identity will emit a handoff signal when handling a buffer.
226    * When set to #FALSE, no signal will be emitted, which might improve performance.
227    */
228   g_object_class_install_property (gobject_class, PROP_SIGNAL_HANDOFFS,
229       g_param_spec_boolean ("signal-handoffs",
230           "Signal handoffs", "Send a signal before pushing the buffer",
231           DEFAULT_SIGNAL_HANDOFFS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232
233   /**
234    * GstIdentity::handoff:
235    * @identity: the identity instance
236    * @buffer: the buffer that just has been received
237    * @pad: the pad that received it
238    *
239    * This signal gets emitted before passing the buffer downstream.
240    */
241   gst_identity_signals[SIGNAL_HANDOFF] =
242       g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
243       G_STRUCT_OFFSET (GstIdentityClass, handoff), NULL, NULL,
244       g_cclosure_marshal_generic, G_TYPE_NONE, 1,
245       GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
246
247   gobject_class->finalize = gst_identity_finalize;
248
249   gst_element_class_set_static_metadata (gstelement_class,
250       "Identity",
251       "Generic",
252       "Pass data without modification", "Erik Walthinsen <omega@cse.ogi.edu>");
253   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
254   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
255
256   gstelement_class->change_state =
257       GST_DEBUG_FUNCPTR (gst_identity_change_state);
258
259   gstbasetrans_class->sink_event = GST_DEBUG_FUNCPTR (gst_identity_sink_event);
260   gstbasetrans_class->transform_ip =
261       GST_DEBUG_FUNCPTR (gst_identity_transform_ip);
262   gstbasetrans_class->start = GST_DEBUG_FUNCPTR (gst_identity_start);
263   gstbasetrans_class->stop = GST_DEBUG_FUNCPTR (gst_identity_stop);
264   gstbasetrans_class->accept_caps =
265       GST_DEBUG_FUNCPTR (gst_identity_accept_caps);
266   gstbasetrans_class->query = gst_identity_query;
267 }
268
269 static void
270 gst_identity_init (GstIdentity * identity)
271 {
272   identity->sleep_time = DEFAULT_SLEEP_TIME;
273   identity->error_after = DEFAULT_ERROR_AFTER;
274   identity->drop_probability = DEFAULT_DROP_PROBABILITY;
275   identity->drop_buffer_flags = DEFAULT_DROP_BUFFER_FLAGS;
276   identity->datarate = DEFAULT_DATARATE;
277   identity->silent = DEFAULT_SILENT;
278   identity->single_segment = DEFAULT_SINGLE_SEGMENT;
279   identity->sync = DEFAULT_SYNC;
280   identity->check_imperfect_timestamp = DEFAULT_CHECK_IMPERFECT_TIMESTAMP;
281   identity->check_imperfect_offset = DEFAULT_CHECK_IMPERFECT_OFFSET;
282   identity->dump = DEFAULT_DUMP;
283   identity->last_message = NULL;
284   identity->signal_handoffs = DEFAULT_SIGNAL_HANDOFFS;
285   identity->ts_offset = DEFAULT_TS_OFFSET;
286   g_cond_init (&identity->blocked_cond);
287
288   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM_CAST (identity), TRUE);
289 }
290
291 static void
292 gst_identity_notify_last_message (GstIdentity * identity)
293 {
294   g_object_notify_by_pspec ((GObject *) identity, pspec_last_message);
295 }
296
297 static GstFlowReturn
298 gst_identity_do_sync (GstIdentity * identity, GstClockTime running_time)
299 {
300   GstFlowReturn ret = GST_FLOW_OK;
301
302   if (identity->sync &&
303       GST_BASE_TRANSFORM_CAST (identity)->segment.format == GST_FORMAT_TIME) {
304     GstClock *clock;
305
306     GST_OBJECT_LOCK (identity);
307
308     while (identity->blocked)
309       g_cond_wait (&identity->blocked_cond, GST_OBJECT_GET_LOCK (identity));
310
311
312     if ((clock = GST_ELEMENT (identity)->clock)) {
313       GstClockReturn cret;
314       GstClockTime timestamp;
315       GstClockTimeDiff ts_offset = identity->ts_offset;
316
317       timestamp = running_time + GST_ELEMENT (identity)->base_time +
318           identity->upstream_latency;
319       if (ts_offset < 0) {
320         ts_offset = -ts_offset;
321         if (ts_offset < timestamp)
322           timestamp -= ts_offset;
323         else
324           timestamp = 0;
325       } else
326         timestamp += ts_offset;
327
328       /* save id if we need to unlock */
329       identity->clock_id = gst_clock_new_single_shot_id (clock, timestamp);
330       GST_OBJECT_UNLOCK (identity);
331
332       cret = gst_clock_id_wait (identity->clock_id, NULL);
333
334       GST_OBJECT_LOCK (identity);
335       if (identity->clock_id) {
336         gst_clock_id_unref (identity->clock_id);
337         identity->clock_id = NULL;
338       }
339       if (cret == GST_CLOCK_UNSCHEDULED)
340         ret = GST_FLOW_EOS;
341     }
342     GST_OBJECT_UNLOCK (identity);
343   }
344
345   return ret;
346 }
347
348 static gboolean
349 gst_identity_sink_event (GstBaseTransform * trans, GstEvent * event)
350 {
351   GstIdentity *identity;
352   gboolean ret = TRUE;
353
354   identity = GST_IDENTITY (trans);
355
356   if (!identity->silent) {
357     const GstStructure *s;
358     const gchar *tstr;
359     gchar *sstr;
360
361     GST_OBJECT_LOCK (identity);
362     g_free (identity->last_message);
363
364     tstr = gst_event_type_get_name (GST_EVENT_TYPE (event));
365     if ((s = gst_event_get_structure (event)))
366       sstr = gst_structure_to_string (s);
367     else
368       sstr = g_strdup ("");
369
370     identity->last_message =
371         g_strdup_printf ("event   ******* (%s:%s) E (type: %s (%d), %s) %p",
372         GST_DEBUG_PAD_NAME (trans->sinkpad), tstr, GST_EVENT_TYPE (event),
373         sstr, event);
374     g_free (sstr);
375     GST_OBJECT_UNLOCK (identity);
376
377     gst_identity_notify_last_message (identity);
378   }
379
380   if (identity->single_segment && (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT)) {
381     if (!trans->have_segment) {
382       GstEvent *news;
383       GstSegment segment;
384
385       gst_event_copy_segment (event, &segment);
386       gst_event_copy_segment (event, &trans->segment);
387       trans->have_segment = TRUE;
388
389       /* This is the first segment, send out a (0, -1) segment */
390       gst_segment_init (&segment, segment.format);
391       news = gst_event_new_segment (&segment);
392
393       gst_pad_event_default (trans->sinkpad, GST_OBJECT_CAST (trans), news);
394     } else {
395       /* need to track segment for proper running time */
396       gst_event_copy_segment (event, &trans->segment);
397     }
398   }
399
400   if (GST_EVENT_TYPE (event) == GST_EVENT_GAP &&
401       trans->have_segment && trans->segment.format == GST_FORMAT_TIME) {
402     GstClockTime start, dur;
403
404     gst_event_parse_gap (event, &start, &dur);
405     if (GST_CLOCK_TIME_IS_VALID (start)) {
406       start = gst_segment_to_running_time (&trans->segment,
407           GST_FORMAT_TIME, start);
408
409       gst_identity_do_sync (identity, start);
410
411       /* also transform GAP timestamp similar to buffer timestamps */
412       if (identity->single_segment) {
413         gst_event_unref (event);
414         event = gst_event_new_gap (start, dur);
415       }
416     }
417   }
418
419   /* Reset previous timestamp, duration and offsets on SEGMENT
420    * to prevent false warnings when checking for perfect streams */
421   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
422     identity->prev_timestamp = identity->prev_duration = GST_CLOCK_TIME_NONE;
423     identity->prev_offset = identity->prev_offset_end = GST_BUFFER_OFFSET_NONE;
424   }
425
426   if (identity->single_segment && GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
427     /* eat up segments */
428     gst_event_unref (event);
429     ret = TRUE;
430   } else {
431     if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
432       GST_OBJECT_LOCK (identity);
433       if (identity->clock_id) {
434         GST_DEBUG_OBJECT (identity, "unlock clock wait");
435         gst_clock_id_unschedule (identity->clock_id);
436       }
437       GST_OBJECT_UNLOCK (identity);
438     }
439
440     ret = GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
441   }
442
443   return ret;
444 }
445
446 static void
447 gst_identity_check_imperfect_timestamp (GstIdentity * identity, GstBuffer * buf)
448 {
449   GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buf);
450
451   /* invalid timestamp drops us out of check.  FIXME: maybe warn ? */
452   if (timestamp != GST_CLOCK_TIME_NONE) {
453     /* check if we had a previous buffer to compare to */
454     if (identity->prev_timestamp != GST_CLOCK_TIME_NONE &&
455         identity->prev_duration != GST_CLOCK_TIME_NONE) {
456       GstClockTime t_expected;
457       GstClockTimeDiff dt;
458
459       t_expected = identity->prev_timestamp + identity->prev_duration;
460       dt = GST_CLOCK_DIFF (t_expected, timestamp);
461       if (dt != 0) {
462         /*
463          * "imperfect-timestamp" bus message:
464          * @identity:        the identity instance
465          * @delta:           the GST_CLOCK_DIFF to the prev timestamp
466          * @prev-timestamp:  the previous buffer timestamp
467          * @prev-duration:   the previous buffer duration
468          * @prev-offset:     the previous buffer offset
469          * @prev-offset-end: the previous buffer offset end
470          * @cur-timestamp:   the current buffer timestamp
471          * @cur-duration:    the current buffer duration
472          * @cur-offset:      the current buffer offset
473          * @cur-offset-end:  the current buffer offset end
474          *
475          * This bus message gets emitted if the check-imperfect-timestamp
476          * property is set and there is a gap in time between the
477          * last buffer and the newly received buffer.
478          */
479         gst_element_post_message (GST_ELEMENT (identity),
480             gst_message_new_element (GST_OBJECT (identity),
481                 gst_structure_new ("imperfect-timestamp",
482                     "delta", G_TYPE_INT64, dt,
483                     "prev-timestamp", G_TYPE_UINT64,
484                     identity->prev_timestamp, "prev-duration", G_TYPE_UINT64,
485                     identity->prev_duration, "prev-offset", G_TYPE_UINT64,
486                     identity->prev_offset, "prev-offset-end", G_TYPE_UINT64,
487                     identity->prev_offset_end, "cur-timestamp", G_TYPE_UINT64,
488                     timestamp, "cur-duration", G_TYPE_UINT64,
489                     GST_BUFFER_DURATION (buf), "cur-offset", G_TYPE_UINT64,
490                     GST_BUFFER_OFFSET (buf), "cur-offset-end", G_TYPE_UINT64,
491                     GST_BUFFER_OFFSET_END (buf), NULL)));
492       }
493     } else {
494       GST_DEBUG_OBJECT (identity, "can't check data-contiguity, no "
495           "offset_end was set on previous buffer");
496     }
497   }
498 }
499
500 static void
501 gst_identity_check_imperfect_offset (GstIdentity * identity, GstBuffer * buf)
502 {
503   guint64 offset;
504
505   offset = GST_BUFFER_OFFSET (buf);
506
507   if (identity->prev_offset_end != offset &&
508       identity->prev_offset_end != GST_BUFFER_OFFSET_NONE &&
509       offset != GST_BUFFER_OFFSET_NONE) {
510     /*
511      * "imperfect-offset" bus message:
512      * @identity:        the identity instance
513      * @prev-timestamp:  the previous buffer timestamp
514      * @prev-duration:   the previous buffer duration
515      * @prev-offset:     the previous buffer offset
516      * @prev-offset-end: the previous buffer offset end
517      * @cur-timestamp:   the current buffer timestamp
518      * @cur-duration:    the current buffer duration
519      * @cur-offset:      the current buffer offset
520      * @cur-offset-end:  the current buffer offset end
521      *
522      * This bus message gets emitted if the check-imperfect-offset
523      * property is set and there is a gap in offsets between the
524      * last buffer and the newly received buffer.
525      */
526     gst_element_post_message (GST_ELEMENT (identity),
527         gst_message_new_element (GST_OBJECT (identity),
528             gst_structure_new ("imperfect-offset", "prev-timestamp",
529                 G_TYPE_UINT64, identity->prev_timestamp, "prev-duration",
530                 G_TYPE_UINT64, identity->prev_duration, "prev-offset",
531                 G_TYPE_UINT64, identity->prev_offset, "prev-offset-end",
532                 G_TYPE_UINT64, identity->prev_offset_end, "cur-timestamp",
533                 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (buf), "cur-duration",
534                 G_TYPE_UINT64, GST_BUFFER_DURATION (buf), "cur-offset",
535                 G_TYPE_UINT64, GST_BUFFER_OFFSET (buf), "cur-offset-end",
536                 G_TYPE_UINT64, GST_BUFFER_OFFSET_END (buf), NULL)));
537   } else {
538     GST_DEBUG_OBJECT (identity, "can't check offset contiguity, no offset "
539         "and/or offset_end were set on previous buffer");
540   }
541 }
542
543 static const gchar *
544 print_pretty_time (gchar * ts_str, gsize ts_str_len, GstClockTime ts)
545 {
546   if (ts == GST_CLOCK_TIME_NONE)
547     return "none";
548
549   g_snprintf (ts_str, ts_str_len, "%" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
550   return ts_str;
551 }
552
553 static void
554 gst_identity_update_last_message_for_buffer (GstIdentity * identity,
555     const gchar * action, GstBuffer * buf, gsize size)
556 {
557   gchar dts_str[64], pts_str[64], dur_str[64];
558   gchar *flag_str, *meta_str;
559
560   GST_OBJECT_LOCK (identity);
561
562   flag_str = gst_buffer_get_flags_string (buf);
563   meta_str = gst_buffer_get_meta_string (buf);
564
565   g_free (identity->last_message);
566   identity->last_message = g_strdup_printf ("%s   ******* (%s:%s) "
567       "(%" G_GSIZE_FORMAT " bytes, dts: %s, pts: %s, duration: %s, offset: %"
568       G_GINT64_FORMAT ", " "offset_end: % " G_GINT64_FORMAT
569       ", flags: %08x %s, meta: %s) %p", action,
570       GST_DEBUG_PAD_NAME (GST_BASE_TRANSFORM_CAST (identity)->sinkpad), size,
571       print_pretty_time (dts_str, sizeof (dts_str), GST_BUFFER_DTS (buf)),
572       print_pretty_time (pts_str, sizeof (pts_str), GST_BUFFER_PTS (buf)),
573       print_pretty_time (dur_str, sizeof (dur_str), GST_BUFFER_DURATION (buf)),
574       GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf),
575       GST_BUFFER_FLAGS (buf), flag_str, meta_str ? meta_str : "none", buf);
576   g_free (flag_str);
577
578   GST_OBJECT_UNLOCK (identity);
579
580   gst_identity_notify_last_message (identity);
581 }
582
583 static GstFlowReturn
584 gst_identity_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
585 {
586   GstFlowReturn ret = GST_FLOW_OK;
587   GstIdentity *identity = GST_IDENTITY (trans);
588   GstClockTime rundts = GST_CLOCK_TIME_NONE;
589   GstClockTime runpts = GST_CLOCK_TIME_NONE;
590   GstClockTime ts, duration, runtimestamp;
591   gsize size;
592
593   size = gst_buffer_get_size (buf);
594
595   if (identity->check_imperfect_timestamp)
596     gst_identity_check_imperfect_timestamp (identity, buf);
597   if (identity->check_imperfect_offset)
598     gst_identity_check_imperfect_offset (identity, buf);
599
600   /* update prev values */
601   identity->prev_timestamp = GST_BUFFER_TIMESTAMP (buf);
602   identity->prev_duration = GST_BUFFER_DURATION (buf);
603   identity->prev_offset_end = GST_BUFFER_OFFSET_END (buf);
604   identity->prev_offset = GST_BUFFER_OFFSET (buf);
605
606   if (identity->error_after >= 0) {
607     identity->error_after--;
608     if (identity->error_after == 0)
609       goto error_after;
610   }
611
612   if (identity->drop_probability > 0.0) {
613     if ((gfloat) (1.0 * rand () / (RAND_MAX)) < identity->drop_probability)
614       goto dropped;
615   }
616
617   if (GST_BUFFER_FLAG_IS_SET (buf, identity->drop_buffer_flags))
618     goto dropped;
619
620   if (identity->dump) {
621     GstMapInfo info;
622
623     if (gst_buffer_map (buf, &info, GST_MAP_READ)) {
624       gst_util_dump_mem (info.data, info.size);
625       gst_buffer_unmap (buf, &info);
626     }
627   }
628
629   if (!identity->silent) {
630     gst_identity_update_last_message_for_buffer (identity, "chain", buf, size);
631   }
632
633   if (identity->datarate > 0) {
634     GstClockTime time = gst_util_uint64_scale_int (identity->offset,
635         GST_SECOND, identity->datarate);
636
637     GST_BUFFER_PTS (buf) = GST_BUFFER_DTS (buf) = time;
638     GST_BUFFER_DURATION (buf) = size * GST_SECOND / identity->datarate;
639   }
640
641   if (identity->signal_handoffs)
642     g_signal_emit (identity, gst_identity_signals[SIGNAL_HANDOFF], 0, buf);
643
644   if (trans->segment.format == GST_FORMAT_TIME) {
645     rundts = gst_segment_to_running_time (&trans->segment,
646         GST_FORMAT_TIME, GST_BUFFER_DTS (buf));
647     runpts = gst_segment_to_running_time (&trans->segment,
648         GST_FORMAT_TIME, GST_BUFFER_PTS (buf));
649   }
650
651   if (GST_CLOCK_TIME_IS_VALID (rundts))
652     runtimestamp = rundts;
653   else if (GST_CLOCK_TIME_IS_VALID (runpts))
654     runtimestamp = runpts;
655   else
656     runtimestamp = 0;
657   ret = gst_identity_do_sync (identity, runtimestamp);
658
659   identity->offset += size;
660
661   if (identity->sleep_time && ret == GST_FLOW_OK)
662     g_usleep (identity->sleep_time);
663
664   if (identity->single_segment && (trans->segment.format == GST_FORMAT_TIME)
665       && (ret == GST_FLOW_OK)) {
666     GST_BUFFER_DTS (buf) = rundts;
667     GST_BUFFER_PTS (buf) = runpts;
668     GST_BUFFER_OFFSET (buf) = GST_CLOCK_TIME_NONE;
669     GST_BUFFER_OFFSET_END (buf) = GST_CLOCK_TIME_NONE;
670   }
671
672   return ret;
673
674   /* ERRORS */
675 error_after:
676   {
677     GST_ELEMENT_ERROR (identity, CORE, FAILED,
678         (_("Failed after iterations as requested.")), (NULL));
679     return GST_FLOW_ERROR;
680   }
681 dropped:
682   {
683     if (!identity->silent) {
684       gst_identity_update_last_message_for_buffer (identity, "dropping", buf,
685           size);
686     }
687
688     ts = GST_BUFFER_TIMESTAMP (buf);
689     if (GST_CLOCK_TIME_IS_VALID (ts)) {
690       duration = GST_BUFFER_DURATION (buf);
691       gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (identity),
692           gst_event_new_gap (ts, duration));
693     }
694
695     /* return DROPPED to basetransform. */
696     return GST_BASE_TRANSFORM_FLOW_DROPPED;
697   }
698 }
699
700 static void
701 gst_identity_set_property (GObject * object, guint prop_id,
702     const GValue * value, GParamSpec * pspec)
703 {
704   GstIdentity *identity;
705
706   identity = GST_IDENTITY (object);
707
708   switch (prop_id) {
709     case PROP_SLEEP_TIME:
710       identity->sleep_time = g_value_get_uint (value);
711       break;
712     case PROP_SILENT:
713       identity->silent = g_value_get_boolean (value);
714       break;
715     case PROP_SINGLE_SEGMENT:
716       identity->single_segment = g_value_get_boolean (value);
717       break;
718     case PROP_DUMP:
719       identity->dump = g_value_get_boolean (value);
720       break;
721     case PROP_ERROR_AFTER:
722       identity->error_after = g_value_get_int (value);
723       break;
724     case PROP_DROP_PROBABILITY:
725       identity->drop_probability = g_value_get_float (value);
726       break;
727     case PROP_DROP_BUFFER_FLAGS:
728       identity->drop_buffer_flags = g_value_get_flags (value);
729       break;
730     case PROP_DATARATE:
731       identity->datarate = g_value_get_int (value);
732       break;
733     case PROP_SYNC:
734       identity->sync = g_value_get_boolean (value);
735       break;
736     case PROP_TS_OFFSET:
737       identity->ts_offset = g_value_get_int64 (value);
738       break;
739     case PROP_CHECK_IMPERFECT_TIMESTAMP:
740       identity->check_imperfect_timestamp = g_value_get_boolean (value);
741       break;
742     case PROP_CHECK_IMPERFECT_OFFSET:
743       identity->check_imperfect_offset = g_value_get_boolean (value);
744       break;
745     case PROP_SIGNAL_HANDOFFS:
746       identity->signal_handoffs = g_value_get_boolean (value);
747       break;
748     default:
749       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
750       break;
751   }
752   if (identity->datarate > 0 || identity->single_segment)
753     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (identity), FALSE);
754   else
755     gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (identity), TRUE);
756 }
757
758 static void
759 gst_identity_get_property (GObject * object, guint prop_id, GValue * value,
760     GParamSpec * pspec)
761 {
762   GstIdentity *identity;
763
764   identity = GST_IDENTITY (object);
765
766   switch (prop_id) {
767     case PROP_SLEEP_TIME:
768       g_value_set_uint (value, identity->sleep_time);
769       break;
770     case PROP_ERROR_AFTER:
771       g_value_set_int (value, identity->error_after);
772       break;
773     case PROP_DROP_PROBABILITY:
774       g_value_set_float (value, identity->drop_probability);
775       break;
776     case PROP_DROP_BUFFER_FLAGS:
777       g_value_set_flags (value, identity->drop_buffer_flags);
778       break;
779     case PROP_DATARATE:
780       g_value_set_int (value, identity->datarate);
781       break;
782     case PROP_SILENT:
783       g_value_set_boolean (value, identity->silent);
784       break;
785     case PROP_SINGLE_SEGMENT:
786       g_value_set_boolean (value, identity->single_segment);
787       break;
788     case PROP_DUMP:
789       g_value_set_boolean (value, identity->dump);
790       break;
791     case PROP_LAST_MESSAGE:
792       GST_OBJECT_LOCK (identity);
793       g_value_set_string (value, identity->last_message);
794       GST_OBJECT_UNLOCK (identity);
795       break;
796     case PROP_SYNC:
797       g_value_set_boolean (value, identity->sync);
798       break;
799     case PROP_TS_OFFSET:
800       identity->ts_offset = g_value_get_int64 (value);
801       break;
802     case PROP_CHECK_IMPERFECT_TIMESTAMP:
803       g_value_set_boolean (value, identity->check_imperfect_timestamp);
804       break;
805     case PROP_CHECK_IMPERFECT_OFFSET:
806       g_value_set_boolean (value, identity->check_imperfect_offset);
807       break;
808     case PROP_SIGNAL_HANDOFFS:
809       g_value_set_boolean (value, identity->signal_handoffs);
810       break;
811     default:
812       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
813       break;
814   }
815 }
816
817 static gboolean
818 gst_identity_start (GstBaseTransform * trans)
819 {
820   GstIdentity *identity;
821
822   identity = GST_IDENTITY (trans);
823
824   identity->offset = 0;
825   identity->prev_timestamp = GST_CLOCK_TIME_NONE;
826   identity->prev_duration = GST_CLOCK_TIME_NONE;
827   identity->prev_offset_end = GST_BUFFER_OFFSET_NONE;
828   identity->prev_offset = GST_BUFFER_OFFSET_NONE;
829
830   return TRUE;
831 }
832
833 static gboolean
834 gst_identity_stop (GstBaseTransform * trans)
835 {
836   GstIdentity *identity;
837
838   identity = GST_IDENTITY (trans);
839
840   GST_OBJECT_LOCK (identity);
841   g_free (identity->last_message);
842   identity->last_message = NULL;
843   GST_OBJECT_UNLOCK (identity);
844
845   return TRUE;
846 }
847
848 static gboolean
849 gst_identity_accept_caps (GstBaseTransform * base,
850     GstPadDirection direction, GstCaps * caps)
851 {
852   gboolean ret;
853   GstPad *pad;
854
855   /* Proxy accept-caps */
856
857   if (direction == GST_PAD_SRC)
858     pad = GST_BASE_TRANSFORM_SINK_PAD (base);
859   else
860     pad = GST_BASE_TRANSFORM_SRC_PAD (base);
861
862   ret = gst_pad_peer_query_accept_caps (pad, caps);
863
864   return ret;
865 }
866
867 static gboolean
868 gst_identity_query (GstBaseTransform * base, GstPadDirection direction,
869     GstQuery * query)
870 {
871   GstIdentity *identity;
872   gboolean ret;
873
874   identity = GST_IDENTITY (base);
875
876   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->query (base, direction, query);
877
878   if (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY) {
879     gboolean live = FALSE;
880     GstClockTime min = 0, max = 0;
881
882     if (ret) {
883       gst_query_parse_latency (query, &live, &min, &max);
884
885       if (identity->sync && max < min) {
886         GST_ELEMENT_WARNING (base, CORE, CLOCK, (NULL),
887             ("Impossible to configure latency before identity sync=true:"
888                 " max %" GST_TIME_FORMAT " < min %"
889                 GST_TIME_FORMAT ". Add queues or other buffering elements.",
890                 GST_TIME_ARGS (max), GST_TIME_ARGS (min)));
891       }
892     }
893
894     /* Ignore the upstream latency if it is not live */
895     GST_OBJECT_LOCK (identity);
896     if (live)
897       identity->upstream_latency = min;
898     else
899       identity->upstream_latency = 0;
900     GST_OBJECT_UNLOCK (identity);
901
902     gst_query_set_latency (query, live || identity->sync, min, max);
903     ret = TRUE;
904   }
905   return ret;
906 }
907
908 static GstStateChangeReturn
909 gst_identity_change_state (GstElement * element, GstStateChange transition)
910 {
911   GstStateChangeReturn ret;
912   GstIdentity *identity = GST_IDENTITY (element);
913   gboolean no_preroll = FALSE;
914
915   switch (transition) {
916     case GST_STATE_CHANGE_NULL_TO_READY:
917       break;
918     case GST_STATE_CHANGE_READY_TO_PAUSED:
919       GST_OBJECT_LOCK (identity);
920       identity->blocked = TRUE;
921       GST_OBJECT_UNLOCK (identity);
922       if (identity->sync)
923         no_preroll = TRUE;
924       break;
925     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
926       GST_OBJECT_LOCK (identity);
927       identity->blocked = FALSE;
928       g_cond_broadcast (&identity->blocked_cond);
929       GST_OBJECT_UNLOCK (identity);
930       break;
931     case GST_STATE_CHANGE_PAUSED_TO_READY:
932       GST_OBJECT_LOCK (identity);
933       if (identity->clock_id) {
934         GST_DEBUG_OBJECT (identity, "unlock clock wait");
935         gst_clock_id_unschedule (identity->clock_id);
936       }
937       identity->blocked = FALSE;
938       g_cond_broadcast (&identity->blocked_cond);
939       GST_OBJECT_UNLOCK (identity);
940       break;
941     default:
942       break;
943   }
944
945   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
946
947   switch (transition) {
948     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
949       GST_OBJECT_LOCK (identity);
950       identity->upstream_latency = 0;
951       identity->blocked = TRUE;
952       GST_OBJECT_UNLOCK (identity);
953       if (identity->sync)
954         no_preroll = TRUE;
955       break;
956     case GST_STATE_CHANGE_PAUSED_TO_READY:
957       break;
958     case GST_STATE_CHANGE_READY_TO_NULL:
959       break;
960     default:
961       break;
962   }
963
964   if (no_preroll && ret == GST_STATE_CHANGE_SUCCESS)
965     ret = GST_STATE_CHANGE_NO_PREROLL;
966
967   return ret;
968 }