diff options
Diffstat (limited to 'ext/amrwb')
-rw-r--r-- | ext/amrwb/Makefile.am | 18 | ||||
-rw-r--r-- | ext/amrwb/README | 12 | ||||
-rw-r--r-- | ext/amrwb/amrwb-code/Makefile.am | 44 | ||||
-rw-r--r-- | ext/amrwb/amrwb-code/run.sh | 6 | ||||
-rw-r--r-- | ext/amrwb/gstamrwb.c | 44 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbdec.c | 297 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbdec.h | 69 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbenc.c | 251 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbenc.h | 69 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbparse.c | 473 | ||||
-rw-r--r-- | ext/amrwb/gstamrwbparse.h | 65 |
11 files changed, 1348 insertions, 0 deletions
diff --git a/ext/amrwb/Makefile.am b/ext/amrwb/Makefile.am new file mode 100644 index 00000000..3efa09d8 --- /dev/null +++ b/ext/amrwb/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = amrwb-code +plugin_LTLIBRARIES = libgstamrwb.la + +libgstamrwb_la_SOURCES = \ + gstamrwb.c \ + gstamrwbdec.c \ + gstamrwbenc.c \ + gstamrwbparse.c + +libgstamrwb_la_CFLAGS = $(GST_CFLAGS) $(AMRWB_CFLAGS) -Iamrwb-code/ +libgstamrwb_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) amrwb-code/libamrwb.la +libgstamrwb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = \ + gstamrwbdec.h \ + gstamrwbenc.h \ + gstamrwbparse.h + diff --git a/ext/amrwb/README b/ext/amrwb/README new file mode 100644 index 00000000..82d33715 --- /dev/null +++ b/ext/amrwb/README @@ -0,0 +1,12 @@ +Compiling AMRWB codec: +====================== + +To compile the amrwb codec, you need to download the source code from +"http://www.3gpp.org/ftp/Specs/html-info/26204.htm" and uncompress the +files inside an amrwb-code directory; + +or execute this commands: +$cd amrwb-code +$sh ./run.sh + +and run the "autogen" script again. diff --git a/ext/amrwb/amrwb-code/Makefile.am b/ext/amrwb/amrwb-code/Makefile.am new file mode 100644 index 00000000..a11be20c --- /dev/null +++ b/ext/amrwb/amrwb-code/Makefile.am @@ -0,0 +1,44 @@ +noinst_LTLIBRARIES = libamrwb.la + +libamrwb_la_SOURCES = \ + enc_acelp.c \ + enc_dtx.c \ + enc_gain.c \ + enc_if.c \ + enc_lpc.c \ + enc_main.c \ + enc_rom.c \ + enc_util.c \ + if_rom.c \ + dec_acelp.c \ + dec_dtx.c \ + dec_gain.c \ + dec_if.c \ + dec_lpc.c \ + dec_main.c \ + dec_rom.c \ + dec_util.c + +libamrwb_la_CFLAGS = -I./amrwb-code/c-code +libamrwb_la_LIBADD = $(GST_BASE_LIBS) $(AMRWB_LIBS) +libamrwb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = \ + dec_acelp.h \ + dec_dtx.h \ + dec_gain.h \ + dec.h \ + dec_if.h \ + dec_lpc.h \ + dec_main.h \ + dec_util.h \ + enc_acelp.h \ + enc_dtx.h \ + enc_gain.h \ + enc.h \ + enc_if.h \ + enc_lpc.h \ + enc_main.h \ + enc_util.h \ + if_rom.h \ + typedef.h diff --git a/ext/amrwb/amrwb-code/run.sh b/ext/amrwb/amrwb-code/run.sh new file mode 100644 index 00000000..323f451a --- /dev/null +++ b/ext/amrwb/amrwb-code/run.sh @@ -0,0 +1,6 @@ +wget http://www.3gpp.org/ftp/Specs/archive/26_series/26.204/26204-600.zip +unzip 26204-600.zip +unzip 26204-600_ANSI-C_source_code.zip +mv c-code/* . +rm -rf c-code/ 26204-600.zip 26204-600_ANSI-C_source_code.zip +echo "" >> typedef.h # to remove compilation warning (no newline at end of file) diff --git a/ext/amrwb/gstamrwb.c b/ext/amrwb/gstamrwb.c new file mode 100644 index 00000000..6974dc25 --- /dev/null +++ b/ext/amrwb/gstamrwb.c @@ -0,0 +1,44 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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 "config.h" +#endif + +#include "gstamrwbdec.h" +#include "gstamrwbenc.h" +#include "gstamrwbparse.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "amrwbdec", + GST_RANK_PRIMARY, GST_TYPE_AMRWBDEC) && + gst_element_register (plugin, "amrwbparse", + GST_RANK_PRIMARY, GST_TYPE_AMRWBPARSE) && + gst_element_register (plugin, "amrwbenc", + GST_RANK_NONE, GST_TYPE_AMRWBENC); +} + + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "amrwb", + "Adaptive Multi-Rate Wide-Band", + plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE, GST_ORIGIN); diff --git a/ext/amrwb/gstamrwbdec.c b/ext/amrwb/gstamrwbdec.c new file mode 100644 index 00000000..0feed2bd --- /dev/null +++ b/ext/amrwb/gstamrwbdec.c @@ -0,0 +1,297 @@ +/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin + * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * 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 "config.h" +#endif + +#include "gstamrwbdec.h" + +static GstElementDetails gst_amrwbdec_details = { + "AMR-WB decoder", + "Codec/Decoder/Audio", + "Adaptive Multi-Rate Wideband audio decoder", + "Renato Araujo <renato.filho@indt.org.br>" +}; + + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR-WB, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "signed = (boolean) TRUE, " + "endianness = (int) BYTE_ORDER, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +extern const UWord8 block_size[]; + +static void gst_amrwbdec_base_init (gpointer klass); +static void gst_amrwbdec_class_init (GstAmrwbDecClass * klass); +static void gst_amrwbdec_init (GstAmrwbDec * amrwbdec, + GstAmrwbDecClass * klass); + +static gboolean gst_amrwbdec_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps); +static GstStateChangeReturn gst_amrwbdec_state_change (GstElement * element, + GstStateChange transition); + +GST_BOILERPLATE (GstAmrwbDec, gst_amrwbdec, GstElement, GST_TYPE_ELEMENT) + + static void gst_amrwbdec_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details (element_class, &gst_amrwbdec_details); +} + +static void +gst_amrwbdec_class_init (GstAmrwbDecClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + element_class->change_state = gst_amrwbdec_state_change; +} + +static void +gst_amrwbdec_init (GstAmrwbDec * amrwbdec, GstAmrwbDecClass * klass) +{ + /* create the sink pad */ + amrwbdec->sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), + "sink"); + gst_pad_set_setcaps_function (amrwbdec->sinkpad, gst_amrwbdec_setcaps); + gst_pad_set_event_function (amrwbdec->sinkpad, gst_amrwbdec_event); + gst_pad_set_chain_function (amrwbdec->sinkpad, gst_amrwbdec_chain); + gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->sinkpad); + + /* create the src pad */ + amrwbdec->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get (&src_template), + "src"); + gst_pad_use_fixed_caps (amrwbdec->srcpad); + gst_element_add_pad (GST_ELEMENT (amrwbdec), amrwbdec->srcpad); + + amrwbdec->adapter = gst_adapter_new (); + + /* init rest */ + amrwbdec->handle = NULL; + amrwbdec->channels = 0; + amrwbdec->rate = 0; + amrwbdec->duration = 0; + amrwbdec->ts = -1; +} + +static gboolean +gst_amrwbdec_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstAmrwbDec *amrwbdec; + GstCaps *copy; + + amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); + + structure = gst_caps_get_structure (caps, 0); + + /* get channel count */ + gst_structure_get_int (structure, "channels", &amrwbdec->channels); + gst_structure_get_int (structure, "rate", &amrwbdec->rate); + + /* create reverse caps */ + copy = gst_caps_new_simple ("audio/x-raw-int", + "channels", G_TYPE_INT, amrwbdec->channels, + "width", G_TYPE_INT, 16, + "depth", G_TYPE_INT, 16, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "rate", G_TYPE_INT, amrwbdec->rate, "signed", G_TYPE_BOOLEAN, TRUE, NULL); + + amrwbdec->duration = gst_util_uint64_scale_int (GST_SECOND, L_FRAME16k, + amrwbdec->rate * amrwbdec->channels); + + gst_pad_set_caps (amrwbdec->srcpad, copy); + gst_caps_unref (copy); + + gst_object_unref (amrwbdec); + + return TRUE; +} + +static gboolean +gst_amrwbdec_event (GstPad * pad, GstEvent * event) +{ + GstAmrwbDec *amrwbdec; + gboolean ret = TRUE; + + amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + ret = gst_pad_push_event (amrwbdec->srcpad, event); + break; + case GST_EVENT_FLUSH_STOP: + ret = gst_pad_push_event (amrwbdec->srcpad, event); + gst_adapter_clear (amrwbdec->adapter); + amrwbdec->ts = -1; + break; + case GST_EVENT_EOS: + gst_adapter_clear (amrwbdec->adapter); + ret = gst_pad_push_event (amrwbdec->srcpad, event); + break; + default: + ret = gst_pad_push_event (amrwbdec->srcpad, event); + break; + } + gst_object_unref (amrwbdec); + + return ret; +} + +static GstFlowReturn +gst_amrwbdec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAmrwbDec *amrwbdec; + GstFlowReturn ret = GST_FLOW_OK; + + amrwbdec = GST_AMRWBDEC (gst_pad_get_parent (pad)); + + if (amrwbdec->rate == 0 || amrwbdec->channels == 0) { + GST_ELEMENT_ERROR (amrwbdec, STREAM, TYPE_NOT_FOUND, (NULL), + ("Decoder is not initialized")); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + amrwbdec->ts = GST_BUFFER_TIMESTAMP (buffer); + + gst_adapter_push (amrwbdec->adapter, buffer); + + while (TRUE) { + GstBuffer *out; + UWord8 *data; + Word16 block, mode; + + + if (gst_adapter_available (amrwbdec->adapter) < 1) + break; + + data = (UWord8 *) gst_adapter_peek (amrwbdec->adapter, 1); + + /* get size */ + mode = (Word16) (data[0] >> 3) & 0x0F; + block = block_size[mode]; + + if (gst_adapter_available (amrwbdec->adapter) < block) { + break; + } + + /* the library seems to write into the source data, hence + * the copy. */ + data = (UWord8 *) gst_adapter_take (amrwbdec->adapter, block); + + /* get output */ + out = gst_buffer_new_and_alloc (sizeof (Word16) * L_FRAME16k); + + GST_BUFFER_DURATION (out) = amrwbdec->duration; + GST_BUFFER_TIMESTAMP (out) = amrwbdec->ts; + + if (amrwbdec->ts != -1) + amrwbdec->ts += GST_BUFFER_DURATION (out); + + gst_buffer_set_caps (out, GST_PAD_CAPS (amrwbdec->srcpad)); + + /* decode */ + D_IF_decode (amrwbdec->handle, data, + (Word16 *) GST_BUFFER_DATA (out), _good_frame); + + g_free (data); + + /* play */ + ret = gst_pad_push (amrwbdec->srcpad, out); + } + +done: + + gst_object_unref (amrwbdec); + return ret; + +} + +static GstStateChangeReturn +gst_amrwbdec_state_change (GstElement * element, GstStateChange transition) +{ + GstAmrwbDec *amrwbdec; + GstStateChangeReturn ret; + + amrwbdec = GST_AMRWBDEC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!(amrwbdec->handle = D_IF_init ())) + goto init_failed; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (amrwbdec->adapter); + amrwbdec->rate = 0; + amrwbdec->channels = 0; + amrwbdec->ts = -1; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + D_IF_exit (amrwbdec->handle); + break; + default: + break; + } + + return ret; + + /* ERRORS */ +init_failed: + { + GST_ELEMENT_ERROR (amrwbdec, LIBRARY, INIT, (NULL), + ("Failed to open AMR Decoder")); + return GST_STATE_CHANGE_FAILURE; + } +} diff --git a/ext/amrwb/gstamrwbdec.h b/ext/amrwb/gstamrwbdec.h new file mode 100644 index 00000000..854c741f --- /dev/null +++ b/ext/amrwb/gstamrwbdec.h @@ -0,0 +1,69 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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_AMRWBDEC_H__ +#define __GST_AMRWBDEC_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include "typedef.h" +#include "dec_if.h" + +G_BEGIN_DECLS + +#define GST_TYPE_AMRWBDEC \ + (gst_amrwbdec_get_type()) +#define GST_AMRWBDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBDEC, GstAmrwbDec)) +#define GST_AMRWBDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBDEC, GstAmrwbDec)) +#define GST_IS_AMRWBDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBDEC)) +#define GST_IS_AMRWBDEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBDEC)) + +typedef struct _GstAmrwbDec GstAmrwbDec; +typedef struct _GstAmrwbDecClass GstAmrwbDecClass; + +struct _GstAmrwbDec { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + guint64 ts; + + GstAdapter *adapter; + + /* library handle */ + void *handle; + + /* output settings */ + gint channels, rate; + gint duration; +}; + +struct _GstAmrwbDecClass { + GstElementClass parent_class; +}; + +GType gst_amrwbdec_get_type (void); + +G_END_DECLS + +#endif /* __GST_AMRWBDEC_H__ */ diff --git a/ext/amrwb/gstamrwbenc.c b/ext/amrwb/gstamrwbenc.c new file mode 100644 index 00000000..ad2ccecc --- /dev/null +++ b/ext/amrwb/gstamrwbenc.c @@ -0,0 +1,251 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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 "config.h" +#endif + +#include "gstamrwbenc.h" + +static GstElementDetails gst_amrwbenc_details = { + "AMR-WB encoder", + "Codec/Encoder/Audio", + "Adaptive Multi-Rate Wideband audio encoder", + "Renato Araujo <renato.filho@indt.org.br>" +}; + + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) 16, " + "depth = (int) 16, " + "signed = (boolean) TRUE, " + "endianness = (int) BYTE_ORDER, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR-WB, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +static void gst_amrwbenc_base_init (gpointer klass); +static void gst_amrwbenc_class_init (GstAmrwbEncClass * klass); +static void gst_amrwbenc_init (GstAmrwbEnc * amrwbenc, + GstAmrwbEncClass * klass); +static void gst_amrwbenc_finalize (GObject * object); + +static GstFlowReturn gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps); +static GstStateChangeReturn gst_amrwbenc_state_change (GstElement * element, + GstStateChange transition); + + +GST_BOILERPLATE (GstAmrwbEnc, gst_amrwbenc, GstElement, GST_TYPE_ELEMENT) + + static void gst_amrwbenc_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details (element_class, &gst_amrwbenc_details); +} + +static void +gst_amrwbenc_class_init (GstAmrwbEncClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + object_class->finalize = gst_amrwbenc_finalize; + + element_class->change_state = gst_amrwbenc_state_change; +} + +static void +gst_amrwbenc_init (GstAmrwbEnc * amrwbenc, GstAmrwbEncClass * klass) +{ + /* create the sink pad */ + amrwbenc->sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), + "sink"); + gst_pad_set_setcaps_function (amrwbenc->sinkpad, gst_amrwbenc_setcaps); + gst_pad_set_chain_function (amrwbenc->sinkpad, gst_amrwbenc_chain); + gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->sinkpad); + + /* create the src pad */ + amrwbenc->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get (&src_template), + "src"); + gst_pad_use_fixed_caps (amrwbenc->srcpad); + gst_element_add_pad (GST_ELEMENT (amrwbenc), amrwbenc->srcpad); + + amrwbenc->adapter = gst_adapter_new (); + + /* init rest */ + amrwbenc->handle = NULL; + amrwbenc->channels = 0; + amrwbenc->rate = 0; + amrwbenc->ts = 0; +} + +static void +gst_amrwbenc_finalize (GObject * object) +{ + GstAmrwbEnc *amrwbenc; + + amrwbenc = GST_AMRWBENC (object); + + g_object_unref (G_OBJECT (amrwbenc->adapter)); + amrwbenc->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_amrwbenc_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstAmrwbEnc *amrwbenc; + GstCaps *copy; + + amrwbenc = GST_AMRWBENC (GST_PAD_PARENT (pad)); + + structure = gst_caps_get_structure (caps, 0); + + /* get channel count */ + gst_structure_get_int (structure, "channels", &amrwbenc->channels); + gst_structure_get_int (structure, "rate", &amrwbenc->rate); + + /* this is not wrong but will sound bad */ + if (amrwbenc->channels != 1) { + GST_WARNING ("amrwbdec is only optimized for mono channels"); + } + if (amrwbenc->rate != 16000) { + GST_WARNING ("amrwbdec is only optimized for 16000 Hz samplerate"); + } + + /* create reverse caps */ + copy = gst_caps_new_simple ("audio/AMR-WB", + "channels", G_TYPE_INT, amrwbenc->channels, + "rate", G_TYPE_INT, amrwbenc->rate, NULL); + + gst_pad_set_caps (amrwbenc->srcpad, copy); + gst_caps_unref (copy); + + return TRUE; +} + +static GstFlowReturn +gst_amrwbenc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAmrwbEnc *amrwbenc; + GstFlowReturn ret = GST_FLOW_OK; + const int buffer_size = sizeof (Word16) * L_FRAME16k; + + amrwbenc = GST_AMRWBENC (gst_pad_get_parent (pad)); + + g_return_val_if_fail (amrwbenc->handle, GST_FLOW_WRONG_STATE); + + if (amrwbenc->rate == 0 || amrwbenc->channels == 0) { + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + amrwbenc->ts = GST_BUFFER_TIMESTAMP (buffer); + + ret = GST_FLOW_OK; + gst_adapter_push (amrwbenc->adapter, buffer); + + /* Collect samples until we have enough for an output frame */ + while (gst_adapter_available (amrwbenc->adapter) >= buffer_size) { + GstBuffer *out; + guint8 *data; + gint outsize; + + out = gst_buffer_new_and_alloc (buffer_size); + GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / + (amrwbenc->rate * amrwbenc->channels); + GST_BUFFER_TIMESTAMP (out) = amrwbenc->ts; + amrwbenc->ts += GST_BUFFER_DURATION (out); + gst_buffer_set_caps (out, gst_pad_get_caps (amrwbenc->srcpad)); + + data = (guint8 *) gst_adapter_peek (amrwbenc->adapter, buffer_size); + + /* encode */ + outsize = E_IF_encode (amrwbenc->handle, 0, (Word16 *) data, + (UWord8 *) GST_BUFFER_DATA (out), 0); + + gst_adapter_flush (amrwbenc->adapter, buffer_size); + GST_BUFFER_SIZE (out) = outsize; + + ret = gst_pad_push (amrwbenc->srcpad, out); + } + +done: + + gst_object_unref (amrwbenc); + return ret; + +} + +static GstStateChangeReturn +gst_amrwbenc_state_change (GstElement * element, GstStateChange transition) +{ + GstAmrwbEnc *amrwbenc; + GstStateChangeReturn ret; + + amrwbenc = GST_AMRWBENC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!(amrwbenc->handle = E_IF_init ())) + return GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + amrwbenc->ts = 0; + gst_adapter_clear (amrwbenc->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + E_IF_exit (amrwbenc->handle); + break; + default: + break; + } + + return ret; +} diff --git a/ext/amrwb/gstamrwbenc.h b/ext/amrwb/gstamrwbenc.h new file mode 100644 index 00000000..e1788102 --- /dev/null +++ b/ext/amrwb/gstamrwbenc.h @@ -0,0 +1,69 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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_AMRWBENC_H__ +#define __GST_AMRWBENC_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include "enc_if.h" +#include "typedef.h" + + +G_BEGIN_DECLS + +#define GST_TYPE_AMRWBENC \ + (gst_amrwbenc_get_type()) +#define GST_AMRWBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBENC, GstAmrwbEnc)) +#define GST_AMRWBENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBENC, GstAmrwbEnc)) +#define GST_IS_AMRWBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBENC)) +#define GST_IS_AMRWBENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBENC)) + +typedef struct _GstAmrwbEnc GstAmrwbEnc; +typedef struct _GstAmrwbEncClass GstAmrwbEncClass; + +struct _GstAmrwbEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + guint64 ts; + + GstAdapter *adapter; + + /* library handle */ + void *handle; + + /* input settings */ + gint channels, rate; +}; + +struct _GstAmrwbEncClass { + GstElementClass parent_class; +}; + +GType gst_amrwbenc_get_type (void); + +G_END_DECLS + +#endif /* __GST_AMRWBENC_H__ */ diff --git a/ext/amrwb/gstamrwbparse.c b/ext/amrwb/gstamrwbparse.c new file mode 100644 index 00000000..9cd03371 --- /dev/null +++ b/ext/amrwb/gstamrwbparse.c @@ -0,0 +1,473 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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 "config.h" +#endif + +#include <string.h> + +#include "typedef.h" +#include "gstamrwbparse.h" +#include "dec_if.h" + + +GST_DEBUG_CATEGORY_STATIC (amrwbparse_debug); +#define GST_CAT_DEFAULT amrwbparse_debug + +static GstElementDetails gst_amrwbparse_details = { + "AMR-WB parser", + "Codec/Parser/Audio", + "Adaptive Multi-Rate WideBand audio parser", + "Renato Filho <renato.filho@indt.org.br>" +}; + + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR-WB, " + "rate = (int) 16000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-amr-wb-sh") + ); + +extern const UWord8 block_size[]; + +static void gst_amrwbparse_base_init (gpointer klass); +static void gst_amrwbparse_class_init (GstAmrwbParseClass * klass); +static void gst_amrwbparse_init (GstAmrwbParse * amrwbparse, + GstAmrwbParseClass * klass); + +static const GstQueryType *gst_amrwbparse_querytypes (GstPad * pad); +static gboolean gst_amrwbparse_query (GstPad * pad, GstQuery * query); + +static GstFlowReturn gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer); +static void gst_amrwbparse_loop (GstPad * pad); +static gboolean gst_amrwbparse_sink_activate (GstPad * sinkpad); +static gboolean gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, + gboolean active); +static GstStateChangeReturn gst_amrwbparse_state_change (GstElement * element, + GstStateChange transition); + +GST_BOILERPLATE (GstAmrwbParse, gst_amrwbparse, GstElement, GST_TYPE_ELEMENT); + +static void +gst_amrwbparse_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details (element_class, &gst_amrwbparse_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + +} + +static void +gst_amrwbparse_class_init (GstAmrwbParseClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + element_class->change_state = gst_amrwbparse_state_change; + + GST_DEBUG_CATEGORY_INIT (amrwbparse_debug, + "amrwbparse", 0, "AMR-WB stream parsing"); +} + +static void +gst_amrwbparse_init (GstAmrwbParse * amrwbparse, GstAmrwbParseClass * klass) +{ + /* create the sink pad */ + amrwbparse->sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), + "sink"); + gst_pad_set_chain_function (amrwbparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_amrwbparse_chain)); + + gst_pad_set_activate_function (amrwbparse->sinkpad, + gst_amrwbparse_sink_activate); + gst_pad_set_activatepull_function (amrwbparse->sinkpad, + gst_amrwbparse_sink_activate_pull); + + gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->sinkpad); + + /* create the src pad */ + amrwbparse->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get (&src_template), + "src"); + gst_pad_set_query_function (amrwbparse->srcpad, + GST_DEBUG_FUNCPTR (gst_amrwbparse_query)); + gst_pad_set_query_type_function (amrwbparse->srcpad, + GST_DEBUG_FUNCPTR (gst_amrwbparse_querytypes)); + gst_element_add_pad (GST_ELEMENT (amrwbparse), amrwbparse->srcpad); + + amrwbparse->adapter = gst_adapter_new (); + + /* init rest */ + amrwbparse->ts = 0; +} + +static const GstQueryType * +gst_amrwbparse_querytypes (GstPad * pad) +{ + static const GstQueryType list[] = { + GST_QUERY_POSITION, + 0 + }; + + return list; +} + +static gboolean +gst_amrwbparse_query (GstPad * pad, GstQuery * query) +{ + GstAmrwbParse *amrwbparse; + gboolean res = TRUE; + + amrwbparse = GST_AMRWBPARSE (GST_PAD_PARENT (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + gint64 cur; + + gst_query_parse_position (query, &format, NULL); + + if (format != GST_FORMAT_TIME) { + res = FALSE; + break; + } + + cur = amrwbparse->ts; + + gst_query_set_position (query, GST_FORMAT_TIME, cur); + res = TRUE; + break; + } + case GST_QUERY_DURATION: + { + GstFormat format; + gint64 tot; + GstPad *peer; + + gst_query_parse_duration (query, &format, NULL); + + if (format != GST_FORMAT_TIME) { + res = FALSE; + break; + } + + tot = -1; + + peer = gst_pad_get_peer (amrwbparse->sinkpad); + if (peer) { + GstFormat pformat; + gint64 pcur, ptot; + + pformat = GST_FORMAT_BYTES; + res = gst_pad_query_position (peer, &pformat, &pcur); + res = gst_pad_query_duration (peer, &pformat, &ptot); + gst_object_unref (GST_OBJECT (peer)); + if (res) { + tot = amrwbparse->ts * ((gdouble) ptot / pcur); + } + } + gst_query_set_duration (query, GST_FORMAT_TIME, tot); + res = TRUE; + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (amrwbparse); + return res; +} + + +/* + * Data reading. + */ + +/* streaming mode */ +static GstFlowReturn +gst_amrwbparse_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAmrwbParse *amrwbparse; + GstFlowReturn res = GST_FLOW_OK; + gint block, mode; + const guint8 *data; + GstBuffer *out; + + amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); + + gst_adapter_push (amrwbparse->adapter, buffer); + + /* init */ + if (amrwbparse->need_header) { + + if (gst_adapter_available (amrwbparse->adapter) < 9) + goto done; + + data = gst_adapter_peek (amrwbparse->adapter, 9); + if (memcmp (data, "#!AMR-WB\n", 9) != 0) + goto done; + + gst_adapter_flush (amrwbparse->adapter, 9); + + amrwbparse->need_header = FALSE; + } + + while (TRUE) { + if (gst_adapter_available (amrwbparse->adapter) < 1) + break; + + data = gst_adapter_peek (amrwbparse->adapter, 1); + + /* get size */ + mode = (data[0] >> 3) & 0x0F; + block = block_size[mode] + 1; /* add one for the mode */ + + if (gst_adapter_available (amrwbparse->adapter) < block) + break; + + out = gst_buffer_new_and_alloc (block); + + data = gst_adapter_peek (amrwbparse->adapter, block); + memcpy (GST_BUFFER_DATA (out), data, block); + + /* output */ + GST_BUFFER_DURATION (out) = GST_SECOND * L_FRAME16k / 16000; + GST_BUFFER_TIMESTAMP (out) = amrwbparse->ts; + amrwbparse->ts += GST_BUFFER_DURATION (out); + gst_buffer_set_caps (out, + (GstCaps *) gst_pad_get_pad_template_caps (amrwbparse->srcpad)); + + res = gst_pad_push (amrwbparse->srcpad, out); + + gst_adapter_flush (amrwbparse->adapter, block); + } +done: + + gst_object_unref (amrwbparse); + return res; +} + +static gboolean +gst_amrwbparse_read_header (GstAmrwbParse * amrwbparse) +{ + GstBuffer *buffer; + gboolean ret = TRUE; + guint8 *data; + gint size; + const guint8 magic_number_size = 9; /* sizeof("#!AMR-WB\n")-1 */ + + if (GST_FLOW_OK != gst_pad_pull_range (amrwbparse->sinkpad, + amrwbparse->offset, magic_number_size, &buffer)) { + ret = FALSE; + goto done; + } + + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + if (size < magic_number_size) { + /* not enough */ + ret = FALSE; + goto done; + } + + if (memcmp (data, "#!AMR-WB\n", magic_number_size)) { + /* no header */ + ret = FALSE; + goto done; + } + + amrwbparse->offset += magic_number_size; + +done: + + gst_buffer_unref (buffer); + return ret; + +} + +/* random access mode, could just read a fixed size buffer and push it to + * the chain function but we don't... */ +static void +gst_amrwbparse_loop (GstPad * pad) +{ + GstAmrwbParse *amrwbparse; + GstBuffer *buffer; + guint8 *data; + gint size; + gint block, mode; + GstFlowReturn ret = GST_FLOW_OK; + + amrwbparse = GST_AMRWBPARSE (gst_pad_get_parent (pad)); + + /* init */ + if (amrwbparse->need_header) { + gboolean got_header; + + got_header = gst_amrwbparse_read_header (amrwbparse); + if (!got_header) { + GST_LOG_OBJECT (amrwbparse, "could not read header"); + goto need_pause; + } + amrwbparse->need_header = FALSE; + } + + ret = gst_pad_pull_range (amrwbparse->sinkpad, + amrwbparse->offset, 1, &buffer); + + if (ret != GST_FLOW_OK) + goto eos; + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + /* get size */ + mode = (data[0] >> 3) & 0x0F; + block = block_size[mode]; /* add one for the mode */ + + gst_buffer_unref (buffer); + + ret = gst_pad_pull_range (amrwbparse->sinkpad, + amrwbparse->offset, block, &buffer); + + if (ret != GST_FLOW_OK) + goto need_pause; + + amrwbparse->offset += block; + + /* output */ + GST_BUFFER_DURATION (buffer) = GST_SECOND * L_FRAME16k / 16000; + GST_BUFFER_TIMESTAMP (buffer) = amrwbparse->ts; + amrwbparse->ts += GST_BUFFER_DURATION (buffer); + gst_buffer_set_caps (buffer, + (GstCaps *) gst_pad_get_pad_template_caps (amrwbparse->srcpad)); + + ret = gst_pad_push (amrwbparse->srcpad, buffer); + if (ret != GST_FLOW_OK) + goto need_pause; + + goto done; + +eos: + if (ret == GST_FLOW_UNEXPECTED) { + gst_pad_push_event (amrwbparse->srcpad, gst_event_new_eos ()); + gst_pad_pause_task (pad); + goto done; + } else { + GST_LOG_OBJECT (amrwbparse, "pausing task %d", ret); + gst_pad_pause_task (pad); + goto done; + } + +need_pause: + GST_LOG_OBJECT (amrwbparse, "pausing task"); + gst_pad_pause_task (pad); + goto done; + +done: + + gst_object_unref (amrwbparse); + +} + +static gboolean +gst_amrwbparse_sink_activate (GstPad * sinkpad) +{ + GstAmrwbParse *amrwbparse; + + amrwbparse = GST_AMRWBPARSE (GST_PAD_PARENT (sinkpad)); + if (gst_pad_check_pull_range (sinkpad)) { + return gst_pad_activate_pull (sinkpad, TRUE); + } else { + amrwbparse->seekable = FALSE; + return gst_pad_activate_push (sinkpad, TRUE); + } +} + + +static gboolean +gst_amrwbparse_sink_activate_pull (GstPad * sinkpad, gboolean active) +{ + gboolean result; + GstAmrwbParse *amrwbparse; + + amrwbparse = GST_AMRWBPARSE (GST_PAD_PARENT (sinkpad)); + if (active) { + amrwbparse->need_header = TRUE; + amrwbparse->seekable = TRUE; + amrwbparse->ts = 0; + /* if we have a scheduler we can start the task */ + result = gst_pad_start_task (sinkpad, + (GstTaskFunction) gst_amrwbparse_loop, sinkpad); + } else { + result = gst_pad_stop_task (sinkpad); + } + + return result; +} + + +static GstStateChangeReturn +gst_amrwbparse_state_change (GstElement * element, GstStateChange transition) +{ + GstAmrwbParse *amrwbparse; + GstStateChangeReturn ret; + + amrwbparse = GST_AMRWBPARSE (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} diff --git a/ext/amrwb/gstamrwbparse.h b/ext/amrwb/gstamrwbparse.h new file mode 100644 index 00000000..d2da5741 --- /dev/null +++ b/ext/amrwb/gstamrwbparse.h @@ -0,0 +1,65 @@ +/* GStreamer Adaptive Multi-Rate Wide-Band (AMR-WB) plugin + * Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br> + * + * 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_AMRWBPARSE_H__ +#define __GST_AMRWBPARSE_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_AMRWBPARSE \ + (gst_amrwbparse_get_type()) +#define GST_AMRWBPARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRWBPARSE, GstAmrwbParse)) +#define GST_AMRWBPARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRWBPARSE, GstAmrwbParse)) +#define GST_IS_AMRWBPARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRWBPARSE)) +#define GST_IS_AMRWBPARSE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRWBPARSE)) + +typedef struct _GstAmrwbParse GstAmrwbParse; +typedef struct _GstAmrwbParseClass GstAmrwbParseClass; + +struct _GstAmrwbParse { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + + GstAdapter *adapter; + + gboolean seekable; + gboolean need_header; + gint64 offset; + + guint64 ts; +}; + +struct _GstAmrwbParseClass { + GstElementClass parent_class; +}; + +GType gst_amrwbparse_get_type (void); + +G_END_DECLS + +#endif /* __GST_AMRWBPARSE_H__ */ |