/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org> * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <string.h> #include "gstsbcutil.h" #include "gstsbcdec.h" GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug); #define GST_CAT_DEFAULT sbc_dec_debug GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT); static const GstElementDetails sbc_dec_details = GST_ELEMENT_DETAILS("Bluetooth SBC decoder", "Codec/Decoder/Audio", "Decode a SBC audio stream", "Marcel Holtmann <marcel@holtmann.org>"); 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) { 16000, 32000, 44100, 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, codesize, offset = 0; guint8 *data; codesize = sbc_get_codesize(&dec->sbc); 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; int consumed; res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, GST_BUFFER_OFFSET_NONE, codesize, NULL, &output); if (res != GST_FLOW_OK) goto done; consumed = sbc_decode(&dec->sbc, data + offset, size - offset, GST_BUFFER_DATA(output), codesize, NULL); if (consumed <= 0) break; /* we will reuse the same caps object */ if (dec->outcaps == NULL) { caps = gst_caps_new_simple("audio/x-raw-int", "rate", G_TYPE_INT, gst_sbc_parse_rate_from_sbc( dec->sbc.frequency), "channels", G_TYPE_INT, gst_sbc_get_channel_number( dec->sbc.mode), NULL); template = gst_static_pad_template_get(&sbc_dec_src_factory); dec->outcaps = gst_caps_intersect(caps, gst_pad_template_get_caps(template)); gst_caps_unref(caps); } gst_buffer_set_caps(output, dec->outcaps); /* FIXME get a real timestamp */ GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE; 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); dec->outcaps = NULL; 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); if (dec->outcaps) { gst_caps_unref(dec->outcaps); dec->outcaps = NULL; } 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 *element_class = GST_ELEMENT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); element_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 *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); self->outcaps = NULL; } gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin) { return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY, GST_TYPE_SBC_DEC); }