1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@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
34GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug);
35#define GST_CAT_DEFAULT sbc_dec_debug
36
37GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
38
39static 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@holtmann.org>");
44
45static 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
49static 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
59static 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
134done:
135	gst_buffer_unref(buffer);
136	gst_object_unref(dec);
137
138	return res;
139}
140
141static 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
177static 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
190static 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
202static 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
217gboolean 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