removesilence: add silent property to control bus message notifications
[platform/upstream/gstreamer.git] / gst / removesilence / gstremovesilence.c
1 /* GStreamer
2  * Copyright (C) 2011 Tiago Katcipis <tiagokatcipis@gmail.com>
3  * Copyright (C) 2011 Paulo Pizarro  <paulo.pizarro@gmail.com>
4  * Copyright (C) 2012-2016 Nicola Murino  <nicola.murino@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-removesilence
24  * @title: removesilence
25  *
26  * Removes all silence periods from an audio stream, dropping silence buffers.
27  *
28  * ## Example launch line
29  * |[
30  * gst-launch-1.0 -v -m filesrc location="audiofile" ! decodebin ! removesilence remove=true ! wavenc ! filesink location=without_audio.wav
31  * ]|
32  *
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include <gst/gst.h>
40 #include <gst/base/gstbasetransform.h>
41 #include <gst/audio/audio.h>
42
43 #include "gstremovesilence.h"
44
45
46 GST_DEBUG_CATEGORY_STATIC (gst_remove_silence_debug);
47 #define GST_CAT_DEFAULT gst_remove_silence_debug
48 #define DEFAULT_VAD_HYSTERESIS  480     /* 60 mseg */
49
50 /* Filter signals and args */
51 enum
52 {
53   /* FILL ME */
54   LAST_SIGNAL
55 };
56
57 enum
58 {
59   PROP_0,
60   PROP_REMOVE,
61   PROP_HYSTERESIS,
62   PROP_SQUASH,
63   PROP_SILENT
64 };
65
66
67 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS ("audio/x-raw, "
71         "format = (string) " GST_AUDIO_NE (S16) ", "
72         "layout = (string) interleaved, "
73         "rate = (int) [ 1, MAX ], " "channels = (int) 1"));
74
75 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS ("audio/x-raw, "
79         "format = (string) " GST_AUDIO_NE (S16) ", "
80         "layout = (string) interleaved, "
81         "rate = (int) [ 1, MAX ], " "channels = (int) 1"));
82
83
84 #define DEBUG_INIT(bla) \
85   GST_DEBUG_CATEGORY_INIT (gst_remove_silence_debug, "removesilence", 0, "removesilence element")
86
87 #define gst_remove_silence_parent_class parent_class
88 G_DEFINE_TYPE_WITH_CODE (GstRemoveSilence, gst_remove_silence,
89     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT (0));
90
91 static void gst_remove_silence_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec);
93 static void gst_remove_silence_get_property (GObject * object, guint prop_id,
94     GValue * value, GParamSpec * pspec);
95
96 static GstFlowReturn gst_remove_silence_transform_ip (GstBaseTransform * base,
97     GstBuffer * buf);
98 static void gst_remove_silence_finalize (GObject * obj);
99
100 /* GObject vmethod implementations */
101
102 /* initialize the removesilence's class */
103 static void
104 gst_remove_silence_class_init (GstRemoveSilenceClass * klass)
105 {
106   GObjectClass *gobject_class;
107   GstElementClass *gstelement_class;
108
109   gobject_class = (GObjectClass *) klass;
110   gstelement_class = (GstElementClass *) klass;
111
112   gobject_class->finalize = gst_remove_silence_finalize;
113   gobject_class->set_property = gst_remove_silence_set_property;
114   gobject_class->get_property = gst_remove_silence_get_property;
115
116   g_object_class_install_property (gobject_class, PROP_REMOVE,
117       g_param_spec_boolean ("remove", "Remove",
118           "Set to true to remove silence from the stream, false otherwhise",
119           FALSE, G_PARAM_READWRITE));
120
121   g_object_class_install_property (gobject_class, PROP_HYSTERESIS,
122       g_param_spec_uint64 ("hysteresis",
123           "Hysteresis",
124           "Set the hysteresis (on samples) used on the internal VAD",
125           1, G_MAXUINT64, DEFAULT_VAD_HYSTERESIS, G_PARAM_READWRITE));
126
127   g_object_class_install_property (gobject_class, PROP_SQUASH,
128       g_param_spec_boolean ("squash", "Squash",
129           "Set to true to retimestamp buffers when silence is removed and so avoid timestamp gap",
130           FALSE, G_PARAM_READWRITE));
131
132   g_object_class_install_property (gobject_class, PROP_SILENT,
133       g_param_spec_boolean ("silent", "Silent",
134           "Disable/enable bus message notifications for silent detected/finished",
135           TRUE, G_PARAM_READWRITE));
136
137   gst_element_class_set_static_metadata (gstelement_class,
138       "RemoveSilence",
139       "Filter/Effect/Audio",
140       "Removes all the silence periods from the audio stream.",
141       "Tiago Katcipis <tiagokatcipis@gmail.com>\n \
142        Paulo Pizarro  <paulo.pizarro@gmail.com>\n \
143        Nicola Murino  <nicola.murino@gmail.com>");
144
145   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
146   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
147
148   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
149       GST_DEBUG_FUNCPTR (gst_remove_silence_transform_ip);
150 }
151
152 /* initialize the new element
153  * instantiate pads and add them to element
154  * set pad calback functions
155  * initialize instance structure
156  */
157 static void
158 gst_remove_silence_init (GstRemoveSilence * filter)
159 {
160   filter->vad = vad_new (DEFAULT_VAD_HYSTERESIS);
161   filter->remove = FALSE;
162   filter->squash = FALSE;
163   filter->ts_offset = 0;
164   filter->silence_detected = FALSE;
165   filter->silent = TRUE;
166
167   if (!filter->vad) {
168     GST_DEBUG ("Error initializing VAD !!");
169     return;
170   }
171 }
172
173 static void
174 gst_remove_silence_finalize (GObject * obj)
175 {
176   GstRemoveSilence *filter = GST_REMOVE_SILENCE (obj);
177   GST_DEBUG ("Destroying VAD");
178   vad_destroy (filter->vad);
179   filter->vad = NULL;
180   GST_DEBUG ("VAD Destroyed");
181   G_OBJECT_CLASS (parent_class)->finalize (obj);
182 }
183
184 static void
185 gst_remove_silence_set_property (GObject * object, guint prop_id,
186     const GValue * value, GParamSpec * pspec)
187 {
188   GstRemoveSilence *filter = GST_REMOVE_SILENCE (object);
189
190   switch (prop_id) {
191     case PROP_REMOVE:
192       filter->remove = g_value_get_boolean (value);
193       break;
194     case PROP_HYSTERESIS:
195       vad_set_hysteresis (filter->vad, g_value_get_uint64 (value));
196       break;
197     case PROP_SQUASH:
198       filter->squash = g_value_get_boolean (value);
199       break;
200     case PROP_SILENT:
201       filter->silent = g_value_get_boolean (value);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206   }
207 }
208
209 static void
210 gst_remove_silence_get_property (GObject * object, guint prop_id,
211     GValue * value, GParamSpec * pspec)
212 {
213   GstRemoveSilence *filter = GST_REMOVE_SILENCE (object);
214
215   switch (prop_id) {
216     case PROP_REMOVE:
217       g_value_set_boolean (value, filter->remove);
218       break;
219     case PROP_HYSTERESIS:
220       g_value_set_uint64 (value, vad_get_hysteresis (filter->vad));
221       break;
222     case PROP_SQUASH:
223       g_value_set_boolean (value, filter->squash);
224       break;
225     case PROP_SILENT:
226       g_value_set_boolean (value, filter->silent);
227       break;
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230       break;
231   }
232 }
233
234 static GstFlowReturn
235 gst_remove_silence_transform_ip (GstBaseTransform * trans, GstBuffer * inbuf)
236 {
237   GstRemoveSilence *filter = NULL;
238   int frame_type;
239   GstMapInfo map;
240
241   filter = GST_REMOVE_SILENCE (trans);
242
243   gst_buffer_map (inbuf, &map, GST_MAP_READ);
244   frame_type =
245       vad_update (filter->vad, (gint16 *) map.data, map.size / sizeof (gint16));
246   gst_buffer_unmap (inbuf, &map);
247
248   if (frame_type == VAD_SILENCE) {
249     GST_DEBUG ("Silence detected");
250     if (!filter->silence_detected) {
251       if (!filter->silent) {
252         if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
253           GstStructure *s;
254           GstMessage *m;
255           s = gst_structure_new ("removesilence", "silence_detected",
256               G_TYPE_UINT64, GST_BUFFER_PTS (inbuf) - filter->ts_offset, NULL);
257           m = gst_message_new_element (GST_OBJECT (filter), s);
258           gst_element_post_message (GST_ELEMENT (filter), m);
259         }
260       }
261       filter->silence_detected = TRUE;
262     }
263
264     if (filter->remove) {
265       GST_DEBUG ("Removing silence");
266       if (filter->squash) {
267         if (GST_BUFFER_DURATION_IS_VALID (inbuf)) {
268           filter->ts_offset += inbuf->duration;
269         } else {
270           GST_WARNING ("Invalid buffer duration: ts_offset not updated");
271         }
272       }
273       return GST_BASE_TRANSFORM_FLOW_DROPPED;
274     }
275
276   } else {
277     if (filter->silence_detected) {
278       if (!filter->silent) {
279         if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
280           GstStructure *s;
281           GstMessage *m;
282           s = gst_structure_new ("removesilence", "silence_finished",
283               G_TYPE_UINT64, GST_BUFFER_PTS (inbuf) - filter->ts_offset, NULL);
284           m = gst_message_new_element (GST_OBJECT (filter), s);
285           gst_element_post_message (GST_ELEMENT (filter), m);
286         }
287       }
288       filter->silence_detected = FALSE;
289     }
290   }
291
292   if (filter->squash && filter->ts_offset > 0) {
293     if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
294       inbuf = gst_buffer_make_writable (inbuf);
295       GST_BUFFER_PTS (inbuf) -= filter->ts_offset;
296     } else {
297       GST_WARNING ("Invalid buffer pts, update not possibile");
298     }
299   }
300
301   return GST_FLOW_OK;
302 }
303
304 /*Plugin init functions*/
305 static gboolean
306 plugin_init (GstPlugin * plugin)
307 {
308   return gst_element_register (plugin, "removesilence", GST_RANK_NONE,
309       gst_remove_silence_get_type ());
310 }
311
312 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
313     GST_VERSION_MINOR,
314     removesilence,
315     "Removes silence from an audio stream",
316     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);