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