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