Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Lesser General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2.1 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Lesser General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Lesser General Public
     19  *  License along with this library; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <string.h>
     29 
     30 #include "gstpragma.h"
     31 #include "gstsbcutil.h"
     32 #include "gstsbcdec.h"
     33 
     34 GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug);
     35 #define GST_CAT_DEFAULT sbc_dec_debug
     36 
     37 GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
     38 
     39 static const GstElementDetails sbc_dec_details =
     40 	GST_ELEMENT_DETAILS("Bluetooth SBC decoder",
     41 				"Codec/Decoder/Audio",
     42 				"Decode a SBC audio stream",
     43 				"Marcel Holtmann <marcel (at) holtmann.org>");
     44 
     45 static GstStaticPadTemplate sbc_dec_sink_factory =
     46 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
     47 		GST_STATIC_CAPS("audio/x-sbc"));
     48 
     49 static GstStaticPadTemplate sbc_dec_src_factory =
     50 	GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
     51 		GST_STATIC_CAPS("audio/x-raw-int, "
     52 				"rate = (int) { 16000, 32000, 44100, 48000 }, "
     53 				"channels = (int) [ 1, 2 ], "
     54 				"endianness = (int) BYTE_ORDER, "
     55 				"signed = (boolean) true, "
     56 				"width = (int) 16, "
     57 				"depth = (int) 16"));
     58 
     59 static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer)
     60 {
     61 	GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad));
     62 	GstFlowReturn res = GST_FLOW_OK;
     63 	guint size, codesize, offset = 0;
     64 	guint8 *data;
     65 
     66 	codesize = sbc_get_codesize(&dec->sbc);
     67 
     68 	if (dec->buffer) {
     69 		GstBuffer *temp = buffer;
     70 		buffer = gst_buffer_span(dec->buffer, 0, buffer,
     71 			GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer));
     72 		gst_buffer_unref(temp);
     73 		gst_buffer_unref(dec->buffer);
     74 		dec->buffer = NULL;
     75 	}
     76 
     77 	data = GST_BUFFER_DATA(buffer);
     78 	size = GST_BUFFER_SIZE(buffer);
     79 
     80 	while (offset < size) {
     81 		GstBuffer *output;
     82 		GstPadTemplate *template;
     83 		GstCaps *caps;
     84 		int consumed;
     85 
     86 		res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad,
     87 						GST_BUFFER_OFFSET_NONE,
     88 						codesize, NULL, &output);
     89 
     90 		if (res != GST_FLOW_OK)
     91 			goto done;
     92 
     93 		consumed = sbc_decode(&dec->sbc, data + offset, size - offset,
     94 					GST_BUFFER_DATA(output), codesize,
     95 					NULL);
     96 		if (consumed <= 0)
     97 			break;
     98 
     99 		/* we will reuse the same caps object */
    100 		if (dec->outcaps == NULL) {
    101 			caps = gst_caps_new_simple("audio/x-raw-int",
    102 					"rate", G_TYPE_INT,
    103 					gst_sbc_parse_rate_from_sbc(
    104 						dec->sbc.frequency),
    105 					"channels", G_TYPE_INT,
    106 					gst_sbc_get_channel_number(
    107 						dec->sbc.mode),
    108 					NULL);
    109 
    110 			template = gst_static_pad_template_get(&sbc_dec_src_factory);
    111 
    112 			dec->outcaps = gst_caps_intersect(caps,
    113 						gst_pad_template_get_caps(template));
    114 
    115 			gst_caps_unref(caps);
    116 		}
    117 
    118 		gst_buffer_set_caps(output, dec->outcaps);
    119 
    120 		/* FIXME get a real timestamp */
    121 		GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE;
    122 
    123 		res = gst_pad_push(dec->srcpad, output);
    124 		if (res != GST_FLOW_OK)
    125 			goto done;
    126 
    127 		offset += consumed;
    128 	}
    129 
    130 	if (offset < size)
    131 		dec->buffer = gst_buffer_create_sub(buffer,
    132 							offset, size - offset);
    133 
    134 done:
    135 	gst_buffer_unref(buffer);
    136 	gst_object_unref(dec);
    137 
    138 	return res;
    139 }
    140 
    141 static GstStateChangeReturn sbc_dec_change_state(GstElement *element,
    142 						GstStateChange transition)
    143 {
    144 	GstSbcDec *dec = GST_SBC_DEC(element);
    145 
    146 	switch (transition) {
    147 	case GST_STATE_CHANGE_READY_TO_PAUSED:
    148 		GST_DEBUG("Setup subband codec");
    149 		if (dec->buffer) {
    150 			gst_buffer_unref(dec->buffer);
    151 			dec->buffer = NULL;
    152 		}
    153 		sbc_init(&dec->sbc, 0);
    154 		dec->outcaps = NULL;
    155 		break;
    156 
    157 	case GST_STATE_CHANGE_PAUSED_TO_READY:
    158 		GST_DEBUG("Finish subband codec");
    159 		if (dec->buffer) {
    160 			gst_buffer_unref(dec->buffer);
    161 			dec->buffer = NULL;
    162 		}
    163 		sbc_finish(&dec->sbc);
    164 		if (dec->outcaps) {
    165 			gst_caps_unref(dec->outcaps);
    166 			dec->outcaps = NULL;
    167 		}
    168 		break;
    169 
    170 	default:
    171 		break;
    172 	}
    173 
    174 	return parent_class->change_state(element, transition);
    175 }
    176 
    177 static void gst_sbc_dec_base_init(gpointer g_class)
    178 {
    179 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
    180 
    181 	gst_element_class_add_pad_template(element_class,
    182 		gst_static_pad_template_get(&sbc_dec_sink_factory));
    183 
    184 	gst_element_class_add_pad_template(element_class,
    185 		gst_static_pad_template_get(&sbc_dec_src_factory));
    186 
    187 	gst_element_class_set_details(element_class, &sbc_dec_details);
    188 }
    189 
    190 static void gst_sbc_dec_class_init(GstSbcDecClass *klass)
    191 {
    192 	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
    193 
    194 	parent_class = g_type_class_peek_parent(klass);
    195 
    196 	element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state);
    197 
    198 	GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0,
    199 						"SBC decoding element");
    200 }
    201 
    202 static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass)
    203 {
    204 	self->sinkpad = gst_pad_new_from_static_template(
    205 			&sbc_dec_sink_factory, "sink");
    206 	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(
    207 			sbc_dec_chain));
    208 	gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
    209 
    210 	self->srcpad = gst_pad_new_from_static_template(
    211 			&sbc_dec_src_factory, "src");
    212 	gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
    213 
    214 	self->outcaps = NULL;
    215 }
    216 
    217 gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin)
    218 {
    219 	return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY,
    220 							GST_TYPE_SBC_DEC);
    221 }
    222 
    223 
    224