diff --git a/configure.ac b/configure.ac index 2b869b9c45..f1619c7e54 100644 --- a/configure.ac +++ b/configure.ac @@ -345,6 +345,7 @@ AG_GST_CHECK_PLUGIN(pcapparse) AG_GST_CHECK_PLUGIN(pnm) AG_GST_CHECK_PLUGIN(rawparse) AG_GST_CHECK_PLUGIN(real) +AG_GST_CHECK_PLUGIN(removesilence) AG_GST_CHECK_PLUGIN(rtpmux) AG_GST_CHECK_PLUGIN(rtpvp8) AG_GST_CHECK_PLUGIN(scaletempo) @@ -1912,6 +1913,7 @@ gst/pcapparse/Makefile gst/pnm/Makefile gst/rawparse/Makefile gst/real/Makefile +gst/removesilence/Makefile gst/rtpmux/Makefile gst/rtpvp8/Makefile gst/scaletempo/Makefile diff --git a/gst/removesilence/Makefile.am b/gst/removesilence/Makefile.am new file mode 100644 index 0000000000..c022ac1333 --- /dev/null +++ b/gst/removesilence/Makefile.am @@ -0,0 +1,12 @@ + +plugin_LTLIBRARIES = libgstremovesilence.la + +libgstremovesilence_la_SOURCES = gstremovesilence.c vad_private.c +libgstremovesilence_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstremovesilence_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) +libgstremovesilence_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstremovesilence_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = \ + gstremovesilence.h \ + vad_private.h diff --git a/gst/removesilence/gstremovesilence.c b/gst/removesilence/gstremovesilence.c new file mode 100644 index 0000000000..edb9af3ab1 --- /dev/null +++ b/gst/removesilence/gstremovesilence.c @@ -0,0 +1,262 @@ +/* GStreamer + * Copyright (C) 2011 Tiago Katcipis + * Copyright (C) 2011 Paulo Pizarro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-removesilence + * + * Removes all silence periods from an audio stream, dropping silence buffers. + * + * + * Example launch line + * |[ + * gst-launch -v -m filesrc location="audiofile" ! decodebin2 ! removesilence remove=true ! wavenc ! filesink location=without_audio.wav + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstremovesilence.h" + + +GST_DEBUG_CATEGORY_STATIC (gst_remove_silence_debug); +#define GST_CAT_DEFAULT gst_remove_silence_debug +#define DEFAULT_VAD_HYSTERESIS 480 /* 60 mseg */ + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_REMOVE, + PROP_HYSTERESIS +}; + + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw-int, " + "rate=[1, MAX], channels=1, endianness=BYTE_ORDER, " + "width=16, depth=16, signed=true")); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw-int, " + "rate=[1, MAX], channels=1, endianness=BYTE_ORDER, " + "width=16, depth=16, signed=true")); + + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_remove_silence_debug, "removesilence", 0, "removesilence element") + +GST_BOILERPLATE_FULL (GstRemoveSilence, gst_remove_silence, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + + +static void gst_remove_silence_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_remove_silence_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstFlowReturn gst_remove_silence_transform_ip (GstBaseTransform * base, + GstBuffer * buf); +static void gst_remove_silence_finalize (GObject * obj); +static void gst_remove_silence_reset (GstRemoveSilence * filter); + +/* GObject vmethod implementations */ + +static void +gst_remove_silence_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_set_details_simple (element_class, + "RemoveSilence", + "Filter/Effect/Audio", + "Removes all the silence periods from the audio stream.", + "Tiago Katcipis \n \ + Paulo Pizarro "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); +} + +/* initialize the removesilence's class */ +static void +gst_remove_silence_class_init (GstRemoveSilenceClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gobject_class->finalize = gst_remove_silence_finalize; + gobject_class->set_property = gst_remove_silence_set_property; + gobject_class->get_property = gst_remove_silence_get_property; + + g_object_class_install_property (gobject_class, PROP_REMOVE, + g_param_spec_boolean ("remove", "Remove", + "Set to true to remove silence from the stream, false otherwhise", + FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HYSTERESIS, + g_param_spec_uint64 ("hysteresis", + "Hysteresis", + "Set the hysteresis (on samples) used on the internal VAD", + 1, G_MAXUINT64, DEFAULT_VAD_HYSTERESIS, G_PARAM_READWRITE)); + + + GST_BASE_TRANSFORM_CLASS (klass)->transform_ip = + GST_DEBUG_FUNCPTR (gst_remove_silence_transform_ip); +} + +/* initialize the new element + * instantiate pads and add them to element + * set pad calback functions + * initialize instance structure + */ +static void +gst_remove_silence_init (GstRemoveSilence * filter, + GstRemoveSilenceClass * gclass) +{ + filter->vad = vad_new (DEFAULT_VAD_HYSTERESIS); + filter->remove = FALSE; + + if (!filter->vad) { + GST_DEBUG ("Error initializing VAD !!"); + return; + } + + gst_remove_silence_reset (filter); +} + +static void +gst_remove_silence_reset (GstRemoveSilence * filter) +{ + GST_DEBUG ("Reseting VAD"); + if (filter->vad) { + vad_reset (filter->vad); + } + GST_DEBUG ("VAD Reseted"); +} + +static void +gst_remove_silence_finalize (GObject * obj) +{ + GstRemoveSilence *filter = GST_REMOVE_SILENCE (obj); + GST_DEBUG ("Destroying VAD"); + vad_destroy (filter->vad); + filter->vad = NULL; + GST_DEBUG ("VAD Destroyed"); + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_remove_silence_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRemoveSilence *filter = GST_REMOVE_SILENCE (object); + + switch (prop_id) { + case PROP_REMOVE: + filter->remove = g_value_get_boolean (value); + break; + case PROP_HYSTERESIS: + vad_set_hysteresis (filter->vad, g_value_get_uint64 (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_remove_silence_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRemoveSilence *filter = GST_REMOVE_SILENCE (object); + + switch (prop_id) { + case PROP_REMOVE: + g_value_set_boolean (value, filter->remove); + break; + case PROP_HYSTERESIS: + g_value_set_uint64 (value, vad_get_hysteresis (filter->vad)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn +gst_remove_silence_transform_ip (GstBaseTransform * trans, GstBuffer * inbuf) +{ + GstRemoveSilence *filter = NULL; + int frame_type; + + filter = GST_REMOVE_SILENCE (trans); + + frame_type = + vad_update (filter->vad, (gint16 *) GST_BUFFER_DATA (inbuf), + GST_BUFFER_SIZE (inbuf) / sizeof (gint16)); + + if (frame_type == VAD_SILENCE) { + + GST_DEBUG ("Silence detected"); + + if (filter->remove) { + GST_DEBUG ("Removing silence"); + return GST_BASE_TRANSFORM_FLOW_DROPPED; + } + + } + + return GST_FLOW_OK; +} + +/*Plugin init functions*/ +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "removesilence", GST_RANK_NONE, + gst_remove_silence_get_type ()); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "removesilence", + "Removes silence from an audio stream", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/removesilence/gstremovesilence.h b/gst/removesilence/gstremovesilence.h new file mode 100644 index 0000000000..0592a37ff4 --- /dev/null +++ b/gst/removesilence/gstremovesilence.h @@ -0,0 +1,56 @@ +/* GStreamer + * Copyright (C) 2011 Tiago Katcipis + * Copyright (C) 2011 Paulo Pizarro + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_REMOVE_SILENCE_H__ +#define __GST_REMOVE_SILENCE_H__ + +#include +#include +#include "vad_private.h" + +G_BEGIN_DECLS + +#define GST_TYPE_REMOVE_SILENCE \ + (gst_remove_silence_get_type()) +#define GST_REMOVE_SILENCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_REMOVE_SILENCE,GstRemoveSilence)) +#define GST_REMOVE_SILENCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_REMOVE_SILENCE,GstRemoveSilenceClass)) +#define GST_IS_REMOVESILENCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_REMOVE_SILENCE)) +#define GST_IS_REMOVESILENCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_REMOVE_SILENCE)) + +typedef struct _GstRemoveSilence { + GstBaseTransform parent; + VADFilter* vad; + gboolean remove; +} GstRemoveSilence; + +typedef struct _GstRemoveSilenceClass { + GstBaseTransformClass parent_class; +} GstRemoveSilenceClass; + +GType gst_remove_silence_get_type (void); + +G_END_DECLS + +#endif /* __GST_REMOVE_SILENCE_H__ */ diff --git a/gst/removesilence/vad_private.c b/gst/removesilence/vad_private.c new file mode 100644 index 0000000000..f187a06149 --- /dev/null +++ b/gst/removesilence/vad_private.c @@ -0,0 +1,152 @@ +/* GStreamer + * Copyright (C) 2009 Tiago Katcipis + * Copyright (C) 2009 Paulo Pizarro + * Copyright (C) 2009 Rogério Santos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include "vad_private.h" + +#define VAD_POWER_ALPHA 0x0800 /* Q16 */ +#define VAD_POWER_THRESHOLD 0x000010C7 /* -60 dB (square wave) */ +#define VAD_ZCR_THRESHOLD 0 +#define VAD_BUFFER_SIZE 256 + + +union pgen +{ + guint64 a; + gpointer v; + guint64 *l; + guchar *b; + guint16 *w; + gint16 *s; +}; + +struct _cqueue_s +{ + union pgen base; + union pgen tail; + union pgen head; + gint size; +}; + +typedef struct _cqueue_s cqueue_t; + +struct _vad_s +{ + gint16 vad_buffer[VAD_BUFFER_SIZE]; + cqueue_t cqueue; + gint vad_state; + guint64 hysteresis; + guint64 vad_samples; + guint64 vad_power; + long vad_zcr; +}; + +VADFilter * +vad_new (guint64 hysteresis) +{ + VADFilter *vad = malloc (sizeof (VADFilter)); + vad_reset (vad); + vad->hysteresis = hysteresis; + return vad; +} + +void +vad_reset (VADFilter * vad) +{ + memset (vad, 0, sizeof (vad)); + vad->cqueue.base.s = vad->vad_buffer; + vad->cqueue.tail.a = vad->cqueue.head.a = 0; + vad->cqueue.size = VAD_BUFFER_SIZE; + vad->vad_state = VAD_SILENCE; +} + +void +vad_destroy (VADFilter * p) +{ + free (p); +} + +void +vad_set_hysteresis (struct _vad_s *p, guint64 hysteresis) +{ + p->hysteresis = hysteresis; +} + +guint64 +vad_get_hysteresis (struct _vad_s *p) +{ + return p->hysteresis; +} + +gint +vad_update (struct _vad_s * p, gint16 * data, gint len) +{ + guint64 tail; + gint frame_type; + gint16 sample; + gint i; + + for (i = 0; i < len; i++) { + p->vad_power = VAD_POWER_ALPHA * ((data[i] * data[i] >> 14) & 0xFFFF) + + (0xFFFF - VAD_POWER_ALPHA) * (p->vad_power >> 16) + + ((0xFFFF - VAD_POWER_ALPHA) * (p->vad_power & 0xFFFF) >> 16); + /* Update VAD buffer */ + p->cqueue.base.s[p->cqueue.head.a] = data[i]; + p->cqueue.head.a = (p->cqueue.head.a + 1) & (p->cqueue.size - 1); + if (p->cqueue.head.a == p->cqueue.tail.a) + p->cqueue.tail.a = (p->cqueue.tail.a + 1) & (p->cqueue.size - 1); + } + + tail = p->cqueue.tail.a; + p->vad_zcr = 0; + for (;;) { + sample = p->cqueue.base.s[tail]; + tail = (tail + 1) & (p->cqueue.size - 1); + if (tail == p->cqueue.head.a) + break; + p->vad_zcr += + ((sample & 0x8000) != (p->cqueue.base.s[tail] & 0x8000)) ? 1 : -1; + } + + frame_type = (p->vad_power > VAD_POWER_THRESHOLD + && p->vad_zcr < VAD_ZCR_THRESHOLD) ? VAD_VOICE : VAD_SILENCE; + + if (p->vad_state != frame_type) { + /* Voice to silence transition */ + if (p->vad_state == VAD_VOICE) { + p->vad_samples += len; + if (p->vad_samples >= p->hysteresis) { + p->vad_state = frame_type; + p->vad_samples = 0; + } + } else { + p->vad_state = frame_type; + p->vad_samples = 0; + } + } else { + p->vad_samples = 0; + } + + return p->vad_state; +} diff --git a/gst/removesilence/vad_private.h b/gst/removesilence/vad_private.h new file mode 100644 index 0000000000..5401f4184e --- /dev/null +++ b/gst/removesilence/vad_private.h @@ -0,0 +1,44 @@ +/* GStreamer + * Copyright (C) 2009 Tiago Katcipis + * Copyright (C) 2009 Paulo Pizarro + * Copyright (C) 2009 Rogério Santos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __VAD_FILTER_H__ +#define __VAD_FILTER_H__ + +#define VAD_SILENCE 0 +#define VAD_VOICE 1 + + +typedef struct _vad_s VADFilter; + +gint vad_update(VADFilter *p, gint16 *data, gint len); + +void vad_set_hysteresis(VADFilter *p, guint64 hysteresis); + +guint64 vad_get_hysteresis(VADFilter *p); + +VADFilter* vad_new(guint64 hysteresis); + +void vad_reset(VADFilter *p); + +void vad_destroy(VADFilter *p); + +#endif /* __VAD_FILTER__ */