From 61ee9918beea424c26496e104671af315f1d4973 Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Tue, 6 Sep 2011 22:11:06 -0400 Subject: [PATCH] spandsp: Adding spandsp plugin with spanplc element for packet loss concealment --- configure.ac | 13 ++ ext/Makefile.am | 8 ++ ext/spandsp/Makefile.am | 9 ++ ext/spandsp/gstspandsp.c | 40 ++++++ ext/spandsp/gstspanplc.c | 300 +++++++++++++++++++++++++++++++++++++++ ext/spandsp/gstspanplc.h | 60 ++++++++ 6 files changed, 430 insertions(+) create mode 100644 ext/spandsp/Makefile.am create mode 100644 ext/spandsp/gstspandsp.c create mode 100644 ext/spandsp/gstspanplc.c create mode 100644 ext/spandsp/gstspanplc.h diff --git a/configure.ac b/configure.ac index b41d54b888..03a367be77 100644 --- a/configure.ac +++ b/configure.ac @@ -1718,6 +1718,17 @@ AG_GST_CHECK_FEATURE(RTMP, [rtmp library], rtmp, [ AG_GST_PKG_CHECK_MODULES(RTMP, librtmp) ]) +dnl *** spandsp *** +translit(dnm, m, l) AM_CONDITIONAL(USE_SPANDSP, true) +AG_GST_CHECK_FEATURE(SPANDSP, [Spandsp], spandsp, [ + PKG_CHECK_MODULES(SPANDSP, spandsp >= 0.0.6, [ + HAVE_SPANDSP="yes" ], [ + HAVE_SPANDSP="no" + ]) +]) +AC_SUBST(SPANDSP_CFLAGS) +AC_SUBST(SPANDSP_LIBS) + dnl *** GSettings *** translit(dnm, m, l) AM_CONDITIONAL(USE_GSETTINGS, true) AG_GST_CHECK_FEATURE(GSETTINGS, [GSettings plugin], gsettings, [ @@ -1780,6 +1791,7 @@ AM_CONDITIONAL(USE_WILDMIDI, false) AM_CONDITIONAL(USE_SDL, false) AM_CONDITIONAL(USE_SNDFILE, false) AM_CONDITIONAL(USE_SOUNDTOUCH, false) +AM_CONDITIONAL(USE_SPANDSP, false) AM_CONDITIONAL(USE_SPC, false) AM_CONDITIONAL(USE_GME, false) AM_CONDITIONAL(USE_GSETTINGS, false) @@ -2029,6 +2041,7 @@ ext/schroedinger/Makefile ext/sdl/Makefile ext/sndfile/Makefile ext/soundtouch/Makefile +ext/spandsp/Makefile ext/teletextdec/Makefile ext/gme/Makefile ext/gsettings/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 2a6f8ec760..95ba3da754 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -324,6 +324,12 @@ else SOUNDTOUCH_DIR= endif +if USE_SPANDSP +SPANDSP_DIR = spandsp +else +SPANDSP_DIR = +endif + if USE_SPC SPC_DIR=spc else @@ -433,6 +439,7 @@ SUBDIRS=\ $(SMOOTHWAVE_DIR) \ $(SNDFILE_DIR) \ $(SOUNDTOUCH_DIR) \ + $(SPANDSP_DIR) \ $(GME_DIR) \ $(SPC_DIR) \ $(SWFDEC_DIR) \ @@ -485,6 +492,7 @@ DIST_SUBDIRS = \ sdl \ sndfile \ soundtouch \ + spandsp \ spc \ gme \ swfdec \ diff --git a/ext/spandsp/Makefile.am b/ext/spandsp/Makefile.am new file mode 100644 index 0000000000..46585958a4 --- /dev/null +++ b/ext/spandsp/Makefile.am @@ -0,0 +1,9 @@ +plugin_LTLIBRARIES = libgstspandsp.la + +libgstspandsp_la_SOURCES = gstspandsp.c gstspanplc.c +libgstspandsp_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(SPANDSP_CFLAGS) +libgstspandsp_la_LIBADD = $(SPANDSP_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) +libgstspandsp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstspandsp_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstspanplc.h diff --git a/ext/spandsp/gstspandsp.c b/ext/spandsp/gstspandsp.c new file mode 100644 index 0000000000..ad1c3a8171 --- /dev/null +++ b/ext/spandsp/gstspandsp.c @@ -0,0 +1,40 @@ +/* + * (C) 2011 Collabora Ltd. + * Contact: Youness Alaoui + * + * 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. + */ + + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstspanplc.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "spanplc", + GST_RANK_PRIMARY, GST_TYPE_SPAN_PLC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "spandsp", + "libspandsp plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/spandsp/gstspanplc.c b/ext/spandsp/gstspanplc.c new file mode 100644 index 0000000000..90a7c10920 --- /dev/null +++ b/ext/spandsp/gstspanplc.c @@ -0,0 +1,300 @@ +/* + * (C) 2011 Collabora Ltd. + * Contact: Youness Alaoui + * + * 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-spanplc + * + * The spanplc (Packet Loss Concealment) element provides a synthetic + * fill-in signal, to minimise the audible effect of lost packets in + * VoIP applications + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstspanplc.h" + +GST_BOILERPLATE (GstSpanPlc, gst_span_plc, GstElement, GST_TYPE_ELEMENT); + +GST_DEBUG_CATEGORY_STATIC (gst_span_plc_debug); +#define GST_CAT_DEFAULT gst_span_plc_debug + + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, signed = (bool) TRUE, " + "width = (int) 16, depth = (int) 16, " + "rate = (int) [ 1, MAX ], channels = (int) 1") + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, signed = (bool) TRUE, " + "width = (int) 16, depth = (int) 16, " + "rate = (int) [ 1, MAX ], channels = (int) 1") + ); + +static void gst_span_plc_dispose (GObject * object); + +static GstStateChangeReturn gst_span_plc_change_state (GstElement * element, + GstStateChange transition); +static gboolean gst_span_plc_setcaps_sink (GstPad * pad, GstCaps * caps); +static GstFlowReturn gst_span_plc_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_span_plc_event_sink (GstPad * pad, GstEvent * event); + +static void +gst_span_plc_base_init (gpointer gclass) +{ + GstElementClass *element_class = (GstElementClass *) gclass; + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + + gst_element_class_set_details_simple (element_class, "SpanDSP PLC", + "Filter/Effect/Audio", + "Adds packet loss concealment to audio", + "Youness Alaoui "); +} + +/* initialize the plugin's class */ +static void +gst_span_plc_class_init (GstSpanPlcClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->dispose = gst_span_plc_dispose; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_span_plc_change_state); + + GST_DEBUG_CATEGORY_INIT (gst_span_plc_debug, "spandlc", + 0, "spandlc's packet loss concealment"); +} + +static void +gst_span_plc_init (GstSpanPlc * plc, GstSpanPlcClass * gclass) +{ + GST_DEBUG_OBJECT (plc, "init"); + + plc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + plc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + + gst_pad_set_setcaps_function (plc->sinkpad, + GST_DEBUG_FUNCPTR (gst_span_plc_setcaps_sink)); + + gst_pad_set_getcaps_function (plc->srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_pad_set_getcaps_function (plc->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + + gst_pad_set_chain_function (plc->sinkpad, + GST_DEBUG_FUNCPTR (gst_span_plc_chain)); + + gst_pad_set_event_function (plc->sinkpad, + GST_DEBUG_FUNCPTR (gst_span_plc_event_sink)); + + gst_element_add_pad (GST_ELEMENT (plc), plc->srcpad); + gst_element_add_pad (GST_ELEMENT (plc), plc->sinkpad); + + plc->plc_state = NULL; + plc->last_stop = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (plc, "init complete"); +} + +static void +gst_span_plc_dispose (GObject * object) +{ + GstSpanPlc *plc = GST_SPAN_PLC (object); + + if (plc->plc_state) + plc_free (plc->plc_state); + plc->plc_state = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_span_plc_flush (GstSpanPlc * plc, gboolean renew) +{ + if (plc->plc_state) + plc_free (plc->plc_state); + if (renew) + plc->plc_state = plc_init (NULL); + else + plc->plc_state = NULL; + plc->last_stop = GST_CLOCK_TIME_NONE; +} + +static GstStateChangeReturn +gst_span_plc_change_state (GstElement * element, GstStateChange transition) +{ + GstSpanPlc *plc = GST_SPAN_PLC (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + gst_span_plc_flush (plc, TRUE); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_span_plc_flush (plc, FALSE); + default: + break; + } + + return ret; +} + +static gboolean +gst_span_plc_setcaps_sink (GstPad * pad, GstCaps * caps) +{ + GstSpanPlc *plc = GST_SPAN_PLC (gst_pad_get_parent (pad)); + GstStructure *s = NULL; + gboolean ret = FALSE; + + ret = gst_pad_set_caps (plc->srcpad, caps); + s = gst_caps_get_structure (caps, 0); + if (s) { + gst_structure_get_int (s, "rate", &plc->sample_rate); + GST_DEBUG_OBJECT (plc, "setcaps: got sample rate : %d", plc->sample_rate); + } + + gst_span_plc_flush (plc, TRUE); + gst_object_unref (plc); + + return ret; +} + +static GstFlowReturn +gst_span_plc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSpanPlc *plc = GST_SPAN_PLC (GST_PAD_PARENT (pad)); + GstClockTime buffer_duration; + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + plc->last_stop = GST_BUFFER_TIMESTAMP (buffer); + else + GST_WARNING_OBJECT (plc, "Buffer has no timestamp!"); + + if (GST_BUFFER_DURATION_IS_VALID (buffer)) { + buffer_duration = GST_BUFFER_DURATION (buffer); + } else { + GST_WARNING_OBJECT (plc, "Buffer has no duration!"); + buffer_duration = (GST_BUFFER_SIZE (buffer) / + (plc->sample_rate * sizeof (guint16))) * GST_SECOND; + GST_DEBUG_OBJECT (plc, "Buffer duration : %" GST_TIME_FORMAT, + GST_TIME_ARGS (buffer_duration)); + } + + plc->last_stop += buffer_duration; + + if (plc->plc_state->missing_samples != 0) + buffer = gst_buffer_make_writable (buffer); + plc_rx (plc->plc_state, (int16_t *) GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer) / 2); + + return gst_pad_push (plc->srcpad, buffer); +} + +static void +gst_span_plc_send_fillin (GstSpanPlc * plc, GstClockTime duration) +{ + guint buf_size; + GstBuffer *buffer = NULL; + + buf_size = ((float) duration / GST_SECOND) * plc->sample_rate; + buf_size *= sizeof (guint16); + buffer = gst_buffer_new_and_alloc (buf_size); + GST_DEBUG_OBJECT (plc, "Missing packet of %" GST_TIME_FORMAT + " == %d bytes", GST_TIME_ARGS (duration), buf_size); + plc_fillin (plc->plc_state, (int16_t *) GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer) / 2); + GST_BUFFER_TIMESTAMP (buffer) = plc->last_stop; + GST_BUFFER_DURATION (buffer) = duration; + gst_buffer_set_caps (buffer, GST_PAD_CAPS (plc->srcpad)); + gst_pad_push (plc->srcpad, buffer); +} + +static gboolean +gst_span_plc_event_sink (GstPad * pad, GstEvent * event) +{ + gboolean ret = FALSE; + GstSpanPlc *plc = GST_SPAN_PLC (gst_pad_get_parent (pad)); + + GST_DEBUG_OBJECT (plc, "received event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &time); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (update) { + /* time progressed without data, see if we can fill the gap with + * some concealment data */ + if (plc->last_stop < start) + gst_span_plc_send_fillin (plc, start - plc->last_stop); + } + plc->last_stop = start; + break; + } + case GST_EVENT_FLUSH_START: + gst_span_plc_flush (plc, TRUE); + break; + default: + break; + } + ret = gst_pad_push_event (plc->srcpad, event); + + gst_object_unref (plc); + + return ret; +newseg_wrong_format: + { + GST_DEBUG_OBJECT (plc, "received non TIME newsegment"); + gst_object_unref (plc); + return FALSE; + } +} diff --git a/ext/spandsp/gstspanplc.h b/ext/spandsp/gstspanplc.h new file mode 100644 index 0000000000..86c10ac232 --- /dev/null +++ b/ext/spandsp/gstspanplc.h @@ -0,0 +1,60 @@ +/* + * (C) 2011 Collabora Ltd. + * Contact: Youness Alaoui + * + * 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_SPANDSP_H__ +#define __GST_SPANDSP_H__ + +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SPAN_PLC (gst_span_plc_get_type()) +#define GST_SPAN_PLC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPAN_PLC,GstSpanPlc)) +#define GST_SPAN_PLC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPAN_PLC,GstSpanPlcClass)) +#define GST_IS_SPAN_PLC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPAN_PLC)) +#define GST_IS_SPAN_PLC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPAN_PLC)) + +typedef struct _GstSpanPlc GstSpanPlc; +typedef struct _GstSpanPlcClass GstSpanPlcClass; + +struct _GstSpanPlc +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + /* */ + plc_state_t *plc_state; + GstClockTime last_stop; + gint sample_rate; +}; + +struct _GstSpanPlcClass +{ + GstElementClass parent_class; +}; + +GType gst_span_plc_get_type (void); + +G_END_DECLS + +#endif