From 36f952b627de6c7f1339a4be95923579d4ea825f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 17:03:03 +0000 Subject: [PATCH] sbc: Implement full decoding support --- ext/sbc/gstsbcdec.c | 134 +++++++++++++++++++++++++++++++++++++++++++- ext/sbc/gstsbcdec.h | 6 ++ 2 files changed, 138 insertions(+), 2 deletions(-) diff --git a/ext/sbc/gstsbcdec.c b/ext/sbc/gstsbcdec.c index f7067a9b2e..1f75e384f4 100644 --- a/ext/sbc/gstsbcdec.c +++ b/ext/sbc/gstsbcdec.c @@ -25,7 +25,7 @@ #include #endif -#include "sbc.h" +#include #include "gstsbcdec.h" @@ -40,23 +40,153 @@ GST_ELEMENT_DETAILS ("Bluetooth SBC decoder", "Decode a SBC audio stream", "Marcel Holtmann "); +static GstStaticPadTemplate sbc_dec_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-sbc")); + +static GstStaticPadTemplate sbc_dec_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "rate = (int) [ 6000, 48000 ], " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")); + +static GstFlowReturn +sbc_dec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSbcDec *dec = GST_SBC_DEC (gst_pad_get_parent (pad)); + GstFlowReturn res = GST_FLOW_OK; + guint size, offset = 0; + guint8 *data; + GstClockTime timestamp; + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (dec->buffer) { + GstBuffer *temp = buffer; + buffer = gst_buffer_span (dec->buffer, 0, buffer, + GST_BUFFER_SIZE (dec->buffer) + GST_BUFFER_SIZE (buffer)); + gst_buffer_unref (temp); + gst_buffer_unref (dec->buffer); + dec->buffer = NULL; + } + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + while (offset < size) { + GstBuffer *output; + GstPadTemplate *template; + GstCaps *caps, *temp; + int consumed; + + consumed = sbc_decode (&dec->sbc, data + offset, size - offset); + if (consumed <= 0) + break; + + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sbc.rate, + "channels", G_TYPE_INT, dec->sbc.channels, NULL); + + template = gst_static_pad_template_get (&sbc_dec_src_factory); + + temp = gst_caps_intersect (caps, gst_pad_template_get_caps (template)); + + gst_caps_unref (caps); + + res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, + GST_BUFFER_OFFSET_NONE, dec->sbc.len, temp, &output); + + gst_caps_unref (temp); + + if (res != GST_FLOW_OK) + goto done; + + memcpy (GST_BUFFER_DATA (output), dec->sbc.data, dec->sbc.len); + + res = gst_pad_push (dec->srcpad, output); + if (res != GST_FLOW_OK) + goto done; + + offset += consumed; + } + + if (offset < size) + dec->buffer = gst_buffer_create_sub (buffer, offset, size - offset); + +done: + gst_buffer_unref (buffer); + gst_object_unref (dec); + + return res; +} + +static GstStateChangeReturn +sbc_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstSbcDec *dec = GST_SBC_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG ("Setup subband codec"); + if (dec->buffer) { + gst_buffer_unref (dec->buffer); + dec->buffer = NULL; + } + sbc_init (&dec->sbc, 0); + break; + + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG ("Finish subband codec"); + if (dec->buffer) { + gst_buffer_unref (dec->buffer); + dec->buffer = NULL; + } + sbc_finish (&dec->sbc); + break; + + default: + break; + } + + return parent_class->change_state (element, transition); +} + static void gst_sbc_dec_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sbc_dec_sink_factory)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sbc_dec_src_factory)); + gst_element_class_set_details (element_class, &sbc_dec_details); } static void gst_sbc_dec_class_init (GstSbcDecClass * klass) { + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + parent_class = g_type_class_peek_parent (klass); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (sbc_dec_change_state); + GST_DEBUG_CATEGORY_INIT (sbc_dec_debug, "sbcdec", 0, "SBC decoding element"); } static void -gst_sbc_dec_init (GstSbcDec * sbcdec, GstSbcDecClass * klass) +gst_sbc_dec_init (GstSbcDec * self, GstSbcDecClass * klass) { + self->sinkpad = + gst_pad_new_from_static_template (&sbc_dec_sink_factory, "sink"); + gst_pad_set_chain_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_dec_chain)); + gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template (&sbc_dec_src_factory, "src"); + gst_element_add_pad (GST_ELEMENT (self), self->srcpad); } diff --git a/ext/sbc/gstsbcdec.h b/ext/sbc/gstsbcdec.h index 0737b9d96a..4a6922a090 100644 --- a/ext/sbc/gstsbcdec.h +++ b/ext/sbc/gstsbcdec.h @@ -23,6 +23,8 @@ #include +#include "sbc.h" + G_BEGIN_DECLS #define GST_TYPE_SBC_DEC \ @@ -44,6 +46,10 @@ struct _GstSbcDec { GstPad *sinkpad; GstPad *srcpad; + + GstBuffer *buffer; + + sbc_t sbc; }; struct _GstSbcDecClass {