From e41644a080dba6f16e385c00473406b71959030b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 18 May 2005 16:51:46 +0000 Subject: [PATCH] ext/amrnb/: Ported AMR decoder/parse. Original commit message from CVS: * ext/amrnb/Makefile.am: * ext/amrnb/amrnb.c: (plugin_init): * ext/amrnb/amrnbdec.c: (gst_amrnbdec_init), (gst_amrnbdec_setcaps), (gst_amrnbdec_chain), (gst_amrnbdec_state_change): * ext/amrnb/amrnbdec.h: * ext/amrnb/amrnbenc.c: (gst_amrnbenc_get_type), (gst_amrnbenc_base_init), (gst_amrnbenc_class_init), (gst_amrnbenc_init), (gst_amrnbenc_finalize), (gst_amrnbenc_setcaps), (gst_amrnbenc_chain), (gst_amrnbenc_state_change): * ext/amrnb/amrnbenc.h: * ext/amrnb/amrnbparse.c: (gst_amrnbparse_init), (gst_amrnbparse_query), (gst_amrnbparse_event), (gst_amrnbparse_chain), (gst_amrnbparse_read_header), (gst_amrnbparse_loop), (gst_amrnbparse_sink_activate), (gst_amrnbparse_state_change): * ext/amrnb/amrnbparse.h: Ported AMR decoder/parse. Added AMR encoder. --- ChangeLog | 23 +++ ext/amrnb/Makefile.am | 4 +- ext/amrnb/amrnb.c | 8 +- ext/amrnb/amrnbdec.c | 119 +++++++++++----- ext/amrnb/amrnbdec.h | 3 + ext/amrnb/amrnbenc.c | 278 ++++++++++++++++++++++++++++++++++++ ext/amrnb/amrnbenc.h | 67 +++++++++ ext/amrnb/amrnbparse.c | 376 ++++++++++++++++++++++++++++++++++++++----------- ext/amrnb/amrnbparse.h | 10 +- 9 files changed, 760 insertions(+), 128 deletions(-) create mode 100644 ext/amrnb/amrnbenc.c create mode 100644 ext/amrnb/amrnbenc.h diff --git a/ChangeLog b/ChangeLog index b2ecd3a..ec6ff12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,28 @@ 2005-05-18 Wim Taymans + * ext/amrnb/Makefile.am: + * ext/amrnb/amrnb.c: (plugin_init): + * ext/amrnb/amrnbdec.c: (gst_amrnbdec_init), + (gst_amrnbdec_setcaps), (gst_amrnbdec_chain), + (gst_amrnbdec_state_change): + * ext/amrnb/amrnbdec.h: + * ext/amrnb/amrnbenc.c: (gst_amrnbenc_get_type), + (gst_amrnbenc_base_init), (gst_amrnbenc_class_init), + (gst_amrnbenc_init), (gst_amrnbenc_finalize), + (gst_amrnbenc_setcaps), (gst_amrnbenc_chain), + (gst_amrnbenc_state_change): + * ext/amrnb/amrnbenc.h: + * ext/amrnb/amrnbparse.c: (gst_amrnbparse_init), + (gst_amrnbparse_query), (gst_amrnbparse_event), + (gst_amrnbparse_chain), (gst_amrnbparse_read_header), + (gst_amrnbparse_loop), (gst_amrnbparse_sink_activate), + (gst_amrnbparse_state_change): + * ext/amrnb/amrnbparse.h: + Ported AMR decoder/parse. + Added AMR encoder. + +2005-05-18 Wim Taymans + * configure.ac: * gst/goom/Makefile.am: * gst/goom/gstgoom.c: (gst_goom_init), (gst_goom_sink_setcaps), diff --git a/ext/amrnb/Makefile.am b/ext/amrnb/Makefile.am index 0f03af4..961cbe9 100644 --- a/ext/amrnb/Makefile.am +++ b/ext/amrnb/Makefile.am @@ -3,12 +3,14 @@ plugin_LTLIBRARIES = libgstamrnb.la libgstamrnb_la_SOURCES = \ amrnb.c \ amrnbdec.c \ + amrnbenc.c \ amrnbparse.c libgstamrnb_la_CFLAGS = $(GST_CFLAGS) $(AMRNB_CFLAGS) libgstamrnb_la_LIBADD = $(AMRNB_LIBS) -libgstamrnb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstamrnb_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(GST_BASE_LIBS) noinst_HEADERS = \ amrnbdec.h \ + amrnbenc.h \ amrnbparse.h diff --git a/ext/amrnb/amrnb.c b/ext/amrnb/amrnb.c index 721fb48..9616271 100644 --- a/ext/amrnb/amrnb.c +++ b/ext/amrnb/amrnb.c @@ -22,18 +22,18 @@ #endif #include "amrnbdec.h" +#include "amrnbenc.h" #include "amrnbparse.h" static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_library_load ("gstbytestream")) - return FALSE; - return gst_element_register (plugin, "amrnbdec", GST_RANK_PRIMARY, GST_TYPE_AMRNBDEC) && gst_element_register (plugin, "amrnbparse", - GST_RANK_PRIMARY, GST_TYPE_AMRNBPARSE); + GST_RANK_PRIMARY, GST_TYPE_AMRNBPARSE) && + gst_element_register (plugin, "amrnbenc", + GST_RANK_NONE, GST_TYPE_AMRNBENC); } diff --git a/ext/amrnb/amrnbdec.c b/ext/amrnb/amrnbdec.c index c28364e..dbb7e1d 100644 --- a/ext/amrnb/amrnbdec.c +++ b/ext/amrnb/amrnbdec.c @@ -27,7 +27,7 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-amr-nb, " - "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]") + "rate = (int) 8000, " "channels = (int) 1") ); static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", @@ -38,15 +38,19 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", "depth = (int) 16, " "signed = (boolean) TRUE, " "endianness = (int) BYTE_ORDER, " - "rate = (int) [ 1000, 96000 ]," "channels = (int) [ 1, 2 ]") + "rate = (int) 8000," "channels = (int) 1") ); +static const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, + 0, 0, 0, 0, 0, 0, 0 +}; + static void gst_amrnbdec_base_init (GstAmrnbDecClass * klass); static void gst_amrnbdec_class_init (GstAmrnbDecClass * klass); static void gst_amrnbdec_init (GstAmrnbDec * amrnbdec); -static void gst_amrnbdec_chain (GstPad * pad, GstData * data); -static GstPadLinkReturn gst_amrnbdec_link (GstPad * pad, const GstCaps * caps); +static GstFlowReturn gst_amrnbdec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_amrnbdec_setcaps (GstPad * pad, GstCaps * caps); static GstElementStateReturn gst_amrnbdec_state_change (GstElement * element); static GstElementClass *parent_class = NULL; @@ -112,7 +116,7 @@ gst_amrnbdec_init (GstAmrnbDec * amrnbdec) amrnbdec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), "sink"); - gst_pad_set_link_function (amrnbdec->sinkpad, gst_amrnbdec_link); + gst_pad_set_setcaps_function (amrnbdec->sinkpad, gst_amrnbdec_setcaps); gst_pad_set_chain_function (amrnbdec->sinkpad, gst_amrnbdec_chain); gst_element_add_pad (GST_ELEMENT (amrnbdec), amrnbdec->sinkpad); @@ -120,9 +124,11 @@ gst_amrnbdec_init (GstAmrnbDec * amrnbdec) amrnbdec->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_template), "src"); - gst_pad_use_explicit_caps (amrnbdec->srcpad); + gst_pad_use_fixed_caps (amrnbdec->srcpad); gst_element_add_pad (GST_ELEMENT (amrnbdec), amrnbdec->srcpad); + amrnbdec->adapter = gst_adapter_new (); + /* init rest */ amrnbdec->handle = NULL; amrnbdec->channels = 0; @@ -130,13 +136,17 @@ gst_amrnbdec_init (GstAmrnbDec * amrnbdec) amrnbdec->ts = 0; } -static GstPadLinkReturn -gst_amrnbdec_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_amrnbdec_setcaps (GstPad * pad, GstCaps * caps) { - GstStructure *structure = gst_caps_get_structure (caps, 0); - GstAmrnbDec *amrnbdec = GST_AMRNBDEC (gst_pad_get_parent (pad)); + GstStructure *structure; + GstAmrnbDec *amrnbdec; GstCaps *copy; + amrnbdec = GST_AMRNBDEC (GST_PAD_PARENT (pad)); + + structure = gst_caps_get_structure (caps, 0); + /* get channel count */ gst_structure_get_int (structure, "channels", &amrnbdec->channels); gst_structure_get_int (structure, "rate", &amrnbdec->rate); @@ -148,32 +158,49 @@ gst_amrnbdec_link (GstPad * pad, const GstCaps * caps) "depth", G_TYPE_INT, 16, "endianness", G_TYPE_INT, G_BYTE_ORDER, "rate", G_TYPE_INT, amrnbdec->rate, "signed", G_TYPE_BOOLEAN, TRUE, NULL); - if (!gst_pad_set_explicit_caps (amrnbdec->srcpad, copy)) - return GST_PAD_LINK_REFUSED; - return GST_PAD_LINK_OK; + gst_pad_set_caps (amrnbdec->srcpad, copy); + gst_caps_unref (copy); + + return TRUE; } -static void -gst_amrnbdec_chain (GstPad * pad, GstData * in_data) +static GstFlowReturn +gst_amrnbdec_chain (GstPad * pad, GstBuffer * buffer) { - const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, - 0, 0, 0, 0, 0, 0, 0 - }; - GstAmrnbDec *amrnbdec = GST_AMRNBDEC (GST_OBJECT_PARENT (pad)); - GstBuffer *buf = gst_buffer_copy_on_write (in_data), *out; - guint8 *data = GST_BUFFER_DATA (buf); - gint size = GST_BUFFER_SIZE (buf), block, mode; + GstAmrnbDec *amrnbdec; + GstFlowReturn ret; + + amrnbdec = GST_AMRNBDEC (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + if (amrnbdec->rate == 0 || amrnbdec->channels == 0) + goto not_negotiated; + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + amrnbdec->ts = GST_BUFFER_TIMESTAMP (buffer); + + gst_adapter_push (amrnbdec->adapter, buffer); + + ret = GST_FLOW_OK; - if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) - amrnbdec->ts = GST_BUFFER_TIMESTAMP (buf); + while (TRUE) { + GstBuffer *out; + guint8 *data; + gint block, mode; + + if (gst_adapter_available (amrnbdec->adapter) < 1) + break; + data = (guint8 *) gst_adapter_peek (amrnbdec->adapter, 1); - while (size >= 1) { /* get size */ mode = (data[0] >> 3) & 0x0F; block = block_size[mode] + 1; - if (size < block) + + if (gst_adapter_available (amrnbdec->adapter) < block) break; + data = (guint8 *) gst_adapter_peek (amrnbdec->adapter, block); /* get output */ out = gst_buffer_new_and_alloc (160 * 2); @@ -181,33 +208,54 @@ gst_amrnbdec_chain (GstPad * pad, GstData * in_data) (amrnbdec->rate * amrnbdec->channels); GST_BUFFER_TIMESTAMP (out) = amrnbdec->ts; amrnbdec->ts += GST_BUFFER_DURATION (out); + gst_buffer_set_caps (out, GST_RPAD_CAPS (amrnbdec->srcpad)); /* decode */ Decoder_Interface_Decode (amrnbdec->handle, data, (short *) GST_BUFFER_DATA (out), 0); - data += block; - size -= block; + + gst_adapter_flush (amrnbdec->adapter, block); /* play */ - gst_pad_push (amrnbdec->srcpad, GST_DATA (out)); + ret = gst_pad_push (amrnbdec->srcpad, out); } + GST_STREAM_UNLOCK (pad); - gst_buffer_unref (buf); + return ret; + +not_negotiated: + { + GST_STREAM_UNLOCK (pad); + return GST_FLOW_NOT_NEGOTIATED; + } } static GstElementStateReturn gst_amrnbdec_state_change (GstElement * element) { - GstAmrnbDec *amrnbdec = GST_AMRNBDEC (element); + GstAmrnbDec *amrnbdec; + GstElementStateReturn ret; + gint transition; + + amrnbdec = GST_AMRNBDEC (element); + transition = GST_STATE_TRANSITION (element); - switch (GST_STATE_TRANSITION (element)) { + switch (transition) { case GST_STATE_NULL_TO_READY: if (!(amrnbdec->handle = Decoder_Interface_init ())) return GST_STATE_FAILURE; break; - case GST_STATE_PAUSED_TO_READY: + case GST_STATE_READY_TO_PAUSED: + gst_adapter_clear (amrnbdec->adapter); amrnbdec->ts = 0; break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { case GST_STATE_READY_TO_NULL: Decoder_Interface_exit (amrnbdec->handle); break; @@ -215,8 +263,5 @@ gst_amrnbdec_state_change (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return ret; } diff --git a/ext/amrnb/amrnbdec.h b/ext/amrnb/amrnbdec.h index e1437bf..826f22a 100644 --- a/ext/amrnb/amrnbdec.h +++ b/ext/amrnb/amrnbdec.h @@ -21,6 +21,7 @@ #define __GST_AMRNBDEC_H__ #include +#include #include G_BEGIN_DECLS @@ -46,6 +47,8 @@ struct _GstAmrnbDec { GstPad *sinkpad, *srcpad; guint64 ts; + GstAdapter *adapter; + /* library handle */ void *handle; diff --git a/ext/amrnb/amrnbenc.c b/ext/amrnb/amrnbenc.c new file mode 100644 index 0000000..abe6d9f --- /dev/null +++ b/ext/amrnb/amrnbenc.c @@ -0,0 +1,278 @@ +/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin + * Copyright (C) 2004 Ronald Bultje + * + * 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 "amrnbenc.h" + +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) 8000," "channels = (int) 1") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-amr-nb, " + "rate = (int) 8000, " "channels = (int) 1") + ); + +static void gst_amrnbenc_base_init (GstAmrnbEncClass * klass); +static void gst_amrnbenc_class_init (GstAmrnbEncClass * klass); +static void gst_amrnbenc_init (GstAmrnbEnc * amrnbenc); +static void gst_amrnbenc_finalize (GObject * object); + +static GstFlowReturn gst_amrnbenc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_amrnbenc_setcaps (GstPad * pad, GstCaps * caps); +static GstElementStateReturn gst_amrnbenc_state_change (GstElement * element); + +static GstElementClass *parent_class = NULL; + +GType +gst_amrnbenc_get_type (void) +{ + static GType amrnbenc_type = 0; + + if (!amrnbenc_type) { + static const GTypeInfo amrnbenc_info = { + sizeof (GstAmrnbEncClass), + (GBaseInitFunc) gst_amrnbenc_base_init, + NULL, + (GClassInitFunc) gst_amrnbenc_class_init, + NULL, + NULL, + sizeof (GstAmrnbEnc), + 0, + (GInstanceInitFunc) gst_amrnbenc_init, + }; + + amrnbenc_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstAmrnbEnc", &amrnbenc_info, 0); + } + + return amrnbenc_type; +} + +static void +gst_amrnbenc_base_init (GstAmrnbEncClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstElementDetails gst_amrnbenc_details = { + "AMR-NB decoder", + "Codec/Decoder/Audio", + "Adaptive Multi-Rate Narrow-Band audio encoder", + "Ronald Bultje , " + "Wim Taymans " + }; + + 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_amrnbenc_details); +} + +static void +gst_amrnbenc_class_init (GstAmrnbEncClass * 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_amrnbenc_finalize; + + element_class->change_state = gst_amrnbenc_state_change; +} + +static void +gst_amrnbenc_init (GstAmrnbEnc * amrnbenc) +{ + /* create the sink pad */ + amrnbenc->sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), + "sink"); + gst_pad_set_setcaps_function (amrnbenc->sinkpad, gst_amrnbenc_setcaps); + gst_pad_set_chain_function (amrnbenc->sinkpad, gst_amrnbenc_chain); + gst_element_add_pad (GST_ELEMENT (amrnbenc), amrnbenc->sinkpad); + + /* create the src pad */ + amrnbenc->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get (&src_template), + "src"); + gst_pad_use_fixed_caps (amrnbenc->srcpad); + gst_element_add_pad (GST_ELEMENT (amrnbenc), amrnbenc->srcpad); + + amrnbenc->adapter = gst_adapter_new (); + + /* init rest */ + amrnbenc->handle = NULL; + amrnbenc->channels = 0; + amrnbenc->rate = 0; + amrnbenc->ts = 0; +} + +static void +gst_amrnbenc_finalize (GObject * object) +{ + GstAmrnbEnc *amrnbenc; + + amrnbenc = GST_AMRNBENC (object); + + g_object_unref (G_OBJECT (amrnbenc->adapter)); + amrnbenc->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_amrnbenc_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstAmrnbEnc *amrnbenc; + GstCaps *copy; + + amrnbenc = GST_AMRNBENC (GST_PAD_PARENT (pad)); + + structure = gst_caps_get_structure (caps, 0); + + /* get channel count */ + gst_structure_get_int (structure, "channels", &amrnbenc->channels); + gst_structure_get_int (structure, "rate", &amrnbenc->rate); + + /* this is not wrong but will sound bad */ + if (amrnbenc->channels != 1) { + g_warning ("amrnbdec is only optimized for mono channels"); + } + if (amrnbenc->rate != 8000) { + g_warning ("amrnbdec is only optimized for 8000 Hz samplerate"); + } + + /* create reverse caps */ + copy = gst_caps_new_simple ("audio/x-amr-nb", + "channels", G_TYPE_INT, amrnbenc->channels, + "rate", G_TYPE_INT, amrnbenc->rate, NULL); + + gst_pad_set_caps (amrnbenc->srcpad, copy); + gst_caps_unref (copy); + + return TRUE; +} + +static GstFlowReturn +gst_amrnbenc_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAmrnbEnc *amrnbenc; + GstFlowReturn ret; + + amrnbenc = GST_AMRNBENC (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + if (amrnbenc->rate == 0 || amrnbenc->channels == 0) + goto not_negotiated; + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + amrnbenc->ts = GST_BUFFER_TIMESTAMP (buffer); + + ret = GST_FLOW_OK; + gst_adapter_push (amrnbenc->adapter, buffer); + + /* Collect samples until we have enough for an output frame */ + while (gst_adapter_available (amrnbenc->adapter) >= 320) { + GstBuffer *out; + guint8 *data; + gint outsize; + + /* get output, max size is 32 */ + out = gst_buffer_new_and_alloc (32); + GST_BUFFER_DURATION (out) = GST_SECOND * 160 / + (amrnbenc->rate * amrnbenc->channels); + GST_BUFFER_TIMESTAMP (out) = amrnbenc->ts; + amrnbenc->ts += GST_BUFFER_DURATION (out); + gst_buffer_set_caps (out, GST_RPAD_CAPS (amrnbenc->srcpad)); + + data = (guint8 *) gst_adapter_peek (amrnbenc->adapter, 320); + + /* decode */ + outsize = Encoder_Interface_Encode (amrnbenc->handle, MR122, (short *) data, + (guint8 *) GST_BUFFER_DATA (out), 0); + + gst_adapter_flush (amrnbenc->adapter, 320); + + GST_BUFFER_SIZE (out) = outsize; + + /* play */ + ret = gst_pad_push (amrnbenc->srcpad, out); + } + + GST_STREAM_UNLOCK (pad); + + return ret; + +not_negotiated: + { + GST_STREAM_UNLOCK (pad); + return GST_FLOW_NOT_NEGOTIATED; + } +} + +static GstElementStateReturn +gst_amrnbenc_state_change (GstElement * element) +{ + GstAmrnbEnc *amrnbenc; + GstElementStateReturn ret; + gint transition; + + amrnbenc = GST_AMRNBENC (element); + transition = GST_STATE_TRANSITION (element); + + switch (transition) { + case GST_STATE_NULL_TO_READY: + if (!(amrnbenc->handle = Encoder_Interface_init (0))) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY_TO_PAUSED: + amrnbenc->ts = 0; + gst_adapter_clear (amrnbenc->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { + case GST_STATE_READY_TO_NULL: + Encoder_Interface_exit (amrnbenc->handle); + break; + default: + break; + } + + return ret; +} diff --git a/ext/amrnb/amrnbenc.h b/ext/amrnb/amrnbenc.h new file mode 100644 index 0000000..60fdf59 --- /dev/null +++ b/ext/amrnb/amrnbenc.h @@ -0,0 +1,67 @@ +/* GStreamer Adaptive Multi-Rate Narrow-Band (AMR-NB) plugin + * Copyright (C) 2004 Ronald Bultje + * + * 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_AMRNBENC_H__ +#define __GST_AMRNBENC_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AMRNBENC \ + (gst_amrnbenc_get_type()) +#define GST_AMRNBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRNBENC, GstAmrnbEnc)) +#define GST_AMRNBENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRNBENC, GstAmrnbEnc)) +#define GST_IS_AMRNBENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRNBENC)) +#define GST_IS_AMRNBENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRNBENC)) + +typedef struct _GstAmrnbEnc GstAmrnbEnc; +typedef struct _GstAmrnbEncClass GstAmrnbEncClass; + +struct _GstAmrnbEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad, *srcpad; + guint64 ts; + + GstAdapter *adapter; + + /* library handle */ + void *handle; + + /* input settings */ + gint channels, rate; +}; + +struct _GstAmrnbEncClass { + GstElementClass parent_class; +}; + +GType gst_amrnbenc_get_type (void); + +G_END_DECLS + +#endif /* __GST_AMRNBENC_H__ */ diff --git a/ext/amrnb/amrnbparse.c b/ext/amrnb/amrnbparse.c index 27d3a8f..6e651d4 100644 --- a/ext/amrnb/amrnbparse.c +++ b/ext/amrnb/amrnbparse.c @@ -41,15 +41,23 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-amr-nb-sh") ); +static const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, + 0, 0, 0, 0, 0, 0, 0 +}; + static void gst_amrnbparse_base_init (GstAmrnbParseClass * klass); static void gst_amrnbparse_class_init (GstAmrnbParseClass * klass); static void gst_amrnbparse_init (GstAmrnbParse * amrnbparse); -static const GstFormat *gst_amrnbparse_formats (GstPad * pad); +//static const GstFormat *gst_amrnbparse_formats (GstPad * pad); static const GstQueryType *gst_amrnbparse_querytypes (GstPad * pad); -static gboolean gst_amrnbparse_query (GstPad * pad, GstQueryType type, - GstFormat * fmt, gint64 * value); -static void gst_amrnbparse_loop (GstElement * element); +static gboolean gst_amrnbparse_query (GstPad * pad, GstQuery * query); + +static gboolean gst_amrnbparse_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer); +static void gst_amrnbparse_loop (GstPad * pad); +static gboolean gst_amrnbparse_sink_activate (GstPad * sinkpad, + GstActivateMode mode); static GstElementStateReturn gst_amrnbparse_state_change (GstElement * element); static GstElementClass *parent_class = NULL; @@ -114,12 +122,18 @@ gst_amrnbparse_class_init (GstAmrnbParseClass * klass) static void gst_amrnbparse_init (GstAmrnbParse * amrnbparse) { - GST_FLAG_SET (amrnbparse, GST_ELEMENT_EVENT_AWARE); - /* create the sink pad */ amrnbparse->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_template), "sink"); + gst_pad_set_chain_function (amrnbparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_amrnbparse_chain)); + gst_pad_set_event_function (amrnbparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_amrnbparse_event)); + gst_pad_set_loop_function (amrnbparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_amrnbparse_loop)); + gst_pad_set_activate_function (amrnbparse->sinkpad, + GST_DEBUG_FUNCPTR (gst_amrnbparse_sink_activate)); gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->sinkpad); /* create the src pad */ @@ -130,11 +144,9 @@ gst_amrnbparse_init (GstAmrnbParse * amrnbparse) GST_DEBUG_FUNCPTR (gst_amrnbparse_query)); gst_pad_set_query_type_function (amrnbparse->srcpad, GST_DEBUG_FUNCPTR (gst_amrnbparse_querytypes)); - gst_pad_set_formats_function (amrnbparse->srcpad, - GST_DEBUG_FUNCPTR (gst_amrnbparse_formats)); gst_element_add_pad (GST_ELEMENT (amrnbparse), amrnbparse->srcpad); - gst_element_set_loop_function (GST_ELEMENT (amrnbparse), gst_amrnbparse_loop); + amrnbparse->adapter = gst_adapter_new (); /* init rest */ amrnbparse->ts = 0; @@ -144,6 +156,7 @@ gst_amrnbparse_init (GstAmrnbParse * amrnbparse) * Position querying. */ +#if 0 static const GstFormat * gst_amrnbparse_formats (GstPad * pad) { @@ -154,6 +167,7 @@ gst_amrnbparse_formats (GstPad * pad) return list; } +#endif static const GstQueryType * gst_amrnbparse_querytypes (GstPad * pad) @@ -168,154 +182,348 @@ gst_amrnbparse_querytypes (GstPad * pad) } static gboolean -gst_amrnbparse_query (GstPad * pad, GstQueryType type, - GstFormat * fmt, gint64 * value) +gst_amrnbparse_query (GstPad * pad, GstQuery * query) { - GstAmrnbParse *amrnbparse = GST_AMRNBPARSE (gst_pad_get_parent (pad)); + GstAmrnbParse *amrnbparse; gboolean res = TRUE; - if (*fmt != GST_FORMAT_TIME) - return FALSE; + amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad)); - switch (type) { + switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: - *value = amrnbparse->ts; - break; - case GST_QUERY_TOTAL:{ - gint64 pos = gst_bytestream_tell (amrnbparse->bs), - tot = gst_bytestream_length (amrnbparse->bs); + { + GstFormat format; + gint64 cur, tot; + GstPad *peer; - *value = amrnbparse->ts * ((gdouble) tot / pos); + gst_query_parse_position (query, &format, NULL, NULL); + + if (format != GST_FORMAT_TIME) + return FALSE; + + tot = -1; + + peer = gst_pad_get_peer (amrnbparse->sinkpad); + if (peer) { + GstFormat pformat; + gint64 pcur, ptot; + + pformat = GST_FORMAT_BYTES; + res = gst_pad_query_position (peer, &pformat, &pcur, &ptot); + gst_object_unref (GST_OBJECT (peer)); + if (res) { + tot = amrnbparse->ts * ((gdouble) ptot / pcur); + } + } + cur = amrnbparse->ts; + + gst_query_set_position (query, GST_FORMAT_TIME, cur, tot); + res = TRUE; break; } default: res = FALSE; break; } - return res; } + /* * Data reading. */ - static gboolean -gst_amrnbparse_handle_event (GstAmrnbParse * amrnbparse, GstEvent * event) +gst_amrnbparse_event (GstPad * pad, GstEvent * event) { + GstAmrnbParse *amrnbparse; gboolean res; - if (!event) { - GST_ELEMENT_ERROR (amrnbparse, RESOURCE, READ, (NULL), (NULL)); - return FALSE; - } + amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad)); GST_LOG ("handling event %d", GST_EVENT_TYPE (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: - case GST_EVENT_INTERRUPT: - res = FALSE; - break; case GST_EVENT_DISCONTINUOUS: default: - res = TRUE; break; } - gst_pad_event_default (amrnbparse->sinkpad, event); + res = gst_pad_event_default (amrnbparse->sinkpad, event); return res; } -static guint8 * -gst_amrnbparse_reserve (GstAmrnbParse * amrnbparse, gint size) +/* streaming mode */ +static GstFlowReturn +gst_amrnbparse_chain (GstPad * pad, GstBuffer * buffer) { - gint read; - guint8 *data; + GstAmrnbParse *amrnbparse; + GstFlowReturn res; + gint block, mode, read; + const guint8 *data; + GstBuffer *out; - GST_DEBUG ("Trying to read %d bytes", size); - do { - if ((read = gst_bytestream_peek_bytes (amrnbparse->bs, - &data, size)) != size) { - GstEvent *event; - guint32 avail; + amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad)); - gst_bytestream_get_status (amrnbparse->bs, &avail, &event); - if (!gst_amrnbparse_handle_event (amrnbparse, event)) - return NULL; - } - } while (read < size); + GST_STREAM_LOCK (pad); + + gst_adapter_push (amrnbparse->adapter, buffer); - GST_DEBUG ("Read %d bytes of data", read); + res = GST_FLOW_OK; - return data; + /* init */ + if (amrnbparse->need_header) { + + if (gst_adapter_available (amrnbparse->adapter) < 6) + goto done; + + data = gst_adapter_peek (amrnbparse->adapter, 6); + if (memcmp (data, "#!AMR\n", 6) != 0) + goto done; + + gst_adapter_flush (amrnbparse->adapter, 6); + + amrnbparse->need_header = FALSE; + } + + while (TRUE) { + if (gst_adapter_available (amrnbparse->adapter) < 1) + break; + data = gst_adapter_peek (amrnbparse->adapter, 1); + + /* get size */ + mode = (data[0] >> 3) & 0x0F; + block = block_size[mode] + 1; /* add one for the mode */ + + if (gst_adapter_available (amrnbparse->adapter) < block) + break; + + out = gst_buffer_new_and_alloc (block); + + data = gst_adapter_peek (amrnbparse->adapter, block); + memcpy (GST_BUFFER_DATA (out), data, block); + + /* output */ + GST_BUFFER_DURATION (out) = GST_SECOND * 160 / 8000; + GST_BUFFER_TIMESTAMP (out) = amrnbparse->ts; + amrnbparse->ts += GST_BUFFER_DURATION (out); + gst_buffer_set_caps (out, + (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad)); + + GST_DEBUG ("Pushing %d bytes of data", block); + res = gst_pad_push (amrnbparse->srcpad, out); + + gst_adapter_flush (amrnbparse->adapter, block); + } +done: + GST_STREAM_UNLOCK (pad); + + return res; } +static gboolean +gst_amrnbparse_read_header (GstAmrnbParse * amrnbparse) +{ + GstBuffer *buffer; + GstFlowReturn ret; + gint8 *data; + gint size; + + ret = + gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 6, &buffer); + if (ret != GST_FLOW_OK) + return FALSE; + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + if (size < 6) + goto not_enough; + + if (memcmp (data, "#!AMR\n", 6)) + goto no_header; + + amrnbparse->offset += 6; + + return TRUE; + +not_enough: + { + gst_buffer_unref (buffer); + return FALSE; + } +no_header: + { + gst_buffer_unref (buffer); + return FALSE; + } +} + +/* random access mode, could just read a fixed size buffer and push it to + * the chain function but we don't... */ static void -gst_amrnbparse_loop (GstElement * element) +gst_amrnbparse_loop (GstPad * pad) { - const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, - 0, 0, 0, 0, 0, 0, 0 - }; - GstAmrnbParse *amrnbparse = GST_AMRNBPARSE (element); - GstBuffer *buf; + GstAmrnbParse *amrnbparse; + GstBuffer *buffer; guint8 *data; + gint size; gint block, mode, read; + GstFlowReturn ret; - if (!(data = gst_amrnbparse_reserve (amrnbparse, 1))) - return; + amrnbparse = GST_AMRNBPARSE (GST_PAD_PARENT (pad)); + GST_STREAM_LOCK (pad); /* init */ - if (amrnbparse->ts == 0 && data[0] == '#') { - if (!(data = gst_amrnbparse_reserve (amrnbparse, 6))) - return; - if (!memcmp (data, "#!AMR", 5)) { - GST_LOG ("Found header"); - gst_bytestream_flush_fast (amrnbparse->bs, 5); - data += 5; + if (amrnbparse->need_header) { + gboolean got_header; + + got_header = gst_amrnbparse_read_header (amrnbparse); + if (!got_header) { + GST_LOG_OBJECT (amrnbparse, "could not read header"); + goto need_pause; } + amrnbparse->need_header = FALSE; } + ret = + gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, 1, &buffer); + if (ret != GST_FLOW_OK) + goto need_pause; + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + /* EOS */ + if (size < 1) + goto eos; + /* get size */ mode = (data[0] >> 3) & 0x0F; - block = block_size[mode] + 1; - if (!gst_amrnbparse_reserve (amrnbparse, block)) - return; - read = gst_bytestream_read (amrnbparse->bs, &buf, block); - g_assert (read == block); + block = block_size[mode] + 1; /* add one for the mode */ + + gst_buffer_unref (buffer); + + ret = + gst_pad_pull_range (amrnbparse->sinkpad, amrnbparse->offset, block, + &buffer); + if (ret != GST_FLOW_OK) + goto need_pause; + + amrnbparse->offset += block; /* output */ - GST_BUFFER_DURATION (buf) = GST_SECOND * 160 / 8000; - GST_BUFFER_TIMESTAMP (buf) = amrnbparse->ts; - amrnbparse->ts += GST_BUFFER_DURATION (buf); + GST_BUFFER_DURATION (buffer) = GST_SECOND * 160 / 8000; + GST_BUFFER_TIMESTAMP (buffer) = amrnbparse->ts; + amrnbparse->ts += GST_BUFFER_DURATION (buffer); + gst_buffer_set_caps (buffer, + (GstCaps *) gst_pad_get_pad_template_caps (amrnbparse->srcpad)); GST_DEBUG ("Pushing %d bytes of data", block); - gst_pad_push (amrnbparse->srcpad, GST_DATA (buf)); + ret = gst_pad_push (amrnbparse->srcpad, buffer); + if (ret != GST_FLOW_OK) + goto need_pause; + + GST_STREAM_UNLOCK (pad); + + return; + +need_pause: + { + GST_LOG_OBJECT (amrnbparse, "pausing task"); + gst_task_pause (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + return; + } +eos: + { + GST_LOG_OBJECT (amrnbparse, "pausing task"); + gst_pad_push_event (amrnbparse->srcpad, gst_event_new (GST_EVENT_EOS)); + gst_task_pause (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + return; + } +} + +static gboolean +gst_amrnbparse_sink_activate (GstPad * sinkpad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstAmrnbParse *amrnbparse; + + amrnbparse = GST_AMRNBPARSE (GST_OBJECT_PARENT (sinkpad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + amrnbparse->seekable = FALSE; + result = TRUE; + break; + case GST_ACTIVATE_PULL: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (amrnbparse)) { + gst_pad_peer_set_active (sinkpad, mode); + GST_STREAM_LOCK (sinkpad); + GST_RPAD_TASK (sinkpad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (amrnbparse), + (GstTaskFunction) gst_amrnbparse_loop, sinkpad); + + amrnbparse->need_header = TRUE; + amrnbparse->seekable = TRUE; + amrnbparse->ts = 0; + gst_task_start (GST_RPAD_TASK (sinkpad)); + GST_STREAM_UNLOCK (sinkpad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (sinkpad); + + /* step 3, stop the task */ + if (GST_RPAD_TASK (sinkpad)) { + gst_task_stop (GST_RPAD_TASK (sinkpad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (sinkpad))); + GST_RPAD_TASK (sinkpad) = NULL; + } + GST_STREAM_UNLOCK (sinkpad); + + result = TRUE; + break; + } + return result; } static GstElementStateReturn gst_amrnbparse_state_change (GstElement * element) { - GstAmrnbParse *amrnbparse = GST_AMRNBPARSE (element); + GstAmrnbParse *amrnbparse; + gint transition; + GstElementStateReturn ret; - switch (GST_STATE_TRANSITION (element)) { + amrnbparse = GST_AMRNBPARSE (element); + transition = GST_STATE_TRANSITION (element); + + switch (transition) { case GST_STATE_NULL_TO_READY: - if (!(amrnbparse->bs = gst_bytestream_new (amrnbparse->sinkpad))) - return GST_STATE_FAILURE; break; + case GST_STATE_READY_TO_PAUSED: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { case GST_STATE_PAUSED_TO_READY: - amrnbparse->ts = 0; break; case GST_STATE_READY_TO_NULL: - gst_bytestream_destroy (amrnbparse->bs); break; default: break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return ret; } diff --git a/ext/amrnb/amrnbparse.h b/ext/amrnb/amrnbparse.h index c6474d9..ee7d4ff 100644 --- a/ext/amrnb/amrnbparse.h +++ b/ext/amrnb/amrnbparse.h @@ -21,7 +21,7 @@ #define __GST_AMRNBPARSE_H__ #include -#include +#include G_BEGIN_DECLS @@ -44,7 +44,13 @@ struct _GstAmrnbParse { /* pads */ GstPad *sinkpad, *srcpad; - GstByteStream *bs; + + GstAdapter *adapter; + + gboolean seekable; + gboolean need_header; + gint64 offset; + guint64 ts; }; -- 2.7.4