• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2009  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 "gstsbcutil.h"
31 #include "gstsbcdec.h"
32 
33 GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug);
34 #define GST_CAT_DEFAULT sbc_dec_debug
35 
36 GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
37 
38 static const GstElementDetails sbc_dec_details =
39 	GST_ELEMENT_DETAILS("Bluetooth SBC decoder",
40 				"Codec/Decoder/Audio",
41 				"Decode a SBC audio stream",
42 				"Marcel Holtmann <marcel@holtmann.org>");
43 
44 static GstStaticPadTemplate sbc_dec_sink_factory =
45 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
46 		GST_STATIC_CAPS("audio/x-sbc"));
47 
48 static GstStaticPadTemplate sbc_dec_src_factory =
49 	GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
50 		GST_STATIC_CAPS("audio/x-raw-int, "
51 				"rate = (int) { 16000, 32000, 44100, 48000 }, "
52 				"channels = (int) [ 1, 2 ], "
53 				"endianness = (int) BYTE_ORDER, "
54 				"signed = (boolean) true, "
55 				"width = (int) 16, "
56 				"depth = (int) 16"));
57 
sbc_dec_chain(GstPad * pad,GstBuffer * buffer)58 static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer)
59 {
60 	GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad));
61 	GstFlowReturn res = GST_FLOW_OK;
62 	guint size, codesize, offset = 0;
63 	guint8 *data;
64 
65 	codesize = sbc_get_codesize(&dec->sbc);
66 
67 	if (dec->buffer) {
68 		GstBuffer *temp = buffer;
69 		buffer = gst_buffer_span(dec->buffer, 0, buffer,
70 			GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer));
71 		gst_buffer_unref(temp);
72 		gst_buffer_unref(dec->buffer);
73 		dec->buffer = NULL;
74 	}
75 
76 	data = GST_BUFFER_DATA(buffer);
77 	size = GST_BUFFER_SIZE(buffer);
78 
79 	while (offset < size) {
80 		GstBuffer *output;
81 		GstPadTemplate *template;
82 		GstCaps *caps;
83 		int consumed;
84 
85 		res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad,
86 						GST_BUFFER_OFFSET_NONE,
87 						codesize, NULL, &output);
88 
89 		if (res != GST_FLOW_OK)
90 			goto done;
91 
92 		consumed = sbc_decode(&dec->sbc, data + offset, size - offset,
93 					GST_BUFFER_DATA(output), codesize,
94 					NULL);
95 		if (consumed <= 0)
96 			break;
97 
98 		/* we will reuse the same caps object */
99 		if (dec->outcaps == NULL) {
100 			caps = gst_caps_new_simple("audio/x-raw-int",
101 					"rate", G_TYPE_INT,
102 					gst_sbc_parse_rate_from_sbc(
103 						dec->sbc.frequency),
104 					"channels", G_TYPE_INT,
105 					gst_sbc_get_channel_number(
106 						dec->sbc.mode),
107 					NULL);
108 
109 			template = gst_static_pad_template_get(&sbc_dec_src_factory);
110 
111 			dec->outcaps = gst_caps_intersect(caps,
112 						gst_pad_template_get_caps(template));
113 
114 			gst_caps_unref(caps);
115 		}
116 
117 		gst_buffer_set_caps(output, dec->outcaps);
118 
119 		/* FIXME get a real timestamp */
120 		GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE;
121 
122 		res = gst_pad_push(dec->srcpad, output);
123 		if (res != GST_FLOW_OK)
124 			goto done;
125 
126 		offset += consumed;
127 	}
128 
129 	if (offset < size)
130 		dec->buffer = gst_buffer_create_sub(buffer,
131 							offset, size - offset);
132 
133 done:
134 	gst_buffer_unref(buffer);
135 	gst_object_unref(dec);
136 
137 	return res;
138 }
139 
sbc_dec_change_state(GstElement * element,GstStateChange transition)140 static GstStateChangeReturn sbc_dec_change_state(GstElement *element,
141 						GstStateChange transition)
142 {
143 	GstSbcDec *dec = GST_SBC_DEC(element);
144 
145 	switch (transition) {
146 	case GST_STATE_CHANGE_READY_TO_PAUSED:
147 		GST_DEBUG("Setup subband codec");
148 		if (dec->buffer) {
149 			gst_buffer_unref(dec->buffer);
150 			dec->buffer = NULL;
151 		}
152 		sbc_init(&dec->sbc, 0);
153 		dec->outcaps = NULL;
154 		break;
155 
156 	case GST_STATE_CHANGE_PAUSED_TO_READY:
157 		GST_DEBUG("Finish subband codec");
158 		if (dec->buffer) {
159 			gst_buffer_unref(dec->buffer);
160 			dec->buffer = NULL;
161 		}
162 		sbc_finish(&dec->sbc);
163 		if (dec->outcaps) {
164 			gst_caps_unref(dec->outcaps);
165 			dec->outcaps = NULL;
166 		}
167 		break;
168 
169 	default:
170 		break;
171 	}
172 
173 	return parent_class->change_state(element, transition);
174 }
175 
gst_sbc_dec_base_init(gpointer g_class)176 static void gst_sbc_dec_base_init(gpointer g_class)
177 {
178 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
179 
180 	gst_element_class_add_pad_template(element_class,
181 		gst_static_pad_template_get(&sbc_dec_sink_factory));
182 
183 	gst_element_class_add_pad_template(element_class,
184 		gst_static_pad_template_get(&sbc_dec_src_factory));
185 
186 	gst_element_class_set_details(element_class, &sbc_dec_details);
187 }
188 
gst_sbc_dec_class_init(GstSbcDecClass * klass)189 static void gst_sbc_dec_class_init(GstSbcDecClass *klass)
190 {
191 	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
192 
193 	parent_class = g_type_class_peek_parent(klass);
194 
195 	element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state);
196 
197 	GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0,
198 						"SBC decoding element");
199 }
200 
gst_sbc_dec_init(GstSbcDec * self,GstSbcDecClass * klass)201 static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass)
202 {
203 	self->sinkpad = gst_pad_new_from_static_template(
204 			&sbc_dec_sink_factory, "sink");
205 	gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(
206 			sbc_dec_chain));
207 	gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
208 
209 	self->srcpad = gst_pad_new_from_static_template(
210 			&sbc_dec_src_factory, "src");
211 	gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
212 
213 	self->outcaps = NULL;
214 }
215 
gst_sbc_dec_plugin_init(GstPlugin * plugin)216 gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin)
217 {
218 	return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY,
219 							GST_TYPE_SBC_DEC);
220 }
221 
222 
223