• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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@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 
sbc_dec_chain(GstPad * pad,GstBuffer * buffer)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 
sbc_dec_change_state(GstElement * element,GstStateChange transition)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 
gst_sbc_dec_base_init(gpointer g_class)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 
gst_sbc_dec_class_init(GstSbcDecClass * klass)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 
gst_sbc_dec_init(GstSbcDec * self,GstSbcDecClass * klass)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 
gst_sbc_dec_plugin_init(GstPlugin * plugin)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