Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-base.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   gint rate, bpf;
372
373   if (src_fmt == dest_fmt) {
374     *dest_val = src_val;
375     return TRUE;
376   }
377
378   rate = GST_AUDIO_INFO_RATE (&audiorate->info);
379   bpf = GST_AUDIO_INFO_BPF (&audiorate->info);
380
381   switch (src_fmt) {
382     case GST_FORMAT_DEFAULT:
383       switch (dest_fmt) {
384         case GST_FORMAT_BYTES:
385           *dest_val = src_val * bpf;
386           break;
387         case GST_FORMAT_TIME:
388           *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND, rate);
389           break;
390         default:
391           return FALSE;;
392       }
393       break;
394     case GST_FORMAT_BYTES:
395       switch (dest_fmt) {
396         case GST_FORMAT_DEFAULT:
397           *dest_val = src_val / bpf;
398           break;
399         case GST_FORMAT_TIME:
400           *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND,
401               rate * bpf);
402           break;
403         default:
404           return FALSE;;
405       }
406       break;
407     case GST_FORMAT_TIME:
408       switch (dest_fmt) {
409         case GST_FORMAT_BYTES:
410           *dest_val = gst_util_uint64_scale_int (src_val,
411               rate * bpf, GST_SECOND);
412           break;
413         case GST_FORMAT_DEFAULT:
414           *dest_val = gst_util_uint64_scale_int (src_val, rate, GST_SECOND);
415           break;
416         default:
417           return FALSE;
418       }
419       break;
420     default:
421       return FALSE;
422   }
423   return TRUE;
424 }
425
426
427 static gboolean
428 gst_audio_rate_convert_segments (GstAudioRate * audiorate)
429 {
430   GstFormat src_fmt, dst_fmt;
431
432   src_fmt = audiorate->sink_segment.format;
433   dst_fmt = audiorate->src_segment.format;
434
435 #define CONVERT_VAL(field) gst_audio_rate_convert (audiorate, \
436                 src_fmt, audiorate->sink_segment.field,       \
437                 dst_fmt, &audiorate->src_segment.field);
438
439   audiorate->sink_segment.rate = audiorate->src_segment.rate;
440   audiorate->sink_segment.flags = audiorate->src_segment.flags;
441   audiorate->sink_segment.applied_rate = audiorate->src_segment.applied_rate;
442   CONVERT_VAL (start);
443   CONVERT_VAL (stop);
444   CONVERT_VAL (time);
445   CONVERT_VAL (base);
446   CONVERT_VAL (position);
447 #undef CONVERT_VAL
448
449   return TRUE;
450 }
451
452 static void
453 gst_audio_rate_notify_drop (GstAudioRate * audiorate)
454 {
455 #if !GLIB_CHECK_VERSION(2,26,0)
456   g_object_notify ((GObject *) audiorate, "drop");
457 #else
458   g_object_notify_by_pspec ((GObject *) audiorate, pspec_drop);
459 #endif
460 }
461
462 static void
463 gst_audio_rate_notify_add (GstAudioRate * audiorate)
464 {
465 #if !GLIB_CHECK_VERSION(2,26,0)
466   g_object_notify ((GObject *) audiorate, "add");
467 #else
468   g_object_notify_by_pspec ((GObject *) audiorate, pspec_add);
469 #endif
470 }
471
472 static GstFlowReturn
473 gst_audio_rate_chain (GstPad * pad, GstBuffer * buf)
474 {
475   GstAudioRate *audiorate;
476   GstClockTime in_time;
477   guint64 in_offset, in_offset_end, in_samples;
478   guint in_size;
479   GstFlowReturn ret = GST_FLOW_OK;
480   GstClockTimeDiff diff;
481   gint rate, bpf;
482
483   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
484
485   rate = GST_AUDIO_INFO_RATE (&audiorate->info);
486   bpf = GST_AUDIO_INFO_BPF (&audiorate->info);
487
488   /* need to be negotiated now */
489   if (bpf == 0)
490     goto not_negotiated;
491
492   /* we have a new pending segment */
493   if (audiorate->next_offset == -1) {
494     gint64 pos;
495
496     /* update the TIME segment */
497     gst_audio_rate_convert_segments (audiorate);
498
499     /* first buffer, we are negotiated and we have a segment, calculate the
500      * current expected offsets based on the segment.start, which is the first
501      * media time of the segment and should match the media time of the first
502      * buffer in that segment, which is the offset expressed in DEFAULT units.
503      */
504     /* convert first timestamp of segment to sample position */
505     pos = gst_util_uint64_scale_int (audiorate->src_segment.start,
506         GST_AUDIO_INFO_RATE (&audiorate->info), GST_SECOND);
507
508     GST_DEBUG_OBJECT (audiorate, "resync to offset %" G_GINT64_FORMAT, pos);
509
510     /* resyncing is a discont */
511     audiorate->discont = TRUE;
512
513     audiorate->next_offset = pos;
514     audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
515         GST_SECOND, GST_AUDIO_INFO_RATE (&audiorate->info));
516
517     if (audiorate->skip_to_first && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
518       GST_DEBUG_OBJECT (audiorate, "but skipping to first buffer instead");
519       pos = gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf),
520           GST_AUDIO_INFO_RATE (&audiorate->info), GST_SECOND);
521       GST_DEBUG_OBJECT (audiorate, "so resync to offset %" G_GINT64_FORMAT,
522           pos);
523       audiorate->next_offset = pos;
524       audiorate->next_ts = GST_BUFFER_TIMESTAMP (buf);
525     }
526   }
527
528   audiorate->in++;
529
530   in_time = GST_BUFFER_TIMESTAMP (buf);
531   if (in_time == GST_CLOCK_TIME_NONE) {
532     GST_DEBUG_OBJECT (audiorate, "no timestamp, using expected next time");
533     in_time = audiorate->next_ts;
534   }
535
536   in_size = gst_buffer_get_size (buf);
537   in_samples = in_size / bpf;
538
539   /* calculate the buffer offset */
540   in_offset = gst_util_uint64_scale_int_round (in_time, rate, GST_SECOND);
541   in_offset_end = in_offset + in_samples;
542
543   GST_LOG_OBJECT (audiorate,
544       "in_time:%" GST_TIME_FORMAT ", in_duration:%" GST_TIME_FORMAT
545       ", in_size:%u, in_offset:%" G_GUINT64_FORMAT ", in_offset_end:%"
546       G_GUINT64_FORMAT ", ->next_offset:%" G_GUINT64_FORMAT ", ->next_ts:%"
547       GST_TIME_FORMAT, GST_TIME_ARGS (in_time),
548       GST_TIME_ARGS (GST_FRAMES_TO_CLOCK_TIME (in_samples, rate)),
549       in_size, in_offset, in_offset_end, audiorate->next_offset,
550       GST_TIME_ARGS (audiorate->next_ts));
551
552   diff = in_time - audiorate->next_ts;
553   if (diff <= (GstClockTimeDiff) audiorate->tolerance &&
554       diff >= (GstClockTimeDiff) - audiorate->tolerance) {
555     /* buffer time close enough to expected time,
556      * so produce a perfect stream by simply 'shifting'
557      * it to next ts and offset and sending */
558     GST_LOG_OBJECT (audiorate, "within tolerance %" GST_TIME_FORMAT,
559         GST_TIME_ARGS (audiorate->tolerance));
560     /* The outgoing buffer's offset will be set to ->next_offset, we also
561      * need to adjust the offset_end value accordingly */
562     in_offset_end = audiorate->next_offset + in_samples;
563     goto send;
564   }
565
566   /* do we need to insert samples */
567   if (in_offset > audiorate->next_offset) {
568     GstBuffer *fill;
569     gint fillsize;
570     guint64 fillsamples;
571
572     /* We don't want to allocate a single unreasonably huge buffer - it might
573        be hundreds of megabytes. So, limit each output buffer to one second of
574        audio */
575     fillsamples = in_offset - audiorate->next_offset;
576
577     while (fillsamples > 0) {
578       guint64 cursamples = MIN (fillsamples, rate);
579       guint8 *data;
580
581       fillsamples -= cursamples;
582       fillsize = cursamples * bpf;
583
584       fill = gst_buffer_new_and_alloc (fillsize);
585
586       data = gst_buffer_map (fill, NULL, NULL, GST_MAP_WRITE);
587       /* FIXME, 0 might not be the silence byte for the negotiated format. */
588       memset (data, 0, fillsize);
589       gst_buffer_unmap (fill, data, fillsize);
590
591       GST_DEBUG_OBJECT (audiorate, "inserting %" G_GUINT64_FORMAT " samples",
592           cursamples);
593
594       GST_BUFFER_OFFSET (fill) = audiorate->next_offset;
595       audiorate->next_offset += cursamples;
596       GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset;
597
598       /* Use next timestamp, then calculate following timestamp based on 
599        * offset to get duration. Neccesary complexity to get 'perfect' 
600        * streams */
601       GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
602       audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
603           GST_SECOND, rate);
604       GST_BUFFER_DURATION (fill) = audiorate->next_ts -
605           GST_BUFFER_TIMESTAMP (fill);
606
607       /* we created this buffer to fill a gap */
608       GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP);
609       /* set discont if it's pending, this is mostly done for the first buffer 
610        * and after a flushing seek */
611       if (audiorate->discont) {
612         GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT);
613         audiorate->discont = FALSE;
614       }
615
616       ret = gst_pad_push (audiorate->srcpad, fill);
617       if (ret != GST_FLOW_OK)
618         goto beach;
619       audiorate->out++;
620       audiorate->add += cursamples;
621
622       if (!audiorate->silent)
623         gst_audio_rate_notify_add (audiorate);
624     }
625
626   } else if (in_offset < audiorate->next_offset) {
627     /* need to remove samples */
628     if (in_offset_end <= audiorate->next_offset) {
629       guint64 drop = in_size / bpf;
630
631       audiorate->drop += drop;
632
633       GST_DEBUG_OBJECT (audiorate, "dropping %" G_GUINT64_FORMAT " samples",
634           drop);
635
636       /* we can drop the buffer completely */
637       gst_buffer_unref (buf);
638       buf = NULL;
639
640       if (!audiorate->silent)
641         gst_audio_rate_notify_drop (audiorate);
642
643       goto beach;
644     } else {
645       guint64 truncsamples;
646       guint truncsize, leftsize;
647       GstBuffer *trunc;
648
649       /* truncate buffer */
650       truncsamples = audiorate->next_offset - in_offset;
651       truncsize = truncsamples * bpf;
652       leftsize = in_size - truncsize;
653
654       trunc =
655           gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, truncsize,
656           leftsize);
657
658       gst_buffer_unref (buf);
659       buf = trunc;
660
661       audiorate->drop += truncsamples;
662       GST_DEBUG_OBJECT (audiorate, "truncating %" G_GUINT64_FORMAT " samples",
663           truncsamples);
664
665       if (!audiorate->silent)
666         gst_audio_rate_notify_drop (audiorate);
667     }
668   }
669
670 send:
671   if (gst_buffer_get_size (buf) == 0)
672     goto beach;
673
674   /* Now calculate parameters for whichever buffer (either the original
675    * or truncated one) we're pushing. */
676   GST_BUFFER_OFFSET (buf) = audiorate->next_offset;
677   GST_BUFFER_OFFSET_END (buf) = in_offset_end;
678
679   GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts;
680   audiorate->next_ts = gst_util_uint64_scale_int (in_offset_end,
681       GST_SECOND, rate);
682   GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);
683
684   if (audiorate->discont) {
685     /* we need to output a discont buffer, do so now */
686     GST_DEBUG_OBJECT (audiorate, "marking DISCONT on output buffer");
687     buf = gst_buffer_make_writable (buf);
688     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
689     audiorate->discont = FALSE;
690   } else if (GST_BUFFER_IS_DISCONT (buf)) {
691     /* else we make everything continuous so we can safely remove the DISCONT
692      * flag from the buffer if there was one */
693     GST_DEBUG_OBJECT (audiorate, "removing DISCONT from buffer");
694     buf = gst_buffer_make_writable (buf);
695     GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
696   }
697
698   /* set last_stop on segment */
699   audiorate->src_segment.position =
700       GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
701
702   ret = gst_pad_push (audiorate->srcpad, buf);
703   buf = NULL;
704   audiorate->out++;
705
706   audiorate->next_offset = in_offset_end;
707 beach:
708
709   if (buf)
710     gst_buffer_unref (buf);
711
712   gst_object_unref (audiorate);
713
714   return ret;
715
716   /* ERRORS */
717 not_negotiated:
718   {
719     gst_buffer_unref (buf);
720
721     GST_ELEMENT_ERROR (audiorate, STREAM, FORMAT,
722         (NULL), ("pipeline error, format was not negotiated"));
723     return GST_FLOW_NOT_NEGOTIATED;
724   }
725 }
726
727 static void
728 gst_audio_rate_set_property (GObject * object,
729     guint prop_id, const GValue * value, GParamSpec * pspec)
730 {
731   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
732
733   switch (prop_id) {
734     case ARG_SILENT:
735       audiorate->silent = g_value_get_boolean (value);
736       break;
737     case ARG_TOLERANCE:
738       audiorate->tolerance = g_value_get_uint64 (value);
739       break;
740     case ARG_SKIP_TO_FIRST:
741       audiorate->skip_to_first = g_value_get_boolean (value);
742       break;
743     default:
744       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
745       break;
746   }
747 }
748
749 static void
750 gst_audio_rate_get_property (GObject * object,
751     guint prop_id, GValue * value, GParamSpec * pspec)
752 {
753   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
754
755   switch (prop_id) {
756     case ARG_IN:
757       g_value_set_uint64 (value, audiorate->in);
758       break;
759     case ARG_OUT:
760       g_value_set_uint64 (value, audiorate->out);
761       break;
762     case ARG_ADD:
763       g_value_set_uint64 (value, audiorate->add);
764       break;
765     case ARG_DROP:
766       g_value_set_uint64 (value, audiorate->drop);
767       break;
768     case ARG_SILENT:
769       g_value_set_boolean (value, audiorate->silent);
770       break;
771     case ARG_TOLERANCE:
772       g_value_set_uint64 (value, audiorate->tolerance);
773       break;
774     case ARG_SKIP_TO_FIRST:
775       g_value_set_boolean (value, audiorate->skip_to_first);
776       break;
777     default:
778       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
779       break;
780   }
781 }
782
783 static GstStateChangeReturn
784 gst_audio_rate_change_state (GstElement * element, GstStateChange transition)
785 {
786   GstAudioRate *audiorate = GST_AUDIO_RATE (element);
787
788   switch (transition) {
789     case GST_STATE_CHANGE_PAUSED_TO_READY:
790       break;
791     case GST_STATE_CHANGE_READY_TO_PAUSED:
792       audiorate->in = 0;
793       audiorate->out = 0;
794       audiorate->drop = 0;
795       audiorate->add = 0;
796       gst_audio_info_init (&audiorate->info);
797       gst_audio_rate_reset (audiorate);
798       break;
799     default:
800       break;
801   }
802
803   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
804 }
805
806 static gboolean
807 plugin_init (GstPlugin * plugin)
808 {
809   GST_DEBUG_CATEGORY_INIT (audio_rate_debug, "audiorate", 0,
810       "AudioRate stream fixer");
811
812   return gst_element_register (plugin, "audiorate", GST_RANK_NONE,
813       GST_TYPE_AUDIO_RATE);
814 }
815
816 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
817     GST_VERSION_MINOR,
818     "audiorate",
819     "Adjusts audio frames",
820     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)