1 /* GStreamer SBC audio parser
2 * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 /**
25 * SECTION:element-sbcparse
26 * @see_also: sbcdec, sbcenc
27 *
28 * The sbcparse element will parse a bluetooth SBC audio stream into
29 * frames and timestamp them properly.
30 *
31 * Since: 1.2.0
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "gstsbcparse.h"
39
40 #include <string.h>
41 #include <gst/tag/tag.h>
42 #include <gst/audio/audio.h>
43 #include <gst/base/base.h>
44 #include <gst/pbutils/pbutils.h>
45
46 #define SBC_SYNCBYTE 0x9C
47
48 GST_DEBUG_CATEGORY_STATIC (sbcparse_debug);
49 #define GST_CAT_DEFAULT sbcparse_debug
50
51 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
52 GST_PAD_SRC,
53 GST_PAD_ALWAYS,
54 GST_STATIC_CAPS ("audio/x-sbc, parsed = (boolean) true, "
55 "channels = (int) [ 1, 2 ], "
56 "rate = (int) { 16000, 32000, 44100, 48000 }")
57 );
58
59 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
60 GST_PAD_SINK,
61 GST_PAD_ALWAYS,
62 GST_STATIC_CAPS ("audio/x-sbc")
63 );
64
65 static gboolean gst_sbc_parse_start (GstBaseParse * parse);
66 static gboolean gst_sbc_parse_stop (GstBaseParse * parse);
67 static GstFlowReturn gst_sbc_parse_handle_frame (GstBaseParse * parse,
68 GstBaseParseFrame * frame, gint * skipsize);
69 static GstFlowReturn gst_sbc_parse_pre_push_frame (GstBaseParse * parse,
70 GstBaseParseFrame * frame);
71 static GstCaps *gst_sbc_parse_get_sink_caps (GstBaseParse * parse,
72 GstCaps * filter);
73
74 static guint8 gst_sbc_calculate_crc8 (const guint8 * data, gint bits_crc);
75 static gsize gst_sbc_calc_framelen (guint subbands, GstSbcChannelMode ch_mode,
76 guint blocks, guint bitpool);
77 static gsize gst_sbc_parse_header (const guint8 * data, guint * rate,
78 guint * n_blocks, GstSbcChannelMode * ch_mode,
79 GstSbcAllocationMethod * alloc_method, guint * n_subbands, guint * bitpool);
80
81 #define parent_class gst_sbc_parse_parent_class
82 G_DEFINE_TYPE (GstSbcParse, gst_sbc_parse, GST_TYPE_BASE_PARSE);
83
84 static void
gst_sbc_parse_class_init(GstSbcParseClass * klass)85 gst_sbc_parse_class_init (GstSbcParseClass * klass)
86 {
87 GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass);
88 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
89
90 GST_DEBUG_CATEGORY_INIT (sbcparse_debug, "sbcparse", 0, "SBC audio parser");
91
92 baseparse_class->start = GST_DEBUG_FUNCPTR (gst_sbc_parse_start);
93 baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_sbc_parse_stop);
94 baseparse_class->pre_push_frame =
95 GST_DEBUG_FUNCPTR (gst_sbc_parse_pre_push_frame);
96 baseparse_class->handle_frame =
97 GST_DEBUG_FUNCPTR (gst_sbc_parse_handle_frame);
98 baseparse_class->get_sink_caps =
99 GST_DEBUG_FUNCPTR (gst_sbc_parse_get_sink_caps);
100
101 gst_element_class_add_static_pad_template (element_class, &src_factory);
102 gst_element_class_add_static_pad_template (element_class, &sink_factory);
103
104 gst_element_class_set_static_metadata (element_class, "SBC audio parser",
105 "Codec/Parser/Audio", "Parses an SBC bluetooth audio stream",
106 "Tim-Philipp Müller <tim.muller@collabora.co.uk>");
107 }
108
109 static void
gst_sbc_parse_reset(GstSbcParse * sbcparse)110 gst_sbc_parse_reset (GstSbcParse * sbcparse)
111 {
112 sbcparse->alloc_method = GST_SBC_ALLOCATION_METHOD_INVALID;
113 sbcparse->ch_mode = GST_SBC_CHANNEL_MODE_INVALID;
114 sbcparse->rate = -1;
115 sbcparse->n_blocks = -1;
116 sbcparse->n_subbands = -1;
117 sbcparse->bitpool = -1;
118 sbcparse->sent_codec_tag = FALSE;
119 }
120
121 static void
gst_sbc_parse_init(GstSbcParse * sbcparse)122 gst_sbc_parse_init (GstSbcParse * sbcparse)
123 {
124 gst_sbc_parse_reset (sbcparse);
125 GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (sbcparse));
126 GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (sbcparse));
127 }
128
129 static gboolean
gst_sbc_parse_start(GstBaseParse * parse)130 gst_sbc_parse_start (GstBaseParse * parse)
131 {
132 gst_base_parse_set_min_frame_size (parse,
133 gst_sbc_calc_framelen (4, GST_SBC_CHANNEL_MODE_MONO, 4, 2));
134
135 gst_base_parse_set_has_timing_info (parse, FALSE);
136
137 gst_base_parse_set_syncable (parse, TRUE);
138
139 return TRUE;
140 }
141
142 static gboolean
gst_sbc_parse_stop(GstBaseParse * parse)143 gst_sbc_parse_stop (GstBaseParse * parse)
144 {
145 gst_sbc_parse_reset (GST_SBC_PARSE (parse));
146 return TRUE;
147 }
148
149 static const gchar *
gst_sbc_channel_mode_get_name(GstSbcChannelMode ch_mode)150 gst_sbc_channel_mode_get_name (GstSbcChannelMode ch_mode)
151 {
152 switch (ch_mode) {
153 case GST_SBC_CHANNEL_MODE_MONO:
154 return "mono";
155 case GST_SBC_CHANNEL_MODE_DUAL:
156 return "dual";
157 case GST_SBC_CHANNEL_MODE_STEREO:
158 return "stereo";
159 case GST_SBC_CHANNEL_MODE_JOINT_STEREO:
160 return "joint";
161 default:
162 break;
163 }
164 return "invalid";
165 }
166
167 static const gchar *
gst_sbc_allocation_method_get_name(GstSbcAllocationMethod alloc_method)168 gst_sbc_allocation_method_get_name (GstSbcAllocationMethod alloc_method)
169 {
170 switch (alloc_method) {
171 case GST_SBC_ALLOCATION_METHOD_SNR:
172 return "snr";
173 case GST_SBC_ALLOCATION_METHOD_LOUDNESS:
174 return "loudness";
175 default:
176 break;
177 }
178 return "invalid";
179 }
180
181 static GstFlowReturn
gst_sbc_parse_handle_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint * skipsize)182 gst_sbc_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame,
183 gint * skipsize)
184 {
185 GstSbcParse *sbcparse = GST_SBC_PARSE (parse);
186 GstSbcAllocationMethod alloc_method = GST_SBC_ALLOCATION_METHOD_INVALID;
187 GstSbcChannelMode ch_mode = GST_SBC_CHANNEL_MODE_INVALID;
188 GstMapInfo map;
189 guint rate = 0, n_blocks = 0, n_subbands = 0, bitpool = 0;
190 gsize frame_len, next_len;
191 gint i, max_frames;
192
193 gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
194
195 g_assert (map.size >= 6);
196
197 frame_len = gst_sbc_parse_header (map.data, &rate, &n_blocks, &ch_mode,
198 &alloc_method, &n_subbands, &bitpool);
199
200 GST_LOG_OBJECT (parse, "frame_len: %u", (guint) frame_len);
201
202 if (frame_len == 0)
203 goto resync;
204
205 if (sbcparse->alloc_method != alloc_method
206 || sbcparse->ch_mode != ch_mode
207 || sbcparse->rate != rate
208 || sbcparse->n_blocks != n_blocks
209 || sbcparse->n_subbands != n_subbands || sbcparse->bitpool != bitpool) {
210 guint avg_bitrate;
211 GstCaps *caps;
212
213 /* FIXME: do all of these need to be in the caps? */
214 caps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT, rate,
215 "channels", G_TYPE_INT, (ch_mode == GST_SBC_CHANNEL_MODE_MONO) ? 1 : 2,
216 "channel-mode", G_TYPE_STRING, gst_sbc_channel_mode_get_name (ch_mode),
217 "blocks", G_TYPE_INT, n_blocks, "subbands", G_TYPE_INT, n_subbands,
218 "allocation-method", G_TYPE_STRING,
219 gst_sbc_allocation_method_get_name (alloc_method),
220 "bitpool", G_TYPE_INT, bitpool, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
221
222 GST_INFO_OBJECT (sbcparse, "caps changed to %" GST_PTR_FORMAT, caps);
223
224 gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (sbcparse),
225 gst_event_new_caps (caps));
226
227 avg_bitrate = (8 * frame_len * rate) / (n_subbands * n_blocks);
228 gst_base_parse_set_average_bitrate (parse, avg_bitrate);
229
230 gst_base_parse_set_frame_rate (parse, rate, n_subbands * n_blocks, 0, 0);
231
232 sbcparse->alloc_method = alloc_method;
233 sbcparse->ch_mode = ch_mode;
234 sbcparse->rate = rate;
235 sbcparse->n_blocks = n_blocks;
236 sbcparse->n_subbands = n_subbands;
237 sbcparse->bitpool = bitpool;
238
239 gst_caps_unref (caps);
240 }
241
242 if (frame_len > map.size)
243 goto need_more_data;
244
245 GST_BUFFER_OFFSET (frame->buffer) = GST_BUFFER_OFFSET_NONE;
246 GST_BUFFER_OFFSET_END (frame->buffer) = GST_BUFFER_OFFSET_NONE;
247
248 /* completely arbitrary limit, we only process data we already have,
249 * so we aren't introducing latency here */
250 max_frames = MIN (map.size / frame_len, n_blocks * n_subbands * 5);
251 GST_LOG_OBJECT (sbcparse, "parsing up to %d frames", max_frames);
252
253 for (i = 1; i < max_frames; ++i) {
254 next_len = gst_sbc_parse_header (map.data + (i * frame_len), &rate,
255 &n_blocks, &ch_mode, &alloc_method, &n_subbands, &bitpool);
256
257 if (next_len != frame_len || sbcparse->alloc_method != alloc_method ||
258 sbcparse->ch_mode != ch_mode || sbcparse->rate != rate ||
259 sbcparse->n_blocks != n_blocks || sbcparse->n_subbands != n_subbands ||
260 sbcparse->bitpool != bitpool) {
261 break;
262 }
263 }
264 GST_LOG_OBJECT (sbcparse, "packing %d SBC frames into next output buffer", i);
265
266 /* Note: local n_subbands and n_blocks variables might be tainted if we
267 * bailed out of the loop above because of a header configuration mismatch */
268 gst_base_parse_set_frame_rate (parse, rate,
269 sbcparse->n_subbands * sbcparse->n_blocks * i, 0, 0);
270
271 gst_buffer_unmap (frame->buffer, &map);
272 return gst_base_parse_finish_frame (parse, frame, i * frame_len);
273
274 resync:
275 {
276 const guint8 *possible_sync;
277
278 GST_DEBUG_OBJECT (parse, "no sync, resyncing");
279
280 possible_sync = memchr (map.data, SBC_SYNCBYTE, map.size);
281
282 if (possible_sync != NULL)
283 *skipsize = (gint) (possible_sync - map.data);
284 else
285 *skipsize = map.size;
286
287 gst_buffer_unmap (frame->buffer, &map);
288
289 /* we could optimise things here by looping over the data and checking
290 * whether the sync is good or not instead of handing control back to
291 * the base class just to be called again */
292 return GST_FLOW_OK;
293 }
294 need_more_data:
295 {
296 GST_LOG_OBJECT (parse,
297 "need %" G_GSIZE_FORMAT " bytes, but only have %" G_GSIZE_FORMAT,
298 frame_len, map.size);
299 gst_base_parse_set_min_frame_size (parse, frame_len);
300 gst_buffer_unmap (frame->buffer, &map);
301 return GST_FLOW_OK;
302 }
303 }
304
305 static void
remove_fields(GstCaps * caps)306 remove_fields (GstCaps * caps)
307 {
308 guint i, n;
309
310 n = gst_caps_get_size (caps);
311 for (i = 0; i < n; i++) {
312 GstStructure *s = gst_caps_get_structure (caps, i);
313
314 gst_structure_remove_field (s, "parsed");
315 }
316 }
317
318 static GstCaps *
gst_sbc_parse_get_sink_caps(GstBaseParse * parse,GstCaps * filter)319 gst_sbc_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
320 {
321 GstCaps *peercaps, *templ;
322 GstCaps *res;
323
324 templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
325 if (filter) {
326 GstCaps *fcopy = gst_caps_copy (filter);
327 /* Remove the fields we convert */
328 remove_fields (fcopy);
329 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
330 gst_caps_unref (fcopy);
331 } else
332 peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
333
334 if (peercaps) {
335 /* Remove the parsed field */
336 peercaps = gst_caps_make_writable (peercaps);
337 remove_fields (peercaps);
338
339 res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
340 gst_caps_unref (peercaps);
341 gst_caps_unref (templ);
342 } else {
343 res = templ;
344 }
345
346 if (filter) {
347 GstCaps *intersection;
348
349 intersection =
350 gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
351 gst_caps_unref (res);
352 res = intersection;
353 }
354
355 return res;
356 }
357
358 static const guint8 crc_table[256] = {
359 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
360 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
361 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
362 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
363 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
364 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
365 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
366 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
367 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
368 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
369 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
370 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
371 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
372 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
373 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
374 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
375 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
376 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
377 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
378 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
379 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
380 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
381 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
382 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
383 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
384 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
385 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
386 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
387 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
388 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
389 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
390 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
391 };
392
393 static guint8
gst_sbc_calculate_crc8(const guint8 * data,gint crc_bits)394 gst_sbc_calculate_crc8 (const guint8 * data, gint crc_bits)
395 {
396 guint8 crc = 0x0f;
397 guint8 octet;
398
399 while (crc_bits >= 8) {
400 crc = crc_table[crc ^ *data];
401 crc_bits -= 8;
402 ++data;
403 }
404
405 octet = *data;
406 while (crc_bits > 0) {
407 gchar bit = ((octet ^ crc) & 0x80) >> 7;
408
409 crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
410
411 octet = octet << 1;
412 --crc_bits;
413 }
414
415 return crc;
416 }
417
418 static gsize
gst_sbc_calc_framelen(guint subbands,GstSbcChannelMode ch_mode,guint blocks,guint bitpool)419 gst_sbc_calc_framelen (guint subbands, GstSbcChannelMode ch_mode,
420 guint blocks, guint bitpool)
421 {
422 switch (ch_mode) {
423 case GST_SBC_CHANNEL_MODE_MONO:
424 return 4 + (subbands * 1) / 2 + ((blocks * 1 * bitpool) + 7) / 8;
425 case GST_SBC_CHANNEL_MODE_DUAL:
426 return 4 + (subbands * 2) / 2 + ((blocks * 2 * bitpool) + 7) / 8;
427 case GST_SBC_CHANNEL_MODE_STEREO:
428 return 4 + (subbands * 2) / 2 + ((blocks * bitpool) + 7) / 8;
429 case GST_SBC_CHANNEL_MODE_JOINT_STEREO:
430 return 4 + (subbands * 2) / 2 + ((subbands + blocks * bitpool) + 7) / 8;
431 default:
432 break;
433 }
434
435 g_return_val_if_reached (0);
436 }
437
438 static gsize
gst_sbc_parse_header(const guint8 * data,guint * rate,guint * n_blocks,GstSbcChannelMode * ch_mode,GstSbcAllocationMethod * alloc_method,guint * n_subbands,guint * bitpool)439 gst_sbc_parse_header (const guint8 * data, guint * rate, guint * n_blocks,
440 GstSbcChannelMode * ch_mode, GstSbcAllocationMethod * alloc_method,
441 guint * n_subbands, guint * bitpool)
442 {
443 static const guint16 sbc_rates[4] = { 16000, 32000, 44100, 48000 };
444 static const guint8 sbc_blocks[4] = { 4, 8, 12, 16 };
445 guint8 crc_data[2 + 1 + 8], crc_bits, i;
446
447 GST_MEMDUMP ("header", data, 8);
448
449 if (data[0] != SBC_SYNCBYTE)
450 return 0;
451
452 *rate = sbc_rates[(data[1] >> 6) & 0x03];
453 *n_blocks = sbc_blocks[(data[1] >> 4) & 0x03];
454 *ch_mode = (GstSbcChannelMode) ((data[1] >> 2) & 0x03);
455 *alloc_method = (data[1] >> 1) & 0x01;
456 *n_subbands = (data[1] & 0x01) ? 8 : 4;
457 *bitpool = data[2];
458
459 GST_TRACE ("rate=%u, n_blocks=%u, ch_mode=%u, alloc_method=%u, "
460 "n_subbands=%u, bitpool=%u", *rate, *n_blocks, *ch_mode, *alloc_method,
461 *n_subbands, *bitpool);
462
463 if (*bitpool < 2)
464 return 0;
465
466 /* check CRC */
467 crc_data[0] = data[1];
468 crc_data[1] = data[2];
469 crc_bits = 16;
470
471 /* joint flags and RFA */
472 if (*ch_mode == GST_SBC_CHANNEL_MODE_JOINT_STEREO)
473 crc_bits += *n_subbands;
474
475 /* scale factors */
476 if (*ch_mode == GST_SBC_CHANNEL_MODE_MONO)
477 crc_bits += *n_subbands * 1 * 4;
478 else
479 crc_bits += *n_subbands * 2 * 4;
480
481 for (i = 16; i < crc_bits; i += 8) {
482 crc_data[i / 8] = data[1 + (i / 8) + 1];
483 }
484
485 if (i > crc_bits) {
486 crc_data[(i / 8) - 1] &= 0xF0;
487 }
488
489 GST_MEMDUMP ("crc bytes", crc_data, GST_ROUND_UP_8 (crc_bits) / 8);
490 if (gst_sbc_calculate_crc8 (crc_data, crc_bits) != data[3]) {
491 GST_LOG ("header CRC check failed, bits=%u, got 0x%02x, expected 0x%02x",
492 crc_bits, gst_sbc_calculate_crc8 (crc_data, crc_bits), data[3]);
493 return 0;
494 }
495
496 return gst_sbc_calc_framelen (*n_subbands, *ch_mode, *n_blocks, *bitpool);
497 }
498
499 static GstFlowReturn
gst_sbc_parse_pre_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)500 gst_sbc_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
501 {
502 GstSbcParse *sbcparse = GST_SBC_PARSE (parse);
503
504 if (!sbcparse->sent_codec_tag) {
505 GstTagList *taglist;
506 GstCaps *caps;
507
508 /* codec tag */
509 caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
510 if (G_UNLIKELY (caps == NULL)) {
511 if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
512 GST_INFO_OBJECT (parse, "Src pad is flushing");
513 return GST_FLOW_FLUSHING;
514 } else {
515 GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
516 return GST_FLOW_NOT_NEGOTIATED;
517 }
518 }
519
520 taglist = gst_tag_list_new_empty ();
521 gst_pb_utils_add_codec_description_to_tag_list (taglist,
522 GST_TAG_AUDIO_CODEC, caps);
523 gst_caps_unref (caps);
524
525 gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
526 gst_tag_list_unref (taglist);
527
528 /* also signals the end of first-frame processing */
529 sbcparse->sent_codec_tag = TRUE;
530 }
531
532 return GST_FLOW_OK;
533 }
534