This is a more o less stable element for aac audio decoding.
* It works for SN 23.i3.3 and 23.i3.8
* The input requires to be parsed (aacparse required if the stream is not in
  a container)
* The performance for an audio only pipeline is from 10% to 0% of arm usage
* A/V rendering looks quite good (IMO)
TODO:
* Fix caps renegotiation when parametric stereo stream is detected
* Tests for non-framed streams
Happy holidays.
vmjl
Víctor Manuel Jáquez Leal (2):
  base: add codec update_params() callback
  adec: aac decoder implementation
 Makefile          |    4 +-
 gstdspadec.c      |  280 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gstdspadec.h      |   47 +++++++++
 gstdspbase.c      |    8 ++
 gstdspbase.h      |    3 +
 plugin.c          |    4 +
 tidsp/td_aacdec.c |  175 +++++++++++++++++++++++++++++++++
 7 files changed, 519 insertions(+), 2 deletions(-)
 create mode 100644 gstdspadec.c
 create mode 100644 gstdspadec.h
 create mode 100644 tidsp/td_aacdec.c
This patch add the callback update_params(), which will update the
SN parameters according to the received message.
1. http://en.wikipedia.org/wiki/Parametric_Stereo
Signed-off-by: Víctor Manuel Jáquez Leal <vja...@igalia.com>
---
 gstdspbase.c |    8 ++++++++
 gstdspbase.h |    1 +
 2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/gstdspbase.c b/gstdspbase.c
index 953328a..dd6b98f 100644
--- a/gstdspbase.c
+++ b/gstdspbase.c
@@ -227,6 +227,14 @@ got_message(GstDspBase *self,
 			pr_debug(self, "playback completed");
 			break;
 		}
+
+		if (msg->arg_1 == 1 && (msg->arg_2 & 0x0600) == 0x0600) {
+			struct td_codec *codec = self->codec;
+			if (codec->update_params && self->node)
+				codec->update_params(self, self->node, msg->arg_2);
+			break;
+		}
+
 		pr_warning(self, "DSP event: cmd=0x%04X, arg1=%u, arg2=0x%04X",
 			   msg->cmd, msg->arg_1, msg->arg_2);
 		if ((msg->arg_2 & 0x0F00) == 0x0F00)
diff --git a/gstdspbase.h b/gstdspbase.h
index 829b5b1..0014635 100644
--- a/gstdspbase.h
+++ b/gstdspbase.h
@@ -56,6 +56,7 @@ struct td_codec {
 	bool (*handle_extra_data)(GstDspBase *base, GstBuffer *buf);
 	void (*flush_buffer)(GstDspBase *base);
 	void (*send_params)(GstDspBase *base, struct dsp_node *node);
+	void (*update_params) (GstDspBase *base, struct dsp_node *node, uint32_t msg);
 };
 
 struct _GstDspBase {
-- 
1.7.0.2
The performance of a simple pipeline goes among 10% and 0% of CPU
usage (montored with top and with gstreamer 0.10.31)
My testing pipeline is something like:
gst-launch filesrc location=test.aac ! aacparse ! dspadec ! alsasink
Issues: when a parametric stereo media is found, a downstream caps
renegotiation is done, but somehow (I don't understand why) the
renegotiation is not honored and the output is rendered wrong (because
of the reduced framerate).
Signed-off-by: Víctor Manuel Jáquez Leal <vja...@igalia.com>
---
 Makefile          |    4 +-
 gstdspadec.c      |  280 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gstdspadec.h      |   47 +++++++++
gstdspbase.h | 2 +
 plugin.c          |    4 +
 tidsp/td_aacdec.c |  175 +++++++++++++++++++++++++++++++++
6 files changed, 510 insertions(+), 2 deletions(-)
 create mode 100644 gstdspadec.c
 create mode 100644 gstdspadec.h
 create mode 100644 tidsp/td_aacdec.c
diff --git a/Makefile b/Makefile
index c397101..4303eeb 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ prefix := /usr
 D = $(DESTDIR)
 
 tidsp.a: tidsp/td_mp4vdec.o tidsp/td_h264dec.o tidsp/td_wmvdec.o \
-	tidsp/td_jpegdec.o \
+	tidsp/td_jpegdec.o tidsp/td_aacdec.o \
 	tidsp/td_mp4venc.o tidsp/td_jpegenc.o tidsp/td_h264enc.o
 tidsp.a: override CFLAGS += -fPIC -I.
 
@@ -34,7 +34,7 @@ gst_plugin := libgstdsp.so
 $(gst_plugin): plugin.o gstdspdummy.o gstdspbase.o gstdspvdec.o \
 	gstdspvenc.o gstdsph263enc.o gstdspmp4venc.o gstdspjpegenc.o \
 	dsp_bridge.o util.o log.o gstdspparse.o async_queue.o gstdsph264enc.o \
-	tidsp.a
+	gstdspadec.o tidsp.a
 $(gst_plugin): override CFLAGS += $(GST_CFLAGS) -fPIC \
 	-D VERSION='"$(version)"' -D DSPDIR='"$(dspdir)"'
 $(gst_plugin): override LIBS += $(GST_LIBS)
diff --git a/gstdspadec.c b/gstdspadec.c
new file mode 100644
index 0000000..98ddcb6
--- /dev/null
+++ b/gstdspadec.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2010 Víctor M. Jáquez Leal
+ *
+ * Author: Víctor M. Jáquez Leal <vja...@igalia.com>
+ *
+ * This file may be used under the terms of the GNU Lesser General Public
+ * License version 2.1, a copy of which is found in LICENSE included in the
+ * packaging of this file.
+ */
+
+#include "gstdspadec.h"
+
+#include "log.h"
+
+#define GST_CAT_DEFAULT gstdsp_debug
+
+static GstDspBaseClass *parent_class;
+
+static inline GstCaps *
+generate_sink_template(void)
+{
+	GstCaps *caps;
+	GstStructure *struc;
+
+	caps = gst_caps_new_empty();
+
+	struc = gst_structure_new("audio/mpeg",
+				"mpegversion", GST_TYPE_INT_RANGE, 1, 4,
+				NULL);
+
+	gst_caps_append_structure(caps, struc);
+
+	return caps;
+}
+
+static inline GstCaps *
+generate_src_template(void)
+{
+	GstCaps *caps;
+	GstStructure *struc;
+
+	caps = gst_caps_new_empty();
+
+	struc = gst_structure_new("audio/x-raw-int",
+				"endianness", G_TYPE_INT, G_BYTE_ORDER,
+				"signed", G_TYPE_BOOLEAN, TRUE,
+				"width", G_TYPE_INT, 16,
+				"depth", G_TYPE_INT, 16,
+				"rate", GST_TYPE_INT_RANGE, 8000, 96000,
+				"channels", GST_TYPE_INT_RANGE, 1, 8,
+				NULL);
+
+	gst_caps_append_structure(caps, struc);
+
+	return caps;
+}
+
+static void *
+create_node(GstDspBase *base)
+{
+	GstDspADec *self;
+	struct td_codec *codec;
+	int dsp_handle;
+	struct dsp_node *node;
+
+	const struct dsp_uuid usn_uuid = { 0x79A3C8B3, 0x95F2, 0x403F, 0x9A, 0x4B,
+		{ 0xCF, 0x80, 0x57, 0x73, 0x05, 0x41 } };
+
+	self = GST_DSP_ADEC(base);
+	dsp_handle = base->dsp_handle;
+
+	if (!gstdsp_register(dsp_handle, &usn_uuid, DSP_DCD_LIBRARYTYPE, "usn.dll64P")) {
+		pr_err(self, "failed to register usn node library");
+		return NULL;
+	}
+
+	codec = base->codec;
+	if (!codec) {
+		pr_err(self, "unknown algorithm");
+		return NULL;
+	}
+
+	pr_info(base, "algo=%s", codec->filename);
+
+	if (!gstdsp_register(dsp_handle, codec->uuid, DSP_DCD_LIBRARYTYPE, codec->filename)) {
+		pr_err(self, "failed to register algo node library");
+		return NULL;
+	}
+
+	if (!gstdsp_register(dsp_handle, codec->uuid, DSP_DCD_NODETYPE, codec->filename)) {
+		pr_err(self, "failed to register algo node");
+		return NULL;
+	}
+
+	{
+		struct dsp_node_attr_in attrs = {
+			.cb = sizeof(attrs),
+			.priority = 10,
+			.timeout = 10000,
+		};
+		void *arg_data;
+
+		codec->create_args(base, &attrs.profile_id, &arg_data);
+		if (!dsp_node_allocate(dsp_handle, base->proc, codec->uuid, arg_data, &attrs, &node)) {
+			pr_err(self, "dsp node allocate failed");
+			free(arg_data);
+			return NULL;
+		}
+		free(arg_data);
+	}
+
+	if (!dsp_node_create(dsp_handle, node)) {
+		pr_err(self, "dsp node create failed");
+		dsp_node_free(dsp_handle, node);
+		return NULL;
+	}
+
+	pr_info(self, "dsp node created");
+
+	if (codec->send_params)
+		codec->send_params(base, node);
+
+	if (codec->setup_params)
+		codec->setup_params(base);
+
+	base->flush_buffer = codec->flush_buffer;
+
+	return node;
+}
+
+static inline void
+configure_caps(GstDspADec *self,
+	       GstCaps *in,
+	       GstCaps *out)
+{
+	GstDspBase *base;
+	GstStructure *out_struc, *in_struc;
+	uint channels;
+
+
+	base = GST_DSP_BASE(self);
+
+	in_struc = gst_caps_get_structure(in, 0);
+
+	out_struc = gst_structure_new("audio/x-raw-int",
+				"endianness", G_TYPE_INT, G_BYTE_ORDER,
+				"signed", G_TYPE_BOOLEAN, TRUE,
+				"width", G_TYPE_INT, 16,
+				"depth", G_TYPE_INT, 16,
+				NULL);
+
+	if (gst_structure_get_int(in_struc, "channels", &channels))
+		gst_structure_set(out_struc, "channels", G_TYPE_INT, channels, NULL);
+
+	if (gst_structure_get_int(in_struc, "rate", &self->samplerate))
+		gst_structure_set(out_struc, "rate", G_TYPE_INT, self->samplerate, NULL);
+
+	if (base->alg == GSTDSP_AACDEC) {
+		const char *fmt;
+		gst_structure_get_boolean(in_struc, "framed", &self->packetised);
+		fmt = gst_structure_get_string(in_struc, "stream-format");
+		self->raw = strcmp(fmt, "raw") == 0;
+	}
+
+	base->output_buffer_size = 4 * 1024;
+
+	gst_caps_append_structure(out, out_struc);
+}
+
+static gboolean
+sink_setcaps(GstPad *pad,
+	     GstCaps *caps)
+{
+	GstDspADec *self;
+	GstDspBase *base;
+	GstStructure *in_struc;
+	const char *name;
+	GstCaps *out_caps;
+
+	self = GST_DSP_ADEC(GST_PAD_PARENT(pad));
+	base = GST_DSP_BASE(self);
+
+	{
+		gchar *str = gst_caps_to_string(caps);
+		pr_info(self, "sink caps: %s", str);
+		g_free(str);
+	}
+
+	in_struc = gst_caps_get_structure(caps, 0);
+
+	name = gst_structure_get_name(in_struc);
+	if (strcmp(name, "audio/mpeg") == 0) {
+		int version = 1;
+
+		gst_structure_get_int(in_struc, "mpegversion", &version);
+		if (version == 2 || version == 4) {
+			base->alg = GSTDSP_AACDEC;
+			base->codec = &td_aacdec_codec;
+		}
+	}
+
+	du_port_alloc_buffers(base->ports[0], 4);
+	du_port_alloc_buffers(base->ports[1], 4);
+
+	out_caps = gst_caps_new_empty();
+	configure_caps(self, caps, out_caps);
+	base->tmp_caps = out_caps;
+
+	return TRUE;
+}
+
+static void
+instance_init(GTypeInstance *instance,
+	      gpointer g_class)
+{
+	GstDspBase *base;
+
+	base = GST_DSP_BASE(instance);
+
+	base->use_pad_alloc = TRUE;
+	base->create_node = create_node;
+
+	gst_pad_set_setcaps_function(base->sinkpad, sink_setcaps);
+}
+
+static void
+base_init(gpointer g_class)
+{
+	GstElementClass *element_class;
+	GstPadTemplate *template;
+
+	element_class = GST_ELEMENT_CLASS(g_class);
+
+	gst_element_class_set_details_simple(element_class,
+					     "DSP audio decoder",
+					     "Codec/Decoder/Audio",
+					     "Decodes audio with TI's DSP algorithms",
+					     "Víctor M. Jáquez Leal");
+
+	template = gst_pad_template_new("src", GST_PAD_SRC,
+					GST_PAD_ALWAYS,
+					generate_src_template());
+
+	gst_element_class_add_pad_template(element_class, template);
+	gst_object_unref(template);
+
+	template = gst_pad_template_new("sink", GST_PAD_SINK,
+					GST_PAD_ALWAYS,
+					generate_sink_template());
+
+	gst_element_class_add_pad_template(element_class, template);
+	gst_object_unref(template);
+}
+
+static void
+class_init(gpointer g_class,
+	   gpointer class_data)
+{
+	parent_class = g_type_class_peek_parent(g_class);
+}
+
+GType
+gst_dsp_adec_get_type(void)
+{
+	static GType type;
+
+	if (G_UNLIKELY(type == 0)) {
+		GTypeInfo type_info = {
+			.class_size = sizeof(GstDspADecClass),
+			.class_init = class_init,
+			.base_init = base_init,
+			.instance_size = sizeof(GstDspADec),
+			.instance_init = instance_init,
+		};
+
+		type = g_type_register_static(GST_DSP_BASE_TYPE, "GstDspADec", &type_info, 0);
+	}
+
+	return type;
+}
diff --git a/gstdspadec.h b/gstdspadec.h
new file mode 100644
index 0000000..5f48463
--- /dev/null
+++ b/gstdspadec.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Víctor M. Jáquez Leal
+ *
+ * Author: Víctor M. Jáquez Leal <vja...@igalia.com>
+ *
+ * This file may be used under the terms of the GNU Lesser General Public
+ * License version 2.1, a copy of which is found in LICENSE included in the
+ * packaging of this file.
+ */
+
+#ifndef GST_DSP_ADEC_H
+#define GST_DSP_ADEC_H
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_DSP_ADEC(obj) (GstDspADec *)(obj)
+#define GST_DSP_ADEC_TYPE (gst_dsp_adec_get_type())
+#define GST_DSP_ADEC_CLASS(obj) (GstDspADecClass *)(obj)
+
+typedef struct GstDspADec GstDspADec;
+typedef struct GstDspADecClass GstDspADecClass;
+
+#include "gstdspbase.h"
+
+enum {
+	GSTDSP_AACDEC,
+};
+
+struct GstDspADec {
+	GstDspBase element;
+	guint samplerate;
+	gboolean parametric_stereo;
+	gboolean packetised;
+	gboolean raw;
+};
+
+struct GstDspADecClass {
+	GstDspBaseClass parent_class;
+};
+
+GType gst_dsp_adec_get_type(void);
+
+G_END_DECLS
+
+#endif /* GST_DSP_ADEC_H */
diff --git a/gstdspbase.h b/gstdspbase.h
index 0014635..90fece4 100644
--- a/gstdspbase.h
+++ b/gstdspbase.h
@@ -159,6 +159,8 @@ extern struct td_codec td_mp4venc_codec;
 extern struct td_codec td_jpegenc_codec;
 extern struct td_codec td_h264enc_codec;
 
+extern struct td_codec td_aacdec_codec;
+
 G_END_DECLS
 
 #endif /* GST_DSP_BASE_H */
diff --git a/plugin.c b/plugin.c
index 82de833..70a7722 100644
--- a/plugin.c
+++ b/plugin.c
@@ -12,6 +12,7 @@
 
 #include "gstdspdummy.h"
 #include "gstdspvdec.h"
+#include "gstdspadec.h"
 #include "gstdsph263enc.h"
 #include "gstdspmp4venc.h"
 #include "gstdspjpegenc.h"
@@ -32,6 +33,9 @@ plugin_init(GstPlugin *plugin)
 	if (!gst_element_register(plugin, "dspvdec", GST_RANK_PRIMARY, GST_DSP_VDEC_TYPE))
 		return FALSE;
 
+	if (!gst_element_register(plugin, "dspadec", GST_RANK_SECONDARY, GST_DSP_ADEC_TYPE))
+		return FALSE;
+
 	if (!gst_element_register(plugin, "dsph263enc", GST_RANK_PRIMARY, GST_DSP_H263ENC_TYPE))
 		return FALSE;
 
diff --git a/tidsp/td_aacdec.c b/tidsp/td_aacdec.c
new file mode 100644
index 0000000..bde6eb5
--- /dev/null
+++ b/tidsp/td_aacdec.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Author: Víctor Manuel Jáquez Leal <vja...@igalia.com>
+ *
+ * This file may be used under the terms of the GNU Lesser General Public
+ * License version 2.1, a copy of which is found in LICENSE included in the
+ * packaging of this file.
+ */
+
+#include "dsp_bridge.h"
+#include "dmm_buffer.h"
+
+#include "gstdspbase.h"
+#include "gstdspadec.h"
+
+#include "util.h"
+
+struct create_args {
+	uint32_t size;
+	uint16_t num_streams;
+
+	uint16_t in_id;
+	uint16_t in_type;
+	uint16_t in_count;
+
+	uint16_t out_id;
+	uint16_t out_type;
+	uint16_t out_count;
+
+	uint16_t out_is_24bps;
+	uint16_t in_is_framed;
+};
+
+static void create_args(GstDspBase *base, unsigned *profile_id, void **arg_data)
+{
+	GstDspADec *self = GST_DSP_ADEC(base);
+
+	struct create_args args = {
+		.size = 9 * sizeof(uint16_t), /* sizeof(args)-4 => alignment issue */
+		.num_streams = 2,
+		.in_id = 0,
+		.in_type = 0,
+		.in_count = base->ports[0]->num_buffers,
+		.out_id = 1,
+		.in_type = 0,
+		.out_count = base->ports[1]->num_buffers,
+		.out_is_24bps = 0,
+		.in_is_framed = self->packetised ? 1 : 0,
+	};
+
+	*profile_id = -1;
+
+	*arg_data = malloc(sizeof(args));
+	memcpy(*arg_data, &args, sizeof(args));
+}
+
+struct dyn_params {
+	uint32_t size;
+	uint32_t out_format;   /* 1 = interleaved / 0 = block */
+	uint32_t enable_ds;    /* do enable down sampler? */
+	uint32_t enable_ps;    /* do enable parametric stereo? */
+	uint32_t samplerate;  /* sample rate index */
+	uint32_t is_raw;       /* is raw format? */
+	uint32_t is_dual_mono; /* is dual mono? */
+};
+
+static inline int
+get_sample_rate_index(int rate)
+{
+	int i;
+	int rates[] = { 96000, 88200, 64000, 48000, 44100, 32000,
+			24000, 22050, 16000, 12000, 11025, 8000 };
+
+	for (i = 0; i < ARRAY_SIZE(rates); i++)
+		if (rate == rates[i])
+			return i;
+
+	return 0; /* 96000 by default */
+}
+
+static void send_params(GstDspBase *base, struct dsp_node *node)
+{
+	struct dyn_params *params;
+	dmm_buffer_t *b;
+	GstDspADec *self = GST_DSP_ADEC(base);
+
+	b = dmm_buffer_calloc(base->dsp_handle, base->proc,
+			sizeof(*params), DMA_TO_DEVICE);
+
+	params = b->data;
+	params->size = sizeof(*params);
+	params->out_format    = 1; /* Interleaved */
+	params->enable_ds     = 1; /* Always on to avoid upsample issues */
+	params->enable_ps     = self->parametric_stereo ? 1: 0;
+	params->samplerate    = get_sample_rate_index(self->samplerate);
+	params->is_raw        = self->raw ? 1 : 0;
+	params->is_dual_mono  = 0; /* ??? */
+
+	gstdsp_send_alg_ctrl(base, node, b);
+}
+
+struct in_params {
+	uint16_t is_last;
+	uint16_t is_conceal;
+	uint32_t index;
+};
+
+struct out_params {
+	uint32_t count;
+	uint32_t is_last;
+	uint32_t index;
+};
+
+static void setup_in_params(GstDspBase *base, dmm_buffer_t *tmp)
+{
+	struct in_params *in_param;
+
+	in_param = tmp->data;
+	in_param->is_last = 0;
+	in_param->is_conceal = 0;
+	in_param->index = 0;
+}
+
+static void setup_params(GstDspBase *base)
+{
+	struct in_params *in_param;
+	struct out_params *out_param;
+	du_port_t *p;
+
+	p = base->ports[0];
+	gstdsp_port_setup_params(base, p, sizeof(*in_param), setup_in_params);
+
+	p = base->ports[1];
+	gstdsp_port_setup_params(base, p, sizeof(*out_param), NULL);
+}
+
+static inline void update_caps(GstDspBase *base, struct dsp_node *node)
+{
+	GstDspADec *self = GST_DSP_ADEC(base);
+	GstCaps *caps;
+
+	send_params(base, base->node);
+
+	pr_info(self, "new sample rate %u", self->samplerate);
+	caps = gst_pad_get_negotiated_caps(base->srcpad);
+	caps = gst_caps_make_writable(caps);
+	gst_caps_set_simple(caps, "rate", G_TYPE_INT, self->samplerate, NULL);
+	gst_pad_take_caps(base->srcpad, caps);
+}
+
+static void update_params(GstDspBase *base, struct dsp_node *node, uint32_t msg)
+{
+	GstDspADec *self = GST_DSP_ADEC(base);
+
+	if (msg == 0x0601 && self->parametric_stereo) { /* SBR */
+		self->parametric_stereo = FALSE;
+		self->samplerate /= 2;
+		update_caps(base, node);
+	} else if (msg == 0x0602 && !self->parametric_stereo) { /* PS */
+		self->parametric_stereo = TRUE;
+		self->samplerate *= 2;
+		update_caps(base, node);
+	}
+}
+
+struct td_codec td_aacdec_codec = {
+	.uuid = &(const struct dsp_uuid) { 0x5c89a1f1, 0x3d83, 0x11d6, 0xb0, 0xd7,
+		{ 0x00, 0xc0, 0x4f, 0x1f, 0xc0, 0x36 } },
+	.filename = "mpeg4aacdec_sn.dll64P",
+	.setup_params = setup_params,
+	.create_args = create_args,
+	.send_params = send_params,
+	.update_params = update_params,
+};
-- 
1.7.0.2
Why do you check for 'self->node'? If the node is there I don't think got_message could be called.
> +				codec->update_params(self, self->node, msg->arg_2);
> +			break;
> +		}
Cheers.
-- 
Felipe Contreras
10%? That's a bit too much for sending audio buffers, isn't it?
> My testing pipeline is something like:
> 
> gst-launch filesrc location=test.aac ! aacparse ! dspadec ! alsasink
> 
> Issues: when a parametric stereo media is found, a downstream caps
> renegotiation is done, but somehow (I don't understand why) the
> renegotiation is not honored and the output is rendered wrong (because
> of the reduced framerate).
> 
> Signed-off-by: Víctor Manuel Jáquez Leal <vja...@igalia.com>
Looks fine to me.
-- 
Felipe Contreras
Yep, I was confused when I wrote it.
vmjl
Shall I resend these patches with this modification?
Do you plan to integrate the duration, vpp and this into your next branch?
vmjl
Please do. You might beat me to it.
> Do you plan to integrate the duration, vpp and this into your next branch?
Yeap. Any time now :)
-- 
Felipe Contreras