expand tabs
[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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <string.h>
24
25 #include <gst/gst.h>
26 #include <gst/audio/audio.h>
27
28 #define GST_TYPE_AUDIO_RATE \
29   (gst_audio_rate_get_type())
30 #define GST_AUDIO_RATE(obj) \
31   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_RATE,GstAudioRate))
32 #define GST_AUDIO_RATE_CLASS(klass) \
33   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_RATE,GstAudioRate))
34 #define GST_IS_AUDIO_RATE(obj) \
35   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_RATE))
36 #define GST_IS_AUDIO_RATE_CLASS(obj) \
37   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_RATE))
38
39 typedef struct _GstAudioRate GstAudioRate;
40 typedef struct _GstAudioRateClass GstAudioRateClass;
41
42 struct _GstAudioRate
43 {
44   GstElement element;
45
46   GstPad *sinkpad, *srcpad;
47
48   gint bytes_per_sample;
49
50   /* audio state */
51   guint64 next_offset;
52
53   guint64 in, out, add, drop;
54   gboolean silent;
55 };
56
57 struct _GstAudioRateClass
58 {
59   GstElementClass parent_class;
60 };
61
62 /* elementfactory information */
63 static GstElementDetails audio_rate_details =
64 GST_ELEMENT_DETAILS ("Audio rate adjuster",
65     "Filter/Effect/Audio",
66     "Drops/duplicates/adjusts timestamps on audio samples to make a perfect stream",
67     "Wim Taymans <wim@fluendo.com>");
68
69 /* GstAudioRate signals and args */
70 enum
71 {
72   /* FILL ME */
73   LAST_SIGNAL
74 };
75
76 #define DEFAULT_SILENT  TRUE
77
78 enum
79 {
80   ARG_0,
81   ARG_IN,
82   ARG_OUT,
83   ARG_ADD,
84   ARG_DROP,
85   ARG_SILENT,
86   /* FILL ME */
87 };
88
89 static GstStaticPadTemplate gst_audio_rate_src_template =
90 GST_STATIC_PAD_TEMPLATE ("src",
91     GST_PAD_SRC,
92     GST_PAD_ALWAYS,
93     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS)
94     );
95
96 static GstStaticPadTemplate gst_audio_rate_sink_template =
97 GST_STATIC_PAD_TEMPLATE ("sink",
98     GST_PAD_SINK,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS)
101     );
102
103 static void gst_audio_rate_base_init (gpointer g_class);
104 static void gst_audio_rate_class_init (GstAudioRateClass * klass);
105 static void gst_audio_rate_init (GstAudioRate * audiorate);
106 static GstFlowReturn gst_audio_rate_chain (GstPad * pad, GstBuffer * buf);
107
108 static void gst_audio_rate_set_property (GObject * object,
109     guint prop_id, const GValue * value, GParamSpec * pspec);
110 static void gst_audio_rate_get_property (GObject * object,
111     guint prop_id, GValue * value, GParamSpec * pspec);
112
113 static GstStateChangeReturn gst_audio_rate_change_state (GstElement * element,
114     GstStateChange transition);
115
116 static GstElementClass *parent_class = NULL;
117
118 /*static guint gst_audio_rate_signals[LAST_SIGNAL] = { 0 }; */
119
120 static GType
121 gst_audio_rate_get_type (void)
122 {
123   static GType audio_rate_type = 0;
124
125   if (!audio_rate_type) {
126     static const GTypeInfo audio_rate_info = {
127       sizeof (GstAudioRateClass),
128       gst_audio_rate_base_init,
129       NULL,
130       (GClassInitFunc) gst_audio_rate_class_init,
131       NULL,
132       NULL,
133       sizeof (GstAudioRate),
134       0,
135       (GInstanceInitFunc) gst_audio_rate_init,
136     };
137
138     audio_rate_type = g_type_register_static (GST_TYPE_ELEMENT,
139         "GstAudioRate", &audio_rate_info, 0);
140   }
141
142   return audio_rate_type;
143 }
144
145 static void
146 gst_audio_rate_base_init (gpointer g_class)
147 {
148   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
149
150   gst_element_class_set_details (element_class, &audio_rate_details);
151
152   gst_element_class_add_pad_template (element_class,
153       gst_static_pad_template_get (&gst_audio_rate_sink_template));
154   gst_element_class_add_pad_template (element_class,
155       gst_static_pad_template_get (&gst_audio_rate_src_template));
156 }
157 static void
158 gst_audio_rate_class_init (GstAudioRateClass * klass)
159 {
160   GObjectClass *object_class = G_OBJECT_CLASS (klass);
161   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
162
163   parent_class = g_type_class_peek_parent (klass);
164
165   object_class->set_property = gst_audio_rate_set_property;
166   object_class->get_property = gst_audio_rate_get_property;
167
168   g_object_class_install_property (object_class, ARG_IN,
169       g_param_spec_uint64 ("in", "In",
170           "Number of input samples", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
171   g_object_class_install_property (object_class, ARG_OUT,
172       g_param_spec_uint64 ("out", "Out",
173           "Number of output samples", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
174   g_object_class_install_property (object_class, ARG_ADD,
175       g_param_spec_uint64 ("add", "Add",
176           "Number of added samples", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
177   g_object_class_install_property (object_class, ARG_DROP,
178       g_param_spec_uint64 ("drop", "Drop",
179           "Number of dropped samples", 0, G_MAXUINT64, 0, G_PARAM_READABLE));
180   g_object_class_install_property (object_class, ARG_SILENT,
181       g_param_spec_boolean ("silent", "silent",
182           "Don't emit notify for dropped and duplicated frames",
183           DEFAULT_SILENT, G_PARAM_READWRITE));
184
185   element_class->change_state = gst_audio_rate_change_state;
186 }
187
188 static gboolean
189 gst_audio_rate_setcaps (GstPad * pad, GstCaps * caps)
190 {
191   GstAudioRate *audiorate;
192   GstStructure *structure;
193   GstPad *otherpad;
194   gint ret, channels, depth;
195
196   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
197
198   otherpad = (pad == audiorate->srcpad) ? audiorate->sinkpad :
199       audiorate->srcpad;
200
201   if (!gst_pad_set_caps (otherpad, caps))
202     return FALSE;
203
204   structure = gst_caps_get_structure (caps, 0);
205
206   ret = gst_structure_get_int (structure, "channels", &channels);
207   ret &= gst_structure_get_int (structure, "depth", &depth);
208
209   if (!ret)
210     return FALSE;
211
212   audiorate->bytes_per_sample = channels * (depth / 8);
213   if (audiorate->bytes_per_sample == 0)
214     audiorate->bytes_per_sample = 1;
215
216   return TRUE;
217 }
218
219 static void
220 gst_audio_rate_init (GstAudioRate * audiorate)
221 {
222   audiorate->sinkpad =
223       gst_pad_new_from_static_template (&gst_audio_rate_sink_template, "sink");
224   gst_element_add_pad (GST_ELEMENT (audiorate), audiorate->sinkpad);
225   gst_pad_set_chain_function (audiorate->sinkpad, gst_audio_rate_chain);
226   gst_pad_set_setcaps_function (audiorate->sinkpad, gst_audio_rate_setcaps);
227   gst_pad_set_getcaps_function (audiorate->sinkpad, gst_pad_proxy_getcaps);
228
229   audiorate->srcpad =
230       gst_pad_new_from_static_template (&gst_audio_rate_src_template, "src");
231   gst_element_add_pad (GST_ELEMENT (audiorate), audiorate->srcpad);
232   gst_pad_set_setcaps_function (audiorate->srcpad, gst_audio_rate_setcaps);
233   gst_pad_set_getcaps_function (audiorate->srcpad, gst_pad_proxy_getcaps);
234
235   audiorate->bytes_per_sample = 1;
236   audiorate->in = 0;
237   audiorate->out = 0;
238   audiorate->drop = 0;
239   audiorate->add = 0;
240   audiorate->silent = DEFAULT_SILENT;
241 }
242
243 static GstFlowReturn
244 gst_audio_rate_chain (GstPad * pad, GstBuffer * buf)
245 {
246   GstAudioRate *audiorate;
247   GstClockTime in_time, in_duration;
248   guint64 in_offset, in_offset_end;
249   gint in_size;
250   GstFlowReturn ret = GST_FLOW_OK;
251
252   audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));
253
254   audiorate->in++;
255
256   in_time = GST_BUFFER_TIMESTAMP (buf);
257   in_duration = GST_BUFFER_DURATION (buf);
258   in_size = GST_BUFFER_SIZE (buf);
259   in_offset = GST_BUFFER_OFFSET (buf);
260   in_offset_end = GST_BUFFER_OFFSET_END (buf);
261
262   if (in_offset == GST_CLOCK_TIME_NONE || in_offset_end == GST_CLOCK_TIME_NONE) {
263     GST_WARNING_OBJECT (audiorate, "audiorate got buffer without offsets");
264   }
265
266   /* do we need to insert samples */
267   if (in_offset > audiorate->next_offset) {
268     GstBuffer *fill;
269     gint fillsize;
270     guint64 fillsamples;
271
272     fillsamples = in_offset - audiorate->next_offset;
273     fillsize = fillsamples * audiorate->bytes_per_sample;
274
275     fill = gst_buffer_new_and_alloc (fillsize);
276     memset (GST_BUFFER_DATA (fill), 0, fillsize);
277
278     GST_LOG_OBJECT (audiorate, "inserting %lld samples", fillsamples);
279
280     GST_BUFFER_DURATION (fill) = in_duration * fillsize / in_size;
281     GST_BUFFER_TIMESTAMP (fill) = in_time - GST_BUFFER_DURATION (fill);
282     GST_BUFFER_OFFSET (fill) = audiorate->next_offset;
283     GST_BUFFER_OFFSET_END (fill) = in_offset;
284
285     if ((ret = gst_pad_push (audiorate->srcpad, fill) != GST_FLOW_OK))
286       goto beach;
287     audiorate->out++;
288     audiorate->add += fillsamples;
289
290     if (!audiorate->silent)
291       g_object_notify (G_OBJECT (audiorate), "add");
292   } else if (in_offset < audiorate->next_offset) {
293     /* need to remove samples */
294     if (in_offset_end <= audiorate->next_offset) {
295       guint64 drop = in_size / audiorate->bytes_per_sample;
296
297       audiorate->drop += drop;
298
299       GST_LOG_OBJECT (audiorate, "dropping %lld samples", drop);
300
301       /* we can drop the buffer completely */
302       gst_buffer_unref (buf);
303
304       if (!audiorate->silent)
305         g_object_notify (G_OBJECT (audiorate), "drop");
306
307       goto beach;
308     } else {
309       guint64 truncsamples, truncsize, leftsize;
310       GstBuffer *trunc;
311
312       /* truncate buffer */
313       truncsamples = audiorate->next_offset - in_offset;
314       truncsize = truncsamples * audiorate->bytes_per_sample;
315       leftsize = in_size - truncsize;
316
317       trunc = gst_buffer_create_sub (buf, truncsize, in_size);
318       GST_BUFFER_DURATION (trunc) = in_duration * leftsize / in_size;
319       GST_BUFFER_TIMESTAMP (trunc) =
320           in_time + in_duration - GST_BUFFER_DURATION (trunc);
321       GST_BUFFER_OFFSET (trunc) = audiorate->next_offset;
322       GST_BUFFER_OFFSET_END (trunc) = in_offset_end;
323
324       GST_LOG_OBJECT (audiorate, "truncating %lld samples", truncsamples);
325
326       gst_buffer_unref (buf);
327       buf = trunc;
328
329       audiorate->drop += truncsamples;
330     }
331   }
332   ret = gst_pad_push (audiorate->srcpad, buf);
333   audiorate->out++;
334
335   audiorate->next_offset = in_offset_end;
336 beach:
337   return ret;
338 }
339
340 static void
341 gst_audio_rate_set_property (GObject * object,
342     guint prop_id, const GValue * value, GParamSpec * pspec)
343 {
344   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
345
346   switch (prop_id) {
347     case ARG_SILENT:
348       audiorate->silent = g_value_get_boolean (value);
349       break;
350     default:
351       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
352       break;
353   }
354 }
355
356 static void
357 gst_audio_rate_get_property (GObject * object,
358     guint prop_id, GValue * value, GParamSpec * pspec)
359 {
360   GstAudioRate *audiorate = GST_AUDIO_RATE (object);
361
362   switch (prop_id) {
363     case ARG_IN:
364       g_value_set_uint64 (value, audiorate->in);
365       break;
366     case ARG_OUT:
367       g_value_set_uint64 (value, audiorate->out);
368       break;
369     case ARG_ADD:
370       g_value_set_uint64 (value, audiorate->add);
371       break;
372     case ARG_DROP:
373       g_value_set_uint64 (value, audiorate->drop);
374       break;
375     case ARG_SILENT:
376       g_value_set_boolean (value, audiorate->silent);
377       break;
378     default:
379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380       break;
381   }
382 }
383
384 static GstStateChangeReturn
385 gst_audio_rate_change_state (GstElement * element, GstStateChange transition)
386 {
387   GstAudioRate *audiorate = GST_AUDIO_RATE (element);
388
389   switch (transition) {
390     case GST_STATE_CHANGE_PAUSED_TO_READY:
391       break;
392     case GST_STATE_CHANGE_READY_TO_PAUSED:
393       audiorate->next_offset = 0;
394       break;
395     default:
396       break;
397   }
398
399   if (parent_class->change_state)
400     return parent_class->change_state (element, transition);
401
402   return GST_STATE_CHANGE_SUCCESS;
403 }
404
405 static gboolean
406 plugin_init (GstPlugin * plugin)
407 {
408   return gst_element_register (plugin, "audiorate", GST_RANK_NONE,
409       GST_TYPE_AUDIO_RATE);
410 }
411
412 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
413     GST_VERSION_MINOR,
414     "audiorate",
415     "Adjusts audio frames",
416     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)