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