audio: change audio format syntax a little
[platform/upstream/gstreamer.git] / gst / audiorate / gstaudiorate.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-audiorate
22  * @see_also: #GstVideoRate
23  *
24  * This element takes an incoming stream of timestamped raw audio frames and
25  * produces a perfect stream by inserting or dropping samples as needed.
26  *
27  * This operation may be of use to link to elements that require or otherwise
28  * implicitly assume a perfect stream as they do not store timestamps,
29  * but derive this by some means (e.g. bitrate for some AVI cases).
30  *
31  * The properties #GstAudioRate:in, #GstAudioRate:out, #GstAudioRate:add
32  * and #GstAudioRate:drop can be read to obtain information about number of
33  * input samples, output samples, dropped samples (i.e. the number of unused
34  * input samples) and inserted samples (i.e. the number of samples added to
35  * stream).
36  *
37  * When the #GstAudioRate:silent property is set to FALSE, a GObject property
38  * notification will be emitted whenever one of the #GstAudioRate:add or
39  * #GstAudioRate:drop values changes.
40  * This can potentially cause performance degradation.
41  * Note that property notification will happen from the streaming thread, so
42  * applications should be prepared for this.
43  *
44  * If the #GstAudioRate:tolerance property is non-zero, and an incoming buffer's
45  * timestamp deviates less than the property indicates from what would make a
46  * 'perfect time', then no samples will be added or dropped.
47  * Note that the output is still guaranteed to be a perfect stream, which means
48  * that the incoming data is then simply shifted (by less than the indicated
49  * tolerance) to a perfect time.
50  *
51  * <refsect2>
52  * <title>Example pipelines</title>
53  * |[
54  * gst-launch -v alsasrc ! audiorate ! wavenc ! filesink location=alsa.wav
55  * ]| Capture audio from an ALSA device, and turn it into a perfect stream
56  * for saving in a raw audio file.
57  * </refsect2>
58  */
59
60 #ifdef HAVE_CONFIG_H
61 #include "config.h"
62 #endif
63
64 #include <string.h>
65 #include <stdlib.h>
66
67 #include "gstaudiorate.h"
68
69 #define GST_CAT_DEFAULT audio_rate_debug
70 GST_DEBUG_CATEGORY_STATIC (audio_rate_debug);
71
72 /* GstAudioRate signals and args */
73 enum
74 {
75   /* FILL ME */
76   LAST_SIGNAL
77 };
78
79 #define DEFAULT_SILENT     TRUE
80 #define DEFAULT_TOLERANCE  0
81 #define DEFAULT_SKIP_TO_FIRST FALSE
82
83 enum
84 {
85   ARG_0,
86   ARG_IN,
87   ARG_OUT,
88   ARG_ADD,
89   ARG_DROP,
90   ARG_SILENT,
91   ARG_TOLERANCE,
92   ARG_SKIP_TO_FIRST
93 };
94
95 static GstStaticPadTemplate gst_audio_rate_src_template =
96 GST_STATIC_PAD_TEMPLATE ("src",
97     GST_PAD_SRC,
98     GST_PAD_ALWAYS,
99     GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
100     );
101
102 static GstStaticPadTemplate gst_audio_rate_sink_template =
103 GST_STATIC_PAD_TEMPLATE ("sink",
104     GST_PAD_SINK,
105     GST_PAD_ALWAYS,
106     GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
107     );
108
109 static gboolean gst_audio_rate_sink_event (GstPad * pad, GstEvent * event);
110 static gboolean gst_audio_rate_src_event (GstPad * pad, GstEvent * event);
111 static GstFlowReturn gst_audio_rate_chain (GstPad * pad, GstBuffer * buf);
112
113 static void gst_audio_rate_set_property (GObject * object,
114     guint prop_id, const GValue * value, GParamSpec * pspec);
115 static void gst_audio_rate_get_property (GObject * object,
116     guint prop_id, GValue * value, GParamSpec * pspec);
117
118 static GstStateChangeReturn gst_audio_rate_change_state (GstElement * element,
119     GstStateChange transition);
120
121 /*static guint gst_audio_rate_signals[LAST_SIGNAL] = { 0 }; */
122
123 static GParamSpec *pspec_drop = NULL;
124 static GParamSpec *pspec_add = NULL;
125
126 #define gst_audio_rate_parent_class parent_class
127 G_DEFINE_TYPE (GstAudioRate, gst_audio_rate, GST_TYPE_ELEMENT);
128
129 static void
130 gst_audio_rate_class_init (GstAudioRateClass * klass)
131 {
132   GObjectClass *object_class = G_OBJECT_CLASS (klass);
133   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
134
135   object_class->set_property = gst_audio_rate_set_property;
136   object_class->get_property = gst_audio_rate_get_property;
137
138   g_object_class_install_property (object_class, ARG_IN,
139       g_param_spec_uint64 ("in", "In",
140           "Number of input samples", 0, G_MAXUINT64, 0,
141           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
142   g_object_class_install_property (object_class, ARG_OUT,
143       g_param_spec_uint64 ("out", "Out", "Number of output samples", 0,
144           G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
145   pspec_add = g_param_spec_uint64 ("add", "Add", "Number of added samples",
146       0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
147   g_object_class_install_property (object_class, ARG_ADD, pspec_add);
148   pspec_drop = g_param_spec_uint64 ("drop", "Drop", "Number of dropped samples",
149       0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
150   g_object_class_install_property (object_class, ARG_DROP, pspec_drop);
151   g_object_class_install_property (object_class, ARG_SILENT,
152       g_param_spec_boolean ("silent", "silent",
153           "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT,
154           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155   /**
156    * GstAudioRate:tolerance
157    *
158    * The difference between incoming timestamp and next timestamp must exceed
159    * the given value for audiorate to add or drop samples.
160    *
161    * Since: 0.10.26
162    **/
163   g_object_class_install_property (object_class, ARG_TOLERANCE,
164       g_param_spec_uint64 ("tolerance", "tolerance",
165           "Only act if timestamp jitter/imperfection exceeds indicated tolerance (ns)",
166           0, G_MAXUINT64, DEFAULT_TOLERANCE,
167           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
168
169   /**
170    * GstAudioRate:skip-to-first:
171    *
172    * Don't produce buffers before the first one we receive.
173    *
174    * Since: 0.10.33
175    **/
176   g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST,
177       g_param_spec_boolean ("skip-to-first", "Skip to first buffer",
178           "Don't produce buffers before the first one we receive",
179           DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180
181   gst_element_class_set_details_simple (element_class,
182       "Audio rate adjuster", "Filter/Effect/Audio",
183       "Drops/duplicates/adjusts timestamps on audio samples to make a perfect stream",
184       "Wim Taymans <wim@fluendo.com>");
185
186   gst_element_class_add_pad_template (element_class,
187       gst_static_pad_template_get (&gst_audio_rate_sink_template));
188   gst_element_class_add_pad_template (element_class,
189       gst_static_pad_template_get (&gst_audio_rate_src_template));
190
191   element_class->change_state = gst_audio_rate_change_state;
192 }
193
194 static void
195 gst_audio_rate_reset (GstAudioRate * audiorate)
196 {
197   audiorate->next_offset = -1;
198   audiorate->next_ts = -1;
199   audiorate->discont = TRUE;
200   gst_segment_init (&audiorate->sink_segment, GST_FORMAT_UNDEFINED);
201   gst_segment_init (&audiorate->src_segment, GST_FORMAT_TIME);
202
203   GST_DEBUG_OBJECT (audiorate, "handle reset");
204 }
205
206 static gboolean
207 gst_audio_rate_setcaps (GstAudioRate * audiorate, GstCaps * caps)
208 {
209   GstAudioInfo info;
210
211   if (!gst_audio_info_from_caps (&info, caps))
212     goto wrong_caps;
213
214   audiorate->info = info;
215
216   return TRUE;
217
218   /* ERRORS */
219 wrong_caps:
220   {
221     GST_DEBUG_OBJECT (audiorate, "could not parse caps");
222     return FALSE;
223   }
224 }
225
226 static void
227 gst_audio_rate_init (GstAudioRate * audiorate)
228 {
229   audiorate->sinkpad =
230       gst_pad_new_from_static_template (&gst_audio_rate_sink_template, "sink");
231   gst_pad_set_event_function (audiorate->sinkpad, gst_audio_rate_sink_event);
232   gst_pad_set_chain_function (audiorate->sinkpad, gst_audio_rate_chain);
233   gst_pad_set_getcaps_function (audiorate->sinkpad, gst_pad_proxy_getcaps);
234   gst_element_add_pad (GST_ELEMENT (audiorate), audiorate->sinkpad);
235
236   audiorate->srcpad =
237       gst_pad_new_from_static_template (&gst_audio_rate_src_template, "src");
238   gst_pad_set_event_function (audiorate->srcpad, gst_audio_rate_src_event);
239   gst_pad_set_getcaps_function (audiorate->srcpad, gst_pad_proxy_getcaps);
240   gst_element_add_pad (GST_ELEMENT (audiorate), audiorate->srcpad);
241
242   audiorate->in = 0;
243   audiorate->out = 0;
244   audiorate->drop = 0;
245   audiorate->add = 0;
246   audiorate->silent = DEFAULT_SILENT;
247   audiorate->tolerance = DEFAULT_TOLERANCE;
248 }
249
250 static void
251 gst_audio_rate_fill_to_time (GstAudioRate * audiorate, GstClockTime time)
252 {
253   GstBuffer *buf;
254
255   GST_DEBUG_OBJECT (audiorate, "next_ts: %" GST_TIME_FORMAT
256       ", filling to %" GST_TIME_FORMAT, GST_TIME_ARGS (audiorate->next_ts),
257       GST_TIME_ARGS (time));
258
259   if (!GST_CLOCK_TIME_IS_VALID (time) ||
260       !GST_CLOCK_TIME_IS_VALID (audiorate->next_ts))
261     return;
262
263   /* feed an empty buffer to chain with the given timestamp,
264    * it will take care of filling */
265   buf = gst_buffer_new ();
266   GST_BUFFER_TIMESTAMP (buf) = time;
267   gst_audio_rate_chain (audiorate->sinkpad, buf);
268 }
269
270 static gboolean
271 gst_audio_rate_sink_event (GstPad * pad, GstEvent * event)
272 {
273   gboolean res;
274   GstAudioRate *audiorate;
275
276   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
277
278   switch (GST_EVENT_TYPE (event)) {
279     case GST_EVENT_CAPS:
280     {
281       GstCaps *caps;
282
283       gst_event_parse_caps (event, &caps);
284       if ((res = gst_audio_rate_setcaps (audiorate, caps))) {
285         res = gst_pad_push_event (audiorate->srcpad, event);
286       } else {
287         gst_event_unref (event);
288       }
289       break;
290     }
291     case GST_EVENT_FLUSH_STOP:
292       GST_DEBUG_OBJECT (audiorate, "handling FLUSH_STOP");
293       gst_audio_rate_reset (audiorate);
294       res = gst_pad_push_event (audiorate->srcpad, event);
295       break;
296     case GST_EVENT_SEGMENT:
297     {
298       gst_event_copy_segment (event, &audiorate->sink_segment);
299
300       GST_DEBUG_OBJECT (audiorate, "handle NEWSEGMENT");
301 #if 0
302       /* FIXME: bad things will likely happen if rate < 0 ... */
303       if (!update) {
304         /* a new segment starts. We need to figure out what will be the next
305          * sample offset. We mark the offsets as invalid so that the _chain
306          * function will perform this calculation. */
307         gst_audio_rate_fill_to_time (audiorate, audiorate->src_segment.stop);
308 #endif
309         audiorate->next_offset = -1;
310         audiorate->next_ts = -1;
311 #if 0
312       } else {
313         gst_audio_rate_fill_to_time (audiorate, audiorate->src_segment.start);
314       }
315 #endif
316
317       GST_DEBUG_OBJECT (audiorate, "updated segment: %" GST_SEGMENT_FORMAT,
318           &audiorate->sink_segment);
319
320       if (audiorate->sink_segment.format == GST_FORMAT_TIME) {
321         /* TIME formats can be copied to src and forwarded */
322         res = gst_pad_push_event (audiorate->srcpad, event);
323         gst_segment_copy_into (&audiorate->sink_segment,
324             &audiorate->src_segment);
325       } else {
326         /* other formats will be handled in the _chain function */
327         gst_event_unref (event);
328         res = TRUE;
329       }
330       break;
331     }
332     case GST_EVENT_EOS:
333       /* Fill segment until the end */
334       if (GST_CLOCK_TIME_IS_VALID (audiorate->src_segment.stop))
335         gst_audio_rate_fill_to_time (audiorate, audiorate->src_segment.stop);
336       res = gst_pad_push_event (audiorate->srcpad, event);
337       break;
338     default:
339       res = gst_pad_push_event (audiorate->srcpad, event);
340       break;
341   }
342
343   gst_object_unref (audiorate);
344
345   return res;
346 }
347
348 static gboolean
349 gst_audio_rate_src_event (GstPad * pad, GstEvent * event)
350 {
351   gboolean res;
352   GstAudioRate *audiorate;
353
354   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
355
356   switch (GST_EVENT_TYPE (event)) {
357     default:
358       res = gst_pad_push_event (audiorate->sinkpad, event);
359       break;
360   }
361
362   gst_object_unref (audiorate);
363
364   return res;
365 }
366
367 static gboolean
368 gst_audio_rate_convert (GstAudioRate * audiorate,
369     GstFormat src_fmt, guint64 src_val, GstFormat dest_fmt, guint64 * dest_val)
370 {
371   return gst_audio_info_convert (&audiorate->info, src_fmt, src_val, dest_fmt,
372       (gint64 *) dest_val);
373 }
374
375
376 static gboolean
377 gst_audio_rate_convert_segments (GstAudioRate * audiorate)
378 {
379   GstFormat src_fmt, dst_fmt;
380
381   src_fmt = audiorate->sink_segment.format;
382   dst_fmt = audiorate->src_segment.format;
383
384 #define CONVERT_VAL(field) gst_audio_rate_convert (audiorate, \
385                 src_fmt, audiorate->sink_segment.field,       \
386                 dst_fmt, &audiorate->src_segment.field);
387
388   audiorate->sink_segment.rate = audiorate->src_segment.rate;
389   audiorate->sink_segment.flags = audiorate->src_segment.flags;
390   audiorate->sink_segment.applied_rate = audiorate->src_segment.applied_rate;
391   CONVERT_VAL (start);
392   CONVERT_VAL (stop);
393   CONVERT_VAL (time);
394   CONVERT_VAL (base);
395   CONVERT_VAL (position);
396 #undef CONVERT_VAL
397
398   return TRUE;
399 }
400
401 static void
402 gst_audio_rate_notify_drop (GstAudioRate * audiorate)
403 {
404 #if !GLIB_CHECK_VERSION(2,26,0)
405   g_object_notify ((GObject *) audiorate, "drop");
406 #else
407   g_object_notify_by_pspec ((GObject *) audiorate, pspec_drop);
408 #endif
409 }
410
411 static void
412 gst_audio_rate_notify_add (GstAudioRate * audiorate)
413 {
414 #if !GLIB_CHECK_VERSION(2,26,0)
415   g_object_notify ((GObject *) audiorate, "add");
416 #else
417   g_object_notify_by_pspec ((GObject *) audiorate, pspec_add);
418 #endif
419 }
420
421 static GstFlowReturn
422 gst_audio_rate_chain (GstPad * pad, GstBuffer * buf)
423 {
424   GstAudioRate *audiorate;
425   GstClockTime in_time;
426   guint64 in_offset, in_offset_end, in_samples;
427   guint in_size;
428   GstFlowReturn ret = GST_FLOW_OK;
429   GstClockTimeDiff diff;
430   gint rate, bpf;
431
432   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
433
434   rate = GST_AUDIO_INFO_RATE (&audiorate->info);
435   bpf = GST_AUDIO_INFO_BPF (&audiorate->info);
436
437   /* need to be negotiated now */
438   if (bpf == 0)
439     goto not_negotiated;
440
441   /* we have a new pending segment */
442   if (audiorate->next_offset == -1) {
443     gint64 pos;
444
445     /* update the TIME segment */
446     gst_audio_rate_convert_segments (audiorate);
447
448     /* first buffer, we are negotiated and we have a segment, calculate the
449      * current expected offsets based on the segment.start, which is the first
450      * media time of the segment and should match the media time of the first
451      * buffer in that segment, which is the offset expressed in DEFAULT units.
452      */
453     /* convert first timestamp of segment to sample position */
454     pos = gst_util_uint64_scale_int (audiorate->src_segment.start,
455         GST_AUDIO_INFO_RATE (&audiorate->info), GST_SECOND);
456
457     GST_DEBUG_OBJECT (audiorate, "resync to offset %" G_GINT64_FORMAT, pos);
458
459     /* resyncing is a discont */
460     audiorate->discont = TRUE;
461
462     audiorate->next_offset = pos;
463     audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
464         GST_SECOND, GST_AUDIO_INFO_RATE (&audiorate->info));
465
466     if (audiorate->skip_to_first && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
467       GST_DEBUG_OBJECT (audiorate, "but skipping to first buffer instead");
468       pos = gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf),
469           GST_AUDIO_INFO_RATE (&audiorate->info), GST_SECOND);
470       GST_DEBUG_OBJECT (audiorate, "so resync to offset %" G_GINT64_FORMAT,
471           pos);
472       audiorate->next_offset = pos;
473       audiorate->next_ts = GST_BUFFER_TIMESTAMP (buf);
474     }
475   }
476
477   audiorate->in++;
478
479   in_time = GST_BUFFER_TIMESTAMP (buf);
480   if (in_time == GST_CLOCK_TIME_NONE) {
481     GST_DEBUG_OBJECT (audiorate, "no timestamp, using expected next time");
482     in_time = audiorate->next_ts;
483   }
484
485   in_size = gst_buffer_get_size (buf);
486   in_samples = in_size / bpf;
487
488   /* calculate the buffer offset */
489   in_offset = gst_util_uint64_scale_int_round (in_time, rate, GST_SECOND);
490   in_offset_end = in_offset + in_samples;
491
492   GST_LOG_OBJECT (audiorate,
493       "in_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT
494       ", in_size:%u, in_offset:%" G_GUINT64_FORMAT ", in_offset_end:%"
495       G_GUINT64_FORMAT ", ->next_offset:%" G_GUINT64_FORMAT ", ->next_ts:%"
496       GST_TIME_FORMAT, GST_TIME_ARGS (in_time),
497       GST_TIME_ARGS (GST_FRAMES_TO_CLOCK_TIME (in_samples, rate)),
498       in_size, in_offset, in_offset_end, audiorate->next_offset,
499       GST_TIME_ARGS (audiorate->next_ts));
500
501   diff = in_time - audiorate->next_ts;
502   if (diff <= (GstClockTimeDiff) audiorate->tolerance &&
503       diff >= (GstClockTimeDiff) - audiorate->tolerance) {
504     /* buffer time close enough to expected time,
505      * so produce a perfect stream by simply 'shifting'
506      * it to next ts and offset and sending */
507     GST_LOG_OBJECT (audiorate, "within tolerance %" GST_TIME_FORMAT,
508         GST_TIME_ARGS (audiorate->tolerance));
509     /* The outgoing buffer's offset will be set to ->next_offset, we also
510      * need to adjust the offset_end value accordingly */
511     in_offset_end = audiorate->next_offset + in_samples;
512     goto send;
513   }
514
515   /* do we need to insert samples */
516   if (in_offset > audiorate->next_offset) {
517     GstBuffer *fill;
518     gint fillsize;
519     guint64 fillsamples;
520
521     /* We don't want to allocate a single unreasonably huge buffer - it might
522        be hundreds of megabytes. So, limit each output buffer to one second of
523        audio */
524     fillsamples = in_offset - audiorate->next_offset;
525
526     while (fillsamples > 0) {
527       guint64 cursamples = MIN (fillsamples, rate);
528       guint8 *data;
529
530       fillsamples -= cursamples;
531       fillsize = cursamples * bpf;
532
533       fill = gst_buffer_new_and_alloc (fillsize);
534
535       data = gst_buffer_map (fill, NULL, NULL, GST_MAP_WRITE);
536       /* FIXME, 0 might not be the silence byte for the negotiated format. */
537       memset (data, 0, fillsize);
538       gst_buffer_unmap (fill, data, fillsize);
539
540       GST_DEBUG_OBJECT (audiorate, "inserting %" G_GUINT64_FORMAT " samples",
541           cursamples);
542
543       GST_BUFFER_OFFSET (fill) = audiorate->next_offset;
544       audiorate->next_offset += cursamples;
545       GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset;
546
547       /* Use next timestamp, then calculate following timestamp based on 
548        * offset to get duration. Neccesary complexity to get 'perfect' 
549        * streams */
550       GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
551       audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
552           GST_SECOND, rate);
553       GST_BUFFER_DURATION (fill) = audiorate->next_ts -
554           GST_BUFFER_TIMESTAMP (fill);
555
556       /* we created this buffer to fill a gap */
557       GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP);
558       /* set discont if it's pending, this is mostly done for the first buffer 
559        * and after a flushing seek */
560       if (audiorate->discont) {
561         GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT);
562         audiorate->discont = FALSE;
563       }
564
565       ret = gst_pad_push (audiorate->srcpad, fill);
566       if (ret != GST_FLOW_OK)
567         goto beach;
568       audiorate->out++;
569       audiorate->add += cursamples;
570
571       if (!audiorate->silent)
572         gst_audio_rate_notify_add (audiorate);
573     }
574
575   } else if (in_offset < audiorate->next_offset) {
576     /* need to remove samples */
577     if (in_offset_end <= audiorate->next_offset) {
578       guint64 drop = in_size / bpf;
579
580       audiorate->drop += drop;
581
582       GST_DEBUG_OBJECT (audiorate, "dropping %" G_GUINT64_FORMAT " samples",
583           drop);
584
585       /* we can drop the buffer completely */
586       gst_buffer_unref (buf);
587       buf = NULL;
588
589       if (!audiorate->silent)
590         gst_audio_rate_notify_drop (audiorate);
591
592       goto beach;
593     } else {
594       guint64 truncsamples;
595       guint truncsize, leftsize;
596       GstBuffer *trunc;
597
598       /* truncate buffer */
599       truncsamples = audiorate->next_offset - in_offset;
600       truncsize = truncsamples * bpf;
601       leftsize = in_size - truncsize;
602
603       trunc =
604           gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, truncsize,
605           leftsize);
606
607       gst_buffer_unref (buf);
608       buf = trunc;
609
610       audiorate->drop += truncsamples;
611       GST_DEBUG_OBJECT (audiorate, "truncating %" G_GUINT64_FORMAT " samples",
612           truncsamples);
613
614       if (!audiorate->silent)
615         gst_audio_rate_notify_drop (audiorate);
616     }
617   }
618
619 send:
620   if (gst_buffer_get_size (buf) == 0)
621     goto beach;
622
623   /* Now calculate parameters for whichever buffer (either the original
624    * or truncated one) we're pushing. */
625   GST_BUFFER_OFFSET (buf) = audiorate->next_offset;
626   GST_BUFFER_OFFSET_END (buf) = in_offset_end;
627
628   GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts;
629   audiorate->next_ts = gst_util_uint64_scale_int (in_offset_end,
630       GST_SECOND, rate);
631   GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);
632
633   if (audiorate->discont) {
634     /* we need to output a discont buffer, do so now */
635     GST_DEBUG_OBJECT (audiorate, "marking DISCONT on output buffer");
636     buf = gst_buffer_make_writable (buf);
637     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
638     audiorate->discont = FALSE;
639   } else if (GST_BUFFER_IS_DISCONT (buf)) {
640     /* else we make everything continuous so we can safely remove the DISCONT
641      * flag from the buffer if there was one */
642     GST_DEBUG_OBJECT (audiorate, "removing DISCONT from buffer");
643     buf = gst_buffer_make_writable (buf);
644     GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
645   }
646
647   /* set last_stop on segment */
648   audiorate->src_segment.position =
649       GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
650
651   ret = gst_pad_push (audiorate->srcpad, buf);
652   buf = NULL;
653   audiorate->out++;
654
655   audiorate->next_offset = in_offset_end;
656 beach:
657
658   if (buf)
659     gst_buffer_unref (buf);
660
661   gst_object_unref (audiorate);
662
663   return ret;
664
665   /* ERRORS */
666 not_negotiated:
667   {
668     gst_buffer_unref (buf);
669
670     GST_ELEMENT_ERROR (audiorate, STREAM, FORMAT,
671         (NULL), ("pipeline error, format was not negotiated"));
672     return GST_FLOW_NOT_NEGOTIATED;
673   }
674 }
675
676 static void
677 gst_audio_rate_set_property (GObject * object,
678     guint prop_id, const GValue * value, GParamSpec * pspec)
679 {
680   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
681
682   switch (prop_id) {
683     case ARG_SILENT:
684       audiorate->silent = g_value_get_boolean (value);
685       break;
686     case ARG_TOLERANCE:
687       audiorate->tolerance = g_value_get_uint64 (value);
688       break;
689     case ARG_SKIP_TO_FIRST:
690       audiorate->skip_to_first = g_value_get_boolean (value);
691       break;
692     default:
693       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
694       break;
695   }
696 }
697
698 static void
699 gst_audio_rate_get_property (GObject * object,
700     guint prop_id, GValue * value, GParamSpec * pspec)
701 {
702   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
703
704   switch (prop_id) {
705     case ARG_IN:
706       g_value_set_uint64 (value, audiorate->in);
707       break;
708     case ARG_OUT:
709       g_value_set_uint64 (value, audiorate->out);
710       break;
711     case ARG_ADD:
712       g_value_set_uint64 (value, audiorate->add);
713       break;
714     case ARG_DROP:
715       g_value_set_uint64 (value, audiorate->drop);
716       break;
717     case ARG_SILENT:
718       g_value_set_boolean (value, audiorate->silent);
719       break;
720     case ARG_TOLERANCE:
721       g_value_set_uint64 (value, audiorate->tolerance);
722       break;
723     case ARG_SKIP_TO_FIRST:
724       g_value_set_boolean (value, audiorate->skip_to_first);
725       break;
726     default:
727       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
728       break;
729   }
730 }
731
732 static GstStateChangeReturn
733 gst_audio_rate_change_state (GstElement * element, GstStateChange transition)
734 {
735   GstAudioRate *audiorate = GST_AUDIO_RATE (element);
736
737   switch (transition) {
738     case GST_STATE_CHANGE_PAUSED_TO_READY:
739       break;
740     case GST_STATE_CHANGE_READY_TO_PAUSED:
741       audiorate->in = 0;
742       audiorate->out = 0;
743       audiorate->drop = 0;
744       audiorate->add = 0;
745       gst_audio_info_init (&audiorate->info);
746       gst_audio_rate_reset (audiorate);
747       break;
748     default:
749       break;
750   }
751
752   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
753 }
754
755 static gboolean
756 plugin_init (GstPlugin * plugin)
757 {
758   GST_DEBUG_CATEGORY_INIT (audio_rate_debug, "audiorate", 0,
759       "AudioRate stream fixer");
760
761   return gst_element_register (plugin, "audiorate", GST_RANK_NONE,
762       GST_TYPE_AUDIO_RATE);
763 }
764
765 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
766     GST_VERSION_MINOR,
767     "audiorate",
768     "Adjusts audio frames",
769     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)