2005-05-18 Wim Taymans <wim@fluendo.com>
+ * 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 <wim@fluendo.com>
+
* configure.ac:
* gst/goom/Makefile.am:
* gst/goom/gstgoom.c: (gst_goom_init), (gst_goom_sink_setcaps),
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
#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);
}
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",
"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;
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);
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;
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);
"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);
(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;
break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
-
- return GST_STATE_SUCCESS;
+ return ret;
}
#define __GST_AMRNBDEC_H__
#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
#include <amrnb/interf_dec.h>
G_BEGIN_DECLS
GstPad *sinkpad, *srcpad;
guint64 ts;
+ GstAdapter *adapter;
+
/* library handle */
void *handle;
--- /dev/null
+/* 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 "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 <rbultje@ronald.bitfreak.net>, "
+ "Wim Taymans <wim@fluendo.com>"
+ };
+
+ 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;
+}
--- /dev/null
+/* 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.
+ */
+
+#ifndef __GST_AMRNBENC_H__
+#define __GST_AMRNBENC_H__
+
+#include <gst/gst.h>
+#include <amrnb/interf_enc.h>
+#include <gst/base/gstadapter.h>
+
+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__ */
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;
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 */
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;
* Position querying.
*/
+#if 0
static const GstFormat *
gst_amrnbparse_formats (GstPad * pad)
{
return list;
}
+#endif
static const GstQueryType *
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;
}
#define __GST_AMRNBPARSE_H__
#include <gst/gst.h>
-#include <gst/bytestream/bytestream.h>
+#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
/* pads */
GstPad *sinkpad, *srcpad;
- GstByteStream *bs;
+
+ GstAdapter *adapter;
+
+ gboolean seekable;
+ gboolean need_header;
+ gint64 offset;
+
guint64 ts;
};