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