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