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  * If the "silent" property is disabled, removesilence will generate
28  * bus messages named "removesilence".
29  * The message's structure contains one of these fields:
30  *
31  * - #guint64 "silence_detected": the PTS for the first silent buffer after a non silence period.
32  *
33  * - #guint64 "silence_finished": the PTS for the first non silent buffer after a silence period.
34  *
35  * ## Example launch line
36  * |[
37  * gst-launch-1.0 -v -m filesrc location="audiofile" ! decodebin ! removesilence remove=true ! wavenc ! filesink location=without_audio.wav
38  * ]|
39  *
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 
46 #include <gst/gst.h>
47 #include <gst/base/gstbasetransform.h>
48 #include <gst/audio/audio.h>
49 
50 #include "gstremovesilence.h"
51 
52 
53 GST_DEBUG_CATEGORY_STATIC (gst_remove_silence_debug);
54 #define GST_CAT_DEFAULT gst_remove_silence_debug
55 #define DEFAULT_VAD_HYSTERESIS  480     /* 60 mseg */
56 #define MINIMUM_SILENCE_BUFFERS_MIN  0
57 #define MINIMUM_SILENCE_BUFFERS_MAX  10000
58 #define MINIMUM_SILENCE_BUFFERS_DEF  0
59 #define MINIMUM_SILENCE_TIME_MIN  0
60 #define MINIMUM_SILENCE_TIME_MAX  10000000000
61 #define MINIMUM_SILENCE_TIME_DEF  0
62 #define DEFAULT_VAD_THRESHOLD -60
63 
64 /* Filter signals and args */
65 enum
66 {
67   /* FILL ME */
68   LAST_SIGNAL
69 };
70 
71 enum
72 {
73   PROP_0,
74   PROP_REMOVE,
75   PROP_HYSTERESIS,
76   PROP_THRESHOLD,
77   PROP_SQUASH,
78   PROP_SILENT,
79   PROP_MINIMUM_SILENCE_BUFFERS,
80   PROP_MINIMUM_SILENCE_TIME
81 };
82 
83 
84 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
85     GST_PAD_SINK,
86     GST_PAD_ALWAYS,
87     GST_STATIC_CAPS ("audio/x-raw, "
88         "format = (string) " GST_AUDIO_NE (S16) ", "
89         "layout = (string) interleaved, "
90         "rate = (int) [ 1, MAX ], " "channels = (int) 1"));
91 
92 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS ("audio/x-raw, "
96         "format = (string) " GST_AUDIO_NE (S16) ", "
97         "layout = (string) interleaved, "
98         "rate = (int) [ 1, MAX ], " "channels = (int) 1"));
99 
100 
101 #define DEBUG_INIT(bla) \
102   GST_DEBUG_CATEGORY_INIT (gst_remove_silence_debug, "removesilence", 0, "removesilence element")
103 
104 #define gst_remove_silence_parent_class parent_class
105 G_DEFINE_TYPE_WITH_CODE (GstRemoveSilence, gst_remove_silence,
106     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT (0));
107 
108 static void gst_remove_silence_set_property (GObject * object, guint prop_id,
109     const GValue * value, GParamSpec * pspec);
110 static void gst_remove_silence_get_property (GObject * object, guint prop_id,
111     GValue * value, GParamSpec * pspec);
112 
113 static gboolean gst_remove_silence_start (GstBaseTransform * trans);
114 static gboolean gst_remove_silence_sink_event (GstBaseTransform * trans,
115     GstEvent * event);
116 static GstFlowReturn gst_remove_silence_transform_ip (GstBaseTransform * base,
117     GstBuffer * buf);
118 static void gst_remove_silence_finalize (GObject * obj);
119 
120 /* GObject vmethod implementations */
121 
122 /* initialize the removesilence's class */
123 static void
gst_remove_silence_class_init(GstRemoveSilenceClass * klass)124 gst_remove_silence_class_init (GstRemoveSilenceClass * klass)
125 {
126   GObjectClass *gobject_class;
127   GstElementClass *gstelement_class;
128   GstBaseTransformClass *base_transform_class;
129 
130   gobject_class = (GObjectClass *) klass;
131   gstelement_class = (GstElementClass *) klass;
132   base_transform_class = GST_BASE_TRANSFORM_CLASS (klass);
133 
134   gobject_class->finalize = gst_remove_silence_finalize;
135   gobject_class->set_property = gst_remove_silence_set_property;
136   gobject_class->get_property = gst_remove_silence_get_property;
137 
138   g_object_class_install_property (gobject_class, PROP_REMOVE,
139       g_param_spec_boolean ("remove", "Remove",
140           "Set to true to remove silence from the stream, false otherwhise",
141           FALSE, G_PARAM_READWRITE));
142 
143   g_object_class_install_property (gobject_class, PROP_HYSTERESIS,
144       g_param_spec_uint64 ("hysteresis",
145           "Hysteresis",
146           "Set the hysteresis (on samples) used on the internal VAD",
147           1, G_MAXUINT64, DEFAULT_VAD_HYSTERESIS, G_PARAM_READWRITE));
148 
149   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
150       g_param_spec_int ("threshold",
151           "Threshold",
152           "Set the silence threshold used on the internal VAD in dB",
153           -70, 70, DEFAULT_VAD_THRESHOLD, G_PARAM_READWRITE));
154 
155   g_object_class_install_property (gobject_class, PROP_SQUASH,
156       g_param_spec_boolean ("squash", "Squash",
157           "Set to true to retimestamp buffers when silence is removed and so avoid timestamp gap",
158           FALSE, G_PARAM_READWRITE));
159 
160   g_object_class_install_property (gobject_class, PROP_SILENT,
161       g_param_spec_boolean ("silent", "Silent",
162           "Disable/enable bus message notifications for silence detected/finished",
163           TRUE, G_PARAM_READWRITE));
164 
165   g_object_class_install_property (gobject_class, PROP_MINIMUM_SILENCE_BUFFERS,
166       g_param_spec_uint ("minimum-silence-buffers", "Minimum silence buffers",
167           "Define the minimum number of consecutive silence buffers before "
168           "removing silence, 0 means disabled. This will not introduce latency",
169           MINIMUM_SILENCE_BUFFERS_MIN, MINIMUM_SILENCE_BUFFERS_MAX,
170           MINIMUM_SILENCE_BUFFERS_DEF, G_PARAM_READWRITE));
171 
172   g_object_class_install_property (gobject_class, PROP_MINIMUM_SILENCE_TIME,
173       g_param_spec_uint64 ("minimum_silence_time",
174           "Minimum silence time",
175           "Define the minimum silence time in nanoseconds before removing "
176           " silence, 0 means disabled. This will not introduce latency",
177           MINIMUM_SILENCE_TIME_MIN, MINIMUM_SILENCE_TIME_MAX,
178           MINIMUM_SILENCE_TIME_DEF, G_PARAM_READWRITE));
179 
180   gst_element_class_set_static_metadata (gstelement_class,
181       "RemoveSilence",
182       "Filter/Effect/Audio",
183       "Removes all the silence periods from the audio stream.",
184       "Tiago Katcipis <tiagokatcipis@gmail.com>\n \
185        Paulo Pizarro  <paulo.pizarro@gmail.com>\n \
186        Nicola Murino  <nicola.murino@gmail.com>");
187 
188   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
189   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
190 
191   base_transform_class->start = GST_DEBUG_FUNCPTR (gst_remove_silence_start);
192   base_transform_class->sink_event =
193       GST_DEBUG_FUNCPTR (gst_remove_silence_sink_event);
194   base_transform_class->transform_ip =
195       GST_DEBUG_FUNCPTR (gst_remove_silence_transform_ip);
196 }
197 
198 static void
gst_remove_silence_reset(GstRemoveSilence * filter)199 gst_remove_silence_reset (GstRemoveSilence * filter)
200 {
201   filter->ts_offset = 0;
202   filter->silence_detected = FALSE;
203   filter->consecutive_silence_buffers = 0;
204   filter->consecutive_silence_time = 0;
205 }
206 
207 /* initialize the new element
208  * instantiate pads and add them to element
209  * set pad calback functions
210  * initialize instance structure
211  */
212 static void
gst_remove_silence_init(GstRemoveSilence * filter)213 gst_remove_silence_init (GstRemoveSilence * filter)
214 {
215   filter->vad = vad_new (DEFAULT_VAD_HYSTERESIS, DEFAULT_VAD_THRESHOLD);
216   filter->remove = FALSE;
217   filter->squash = FALSE;
218   filter->silent = TRUE;
219   filter->minimum_silence_buffers = MINIMUM_SILENCE_BUFFERS_DEF;
220   filter->minimum_silence_time = MINIMUM_SILENCE_TIME_DEF;
221 
222   gst_remove_silence_reset (filter);
223 
224   if (!filter->vad) {
225     GST_DEBUG ("Error initializing VAD !!");
226     return;
227   }
228 }
229 
230 static gboolean
gst_remove_silence_start(GstBaseTransform * trans)231 gst_remove_silence_start (GstBaseTransform * trans)
232 {
233   GstRemoveSilence *filter = GST_REMOVE_SILENCE (trans);
234 
235   GST_INFO ("reset filter on start");
236   gst_remove_silence_reset (filter);
237 
238   return TRUE;
239 }
240 
241 static gboolean
gst_remove_silence_sink_event(GstBaseTransform * trans,GstEvent * event)242 gst_remove_silence_sink_event (GstBaseTransform * trans, GstEvent * event)
243 {
244   GstRemoveSilence *filter = GST_REMOVE_SILENCE (trans);
245 
246   if (event->type == GST_EVENT_SEGMENT) {
247     GST_INFO ("reset filter on segment event");
248     gst_remove_silence_reset (filter);
249   }
250 
251   return
252       GST_BASE_TRANSFORM_CLASS (gst_remove_silence_parent_class)->sink_event
253       (trans, event);
254 }
255 
256 static void
gst_remove_silence_finalize(GObject * obj)257 gst_remove_silence_finalize (GObject * obj)
258 {
259   GstRemoveSilence *filter = GST_REMOVE_SILENCE (obj);
260   GST_DEBUG ("Destroying VAD");
261   vad_destroy (filter->vad);
262   filter->vad = NULL;
263   GST_DEBUG ("VAD Destroyed");
264   G_OBJECT_CLASS (parent_class)->finalize (obj);
265 }
266 
267 static void
gst_remove_silence_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)268 gst_remove_silence_set_property (GObject * object, guint prop_id,
269     const GValue * value, GParamSpec * pspec)
270 {
271   GstRemoveSilence *filter = GST_REMOVE_SILENCE (object);
272 
273   switch (prop_id) {
274     case PROP_REMOVE:
275       filter->remove = g_value_get_boolean (value);
276       break;
277     case PROP_HYSTERESIS:
278       vad_set_hysteresis (filter->vad, g_value_get_uint64 (value));
279       break;
280     case PROP_THRESHOLD:
281       vad_set_threshold (filter->vad, g_value_get_int (value));
282       break;
283     case PROP_SQUASH:
284       filter->squash = g_value_get_boolean (value);
285       break;
286     case PROP_SILENT:
287       filter->silent = g_value_get_boolean (value);
288       break;
289     case PROP_MINIMUM_SILENCE_BUFFERS:
290       filter->minimum_silence_buffers = g_value_get_uint (value);
291       break;
292     case PROP_MINIMUM_SILENCE_TIME:
293       filter->minimum_silence_time = g_value_get_uint64 (value);
294       break;
295     default:
296       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297       break;
298   }
299 }
300 
301 static void
gst_remove_silence_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)302 gst_remove_silence_get_property (GObject * object, guint prop_id,
303     GValue * value, GParamSpec * pspec)
304 {
305   GstRemoveSilence *filter = GST_REMOVE_SILENCE (object);
306 
307   switch (prop_id) {
308     case PROP_REMOVE:
309       g_value_set_boolean (value, filter->remove);
310       break;
311     case PROP_HYSTERESIS:
312       g_value_set_uint64 (value, vad_get_hysteresis (filter->vad));
313       break;
314     case PROP_THRESHOLD:
315       g_value_set_int (value, vad_get_threshold_as_db (filter->vad));
316       break;
317     case PROP_SQUASH:
318       g_value_set_boolean (value, filter->squash);
319       break;
320     case PROP_SILENT:
321       g_value_set_boolean (value, filter->silent);
322       break;
323     case PROP_MINIMUM_SILENCE_BUFFERS:
324       g_value_set_uint (value, filter->minimum_silence_buffers);
325       break;
326     case PROP_MINIMUM_SILENCE_TIME:
327       g_value_set_uint64 (value, filter->minimum_silence_time);
328       break;
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331       break;
332   }
333 }
334 
335 static GstFlowReturn
gst_remove_silence_transform_ip(GstBaseTransform * trans,GstBuffer * inbuf)336 gst_remove_silence_transform_ip (GstBaseTransform * trans, GstBuffer * inbuf)
337 {
338   GstRemoveSilence *filter = NULL;
339   int frame_type;
340   GstMapInfo map;
341   gboolean consecutive_silence_reached;
342 
343   filter = GST_REMOVE_SILENCE (trans);
344 
345   gst_buffer_map (inbuf, &map, GST_MAP_READ);
346   frame_type =
347       vad_update (filter->vad, (gint16 *) map.data, map.size / sizeof (gint16));
348   gst_buffer_unmap (inbuf, &map);
349 
350   if (frame_type == VAD_SILENCE) {
351     GST_DEBUG ("Silence detected");
352     filter->consecutive_silence_buffers++;
353     if (GST_BUFFER_DURATION_IS_VALID (inbuf)) {
354       filter->consecutive_silence_time += inbuf->duration;
355     } else {
356       GST_WARNING
357           ("Invalid buffer duration, consecutive_silence_time update not possible");
358     }
359     if (filter->minimum_silence_buffers == 0
360         && filter->minimum_silence_time == 0) {
361       consecutive_silence_reached = TRUE;
362     } else {
363       consecutive_silence_reached =
364           (filter->minimum_silence_buffers > 0
365           && filter->consecutive_silence_buffers >=
366           filter->minimum_silence_buffers)
367           || (filter->minimum_silence_time > 0
368           && filter->consecutive_silence_time >= filter->minimum_silence_time);
369     }
370     if (!filter->silence_detected && consecutive_silence_reached) {
371       if (!filter->silent) {
372         if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
373           GstStructure *s;
374           GstMessage *m;
375           s = gst_structure_new ("removesilence", "silence_detected",
376               G_TYPE_UINT64, GST_BUFFER_PTS (inbuf) - filter->ts_offset, NULL);
377           m = gst_message_new_element (GST_OBJECT (filter), s);
378           gst_element_post_message (GST_ELEMENT (filter), m);
379         }
380       }
381       filter->silence_detected = TRUE;
382     }
383 
384     if (filter->remove && consecutive_silence_reached) {
385       GST_DEBUG ("Removing silence");
386       if (filter->squash) {
387         if (GST_BUFFER_DURATION_IS_VALID (inbuf)) {
388           filter->ts_offset += inbuf->duration;
389         } else {
390           GST_WARNING ("Invalid buffer duration: ts_offset not updated");
391         }
392       }
393       return GST_BASE_TRANSFORM_FLOW_DROPPED;
394     }
395 
396   } else {
397     filter->consecutive_silence_buffers = 0;
398     filter->consecutive_silence_time = 0;
399     if (filter->silence_detected) {
400       if (!filter->silent) {
401         if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
402           GstStructure *s;
403           GstMessage *m;
404           s = gst_structure_new ("removesilence", "silence_finished",
405               G_TYPE_UINT64, GST_BUFFER_PTS (inbuf) - filter->ts_offset, NULL);
406           m = gst_message_new_element (GST_OBJECT (filter), s);
407           gst_element_post_message (GST_ELEMENT (filter), m);
408         }
409       }
410       filter->silence_detected = FALSE;
411     }
412   }
413 
414   if (filter->squash && filter->ts_offset > 0) {
415     if (GST_BUFFER_PTS_IS_VALID (inbuf)) {
416       inbuf = gst_buffer_make_writable (inbuf);
417       GST_BUFFER_PTS (inbuf) -= filter->ts_offset;
418     } else {
419       GST_WARNING ("Invalid buffer pts, update not possibile");
420     }
421   }
422 
423   return GST_FLOW_OK;
424 }
425 
426 /*Plugin init functions*/
427 static gboolean
plugin_init(GstPlugin * plugin)428 plugin_init (GstPlugin * plugin)
429 {
430   return gst_element_register (plugin, "removesilence", GST_RANK_NONE,
431       gst_remove_silence_get_type ());
432 }
433 
434 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
435     GST_VERSION_MINOR,
436     removesilence,
437     "Removes silence from an audio stream",
438     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
439