Blame gst/audioparsers/gstsbcparse.c

Packit 1f69a5
/* GStreamer SBC audio parser
Packit 1f69a5
 * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk>
Packit 1f69a5
 *
Packit 1f69a5
 * This library is free software; you can redistribute it and/or
Packit 1f69a5
 * modify it under the terms of the GNU Library General Public
Packit 1f69a5
 * License as published by the Free Software Foundation; either
Packit 1f69a5
 * version 2 of the License, or (at your option) any later version.
Packit 1f69a5
 *
Packit 1f69a5
 * This library is distributed in the hope that it will be useful,
Packit 1f69a5
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1f69a5
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 1f69a5
 * Library General Public License for more details.
Packit 1f69a5
 *
Packit 1f69a5
 * You should have received a copy of the GNU Library General Public
Packit 1f69a5
 * License along with this library; if not, write to the
Packit 1f69a5
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 1f69a5
 * Boston, MA 02110-1301, USA.
Packit 1f69a5
 */
Packit 1f69a5
Packit 1f69a5
#ifdef HAVE_CONFIG_H
Packit 1f69a5
#include "config.h"
Packit 1f69a5
#endif
Packit 1f69a5
Packit 1f69a5
/**
Packit 1f69a5
 * SECTION:element-sbcparse
Packit 1f69a5
 * @see_also: sbcdec, sbcenc
Packit 1f69a5
 *
Packit 1f69a5
 * The sbcparse element will parse a bluetooth SBC audio stream into
Packit 1f69a5
 * frames and timestamp them properly.
Packit 1f69a5
 *
Packit 1f69a5
 * Since: 1.2.0
Packit 1f69a5
 */
Packit 1f69a5
Packit 1f69a5
#ifdef HAVE_CONFIG_H
Packit 1f69a5
#include "config.h"
Packit 1f69a5
#endif
Packit 1f69a5
Packit 1f69a5
#include "gstsbcparse.h"
Packit 1f69a5
Packit 1f69a5
#include <string.h>
Packit 1f69a5
#include <gst/tag/tag.h>
Packit 1f69a5
#include <gst/audio/audio.h>
Packit 1f69a5
#include <gst/base/base.h>
Packit 1f69a5
#include <gst/pbutils/pbutils.h>
Packit 1f69a5
Packit 1f69a5
#define SBC_SYNCBYTE 0x9C
Packit 1f69a5
Packit 1f69a5
GST_DEBUG_CATEGORY_STATIC (sbcparse_debug);
Packit 1f69a5
#define GST_CAT_DEFAULT sbcparse_debug
Packit 1f69a5
Packit 1f69a5
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
Packit 1f69a5
    GST_PAD_SRC,
Packit 1f69a5
    GST_PAD_ALWAYS,
Packit 1f69a5
    GST_STATIC_CAPS ("audio/x-sbc, parsed = (boolean) true, "
Packit 1f69a5
        "channels = (int) [ 1, 2 ], "
Packit 1f69a5
        "rate = (int) { 16000, 32000, 44100, 48000 }")
Packit 1f69a5
    );
Packit 1f69a5
Packit 1f69a5
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
Packit 1f69a5
    GST_PAD_SINK,
Packit 1f69a5
    GST_PAD_ALWAYS,
Packit 1f69a5
    GST_STATIC_CAPS ("audio/x-sbc")
Packit 1f69a5
    );
Packit 1f69a5
Packit 1f69a5
static gboolean gst_sbc_parse_start (GstBaseParse * parse);
Packit 1f69a5
static gboolean gst_sbc_parse_stop (GstBaseParse * parse);
Packit 1f69a5
static GstFlowReturn gst_sbc_parse_handle_frame (GstBaseParse * parse,
Packit 1f69a5
    GstBaseParseFrame * frame, gint * skipsize);
Packit 1f69a5
static GstFlowReturn gst_sbc_parse_pre_push_frame (GstBaseParse * parse,
Packit 1f69a5
    GstBaseParseFrame * frame);
Packit 1f69a5
static GstCaps *gst_sbc_parse_get_sink_caps (GstBaseParse * parse,
Packit 1f69a5
    GstCaps * filter);
Packit 1f69a5
Packit 1f69a5
static guint8 gst_sbc_calculate_crc8 (const guint8 * data, gint bits_crc);
Packit 1f69a5
static gsize gst_sbc_calc_framelen (guint subbands, GstSbcChannelMode ch_mode,
Packit 1f69a5
    guint blocks, guint bitpool);
Packit 1f69a5
static gsize gst_sbc_parse_header (const guint8 * data, guint * rate,
Packit 1f69a5
    guint * n_blocks, GstSbcChannelMode * ch_mode,
Packit 1f69a5
    GstSbcAllocationMethod * alloc_method, guint * n_subbands, guint * bitpool);
Packit 1f69a5
Packit 1f69a5
#define parent_class gst_sbc_parse_parent_class
Packit 1f69a5
G_DEFINE_TYPE (GstSbcParse, gst_sbc_parse, GST_TYPE_BASE_PARSE);
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
gst_sbc_parse_class_init (GstSbcParseClass * klass)
Packit 1f69a5
{
Packit 1f69a5
  GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass);
Packit 1f69a5
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
Packit 1f69a5
Packit 1f69a5
  GST_DEBUG_CATEGORY_INIT (sbcparse_debug, "sbcparse", 0, "SBC audio parser");
Packit 1f69a5
Packit 1f69a5
  baseparse_class->start = GST_DEBUG_FUNCPTR (gst_sbc_parse_start);
Packit 1f69a5
  baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_sbc_parse_stop);
Packit 1f69a5
  baseparse_class->pre_push_frame =
Packit 1f69a5
      GST_DEBUG_FUNCPTR (gst_sbc_parse_pre_push_frame);
Packit 1f69a5
  baseparse_class->handle_frame =
Packit 1f69a5
      GST_DEBUG_FUNCPTR (gst_sbc_parse_handle_frame);
Packit 1f69a5
  baseparse_class->get_sink_caps =
Packit 1f69a5
      GST_DEBUG_FUNCPTR (gst_sbc_parse_get_sink_caps);
Packit 1f69a5
Packit 1f69a5
  gst_element_class_add_static_pad_template (element_class, &src_factory);
Packit 1f69a5
  gst_element_class_add_static_pad_template (element_class, &sink_factory);
Packit 1f69a5
Packit 1f69a5
  gst_element_class_set_static_metadata (element_class, "SBC audio parser",
Packit 1f69a5
      "Codec/Parser/Audio", "Parses an SBC bluetooth audio stream",
Packit 1f69a5
      "Tim-Philipp Müller <tim.muller@collabora.co.uk>");
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
gst_sbc_parse_reset (GstSbcParse * sbcparse)
Packit 1f69a5
{
Packit 1f69a5
  sbcparse->alloc_method = GST_SBC_ALLOCATION_METHOD_INVALID;
Packit 1f69a5
  sbcparse->ch_mode = GST_SBC_CHANNEL_MODE_INVALID;
Packit 1f69a5
  sbcparse->rate = -1;
Packit 1f69a5
  sbcparse->n_blocks = -1;
Packit 1f69a5
  sbcparse->n_subbands = -1;
Packit 1f69a5
  sbcparse->bitpool = -1;
Packit 1f69a5
  sbcparse->sent_codec_tag = FALSE;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
gst_sbc_parse_init (GstSbcParse * sbcparse)
Packit 1f69a5
{
Packit 1f69a5
  gst_sbc_parse_reset (sbcparse);
Packit 1f69a5
  GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (sbcparse));
Packit 1f69a5
  GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (sbcparse));
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static gboolean
Packit 1f69a5
gst_sbc_parse_start (GstBaseParse * parse)
Packit 1f69a5
{
Packit 1f69a5
  gst_base_parse_set_min_frame_size (parse,
Packit 1f69a5
      gst_sbc_calc_framelen (4, GST_SBC_CHANNEL_MODE_MONO, 4, 2));
Packit 1f69a5
Packit 1f69a5
  gst_base_parse_set_has_timing_info (parse, FALSE);
Packit 1f69a5
Packit 1f69a5
  gst_base_parse_set_syncable (parse, TRUE);
Packit 1f69a5
Packit 1f69a5
  return TRUE;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static gboolean
Packit 1f69a5
gst_sbc_parse_stop (GstBaseParse * parse)
Packit 1f69a5
{
Packit 1f69a5
  gst_sbc_parse_reset (GST_SBC_PARSE (parse));
Packit 1f69a5
  return TRUE;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static const gchar *
Packit 1f69a5
gst_sbc_channel_mode_get_name (GstSbcChannelMode ch_mode)
Packit 1f69a5
{
Packit 1f69a5
  switch (ch_mode) {
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_MONO:
Packit 1f69a5
      return "mono";
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_DUAL:
Packit 1f69a5
      return "dual";
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_STEREO:
Packit 1f69a5
      return "stereo";
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_JOINT_STEREO:
Packit 1f69a5
      return "joint";
Packit 1f69a5
    default:
Packit 1f69a5
      break;
Packit 1f69a5
  }
Packit 1f69a5
  return "invalid";
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static const gchar *
Packit 1f69a5
gst_sbc_allocation_method_get_name (GstSbcAllocationMethod alloc_method)
Packit 1f69a5
{
Packit 1f69a5
  switch (alloc_method) {
Packit 1f69a5
    case GST_SBC_ALLOCATION_METHOD_SNR:
Packit 1f69a5
      return "snr";
Packit 1f69a5
    case GST_SBC_ALLOCATION_METHOD_LOUDNESS:
Packit 1f69a5
      return "loudness";
Packit 1f69a5
    default:
Packit 1f69a5
      break;
Packit 1f69a5
  }
Packit 1f69a5
  return "invalid";
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstFlowReturn
Packit 1f69a5
gst_sbc_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame,
Packit 1f69a5
    gint * skipsize)
Packit 1f69a5
{
Packit 1f69a5
  GstSbcParse *sbcparse = GST_SBC_PARSE (parse);
Packit 1f69a5
  GstSbcAllocationMethod alloc_method = GST_SBC_ALLOCATION_METHOD_INVALID;
Packit 1f69a5
  GstSbcChannelMode ch_mode = GST_SBC_CHANNEL_MODE_INVALID;
Packit 1f69a5
  GstMapInfo map;
Packit 1f69a5
  guint rate = 0, n_blocks = 0, n_subbands = 0, bitpool = 0;
Packit 1f69a5
  gsize frame_len, next_len;
Packit 1f69a5
  gint i, max_frames;
Packit 1f69a5
Packit 1f69a5
  gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
Packit 1f69a5
Packit 1f69a5
  g_assert (map.size >= 6);
Packit 1f69a5
Packit 1f69a5
  frame_len = gst_sbc_parse_header (map.data, &rate, &n_blocks, &ch_mode,
Packit 1f69a5
      &alloc_method, &n_subbands, &bitpool);
Packit 1f69a5
Packit 1f69a5
  GST_LOG_OBJECT (parse, "frame_len: %u", (guint) frame_len);
Packit 1f69a5
Packit 1f69a5
  if (frame_len == 0)
Packit 1f69a5
    goto resync;
Packit 1f69a5
Packit 1f69a5
  if (sbcparse->alloc_method != alloc_method
Packit 1f69a5
      || sbcparse->ch_mode != ch_mode
Packit 1f69a5
      || sbcparse->rate != rate
Packit 1f69a5
      || sbcparse->n_blocks != n_blocks
Packit 1f69a5
      || sbcparse->n_subbands != n_subbands || sbcparse->bitpool != bitpool) {
Packit 1f69a5
    guint avg_bitrate;
Packit 1f69a5
    GstCaps *caps;
Packit 1f69a5
Packit 1f69a5
    /* FIXME: do all of these need to be in the caps? */
Packit 1f69a5
    caps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT, rate,
Packit 1f69a5
        "channels", G_TYPE_INT, (ch_mode == GST_SBC_CHANNEL_MODE_MONO) ? 1 : 2,
Packit 1f69a5
        "channel-mode", G_TYPE_STRING, gst_sbc_channel_mode_get_name (ch_mode),
Packit 1f69a5
        "blocks", G_TYPE_INT, n_blocks, "subbands", G_TYPE_INT, n_subbands,
Packit 1f69a5
        "allocation-method", G_TYPE_STRING,
Packit 1f69a5
        gst_sbc_allocation_method_get_name (alloc_method),
Packit 1f69a5
        "bitpool", G_TYPE_INT, bitpool, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
Packit 1f69a5
Packit 1f69a5
    GST_INFO_OBJECT (sbcparse, "caps changed to %" GST_PTR_FORMAT, caps);
Packit 1f69a5
Packit 1f69a5
    gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (sbcparse),
Packit 1f69a5
        gst_event_new_caps (caps));
Packit 1f69a5
Packit 1f69a5
    avg_bitrate = (8 * frame_len * rate) / (n_subbands * n_blocks);
Packit 1f69a5
    gst_base_parse_set_average_bitrate (parse, avg_bitrate);
Packit 1f69a5
Packit 1f69a5
    gst_base_parse_set_frame_rate (parse, rate, n_subbands * n_blocks, 0, 0);
Packit 1f69a5
Packit 1f69a5
    sbcparse->alloc_method = alloc_method;
Packit 1f69a5
    sbcparse->ch_mode = ch_mode;
Packit 1f69a5
    sbcparse->rate = rate;
Packit 1f69a5
    sbcparse->n_blocks = n_blocks;
Packit 1f69a5
    sbcparse->n_subbands = n_subbands;
Packit 1f69a5
    sbcparse->bitpool = bitpool;
Packit 1f69a5
Packit 1f69a5
    gst_caps_unref (caps);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  if (frame_len > map.size)
Packit 1f69a5
    goto need_more_data;
Packit 1f69a5
Packit 1f69a5
  GST_BUFFER_OFFSET (frame->buffer) = GST_BUFFER_OFFSET_NONE;
Packit 1f69a5
  GST_BUFFER_OFFSET_END (frame->buffer) = GST_BUFFER_OFFSET_NONE;
Packit 1f69a5
Packit 1f69a5
  /* completely arbitrary limit, we only process data we already have,
Packit 1f69a5
   * so we aren't introducing latency here */
Packit 1f69a5
  max_frames = MIN (map.size / frame_len, n_blocks * n_subbands * 5);
Packit 1f69a5
  GST_LOG_OBJECT (sbcparse, "parsing up to %d frames", max_frames);
Packit 1f69a5
Packit 1f69a5
  for (i = 1; i < max_frames; ++i) {
Packit 1f69a5
    next_len = gst_sbc_parse_header (map.data + (i * frame_len), &rate,
Packit 1f69a5
        &n_blocks, &ch_mode, &alloc_method, &n_subbands, &bitpool);
Packit 1f69a5
Packit 1f69a5
    if (next_len != frame_len || sbcparse->alloc_method != alloc_method ||
Packit 1f69a5
        sbcparse->ch_mode != ch_mode || sbcparse->rate != rate ||
Packit 1f69a5
        sbcparse->n_blocks != n_blocks || sbcparse->n_subbands != n_subbands ||
Packit 1f69a5
        sbcparse->bitpool != bitpool) {
Packit 1f69a5
      break;
Packit 1f69a5
    }
Packit 1f69a5
  }
Packit 1f69a5
  GST_LOG_OBJECT (sbcparse, "packing %d SBC frames into next output buffer", i);
Packit 1f69a5
Packit 1f69a5
  /* Note: local n_subbands and n_blocks variables might be tainted if we
Packit 1f69a5
   * bailed out of the loop above because of a header configuration mismatch */
Packit 1f69a5
  gst_base_parse_set_frame_rate (parse, rate,
Packit 1f69a5
      sbcparse->n_subbands * sbcparse->n_blocks * i, 0, 0);
Packit 1f69a5
Packit 1f69a5
  gst_buffer_unmap (frame->buffer, &map);
Packit 1f69a5
  return gst_base_parse_finish_frame (parse, frame, i * frame_len);
Packit 1f69a5
Packit 1f69a5
resync:
Packit 1f69a5
  {
Packit 1f69a5
    const guint8 *possible_sync;
Packit 1f69a5
Packit 1f69a5
    GST_DEBUG_OBJECT (parse, "no sync, resyncing");
Packit 1f69a5
Packit 1f69a5
    possible_sync = memchr (map.data, SBC_SYNCBYTE, map.size);
Packit 1f69a5
Packit 1f69a5
    if (possible_sync != NULL)
Packit 1f69a5
      *skipsize = (gint) (possible_sync - map.data);
Packit 1f69a5
    else
Packit 1f69a5
      *skipsize = map.size;
Packit 1f69a5
Packit 1f69a5
    gst_buffer_unmap (frame->buffer, &map);
Packit 1f69a5
Packit 1f69a5
    /* we could optimise things here by looping over the data and checking
Packit 1f69a5
     * whether the sync is good or not instead of handing control back to
Packit 1f69a5
     * the base class just to be called again */
Packit 1f69a5
    return GST_FLOW_OK;
Packit 1f69a5
  }
Packit 1f69a5
need_more_data:
Packit 1f69a5
  {
Packit 1f69a5
    GST_LOG_OBJECT (parse,
Packit 1f69a5
        "need %" G_GSIZE_FORMAT " bytes, but only have %" G_GSIZE_FORMAT,
Packit 1f69a5
        frame_len, map.size);
Packit 1f69a5
    gst_base_parse_set_min_frame_size (parse, frame_len);
Packit 1f69a5
    gst_buffer_unmap (frame->buffer, &map);
Packit 1f69a5
    return GST_FLOW_OK;
Packit 1f69a5
  }
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
remove_fields (GstCaps * caps)
Packit 1f69a5
{
Packit 1f69a5
  guint i, n;
Packit 1f69a5
Packit 1f69a5
  n = gst_caps_get_size (caps);
Packit 1f69a5
  for (i = 0; i < n; i++) {
Packit 1f69a5
    GstStructure *s = gst_caps_get_structure (caps, i);
Packit 1f69a5
Packit 1f69a5
    gst_structure_remove_field (s, "parsed");
Packit 1f69a5
  }
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstCaps *
Packit 1f69a5
gst_sbc_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
Packit 1f69a5
{
Packit 1f69a5
  GstCaps *peercaps, *templ;
Packit 1f69a5
  GstCaps *res;
Packit 1f69a5
Packit 1f69a5
  templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
Packit 1f69a5
  if (filter) {
Packit 1f69a5
    GstCaps *fcopy = gst_caps_copy (filter);
Packit 1f69a5
    /* Remove the fields we convert */
Packit 1f69a5
    remove_fields (fcopy);
Packit 1f69a5
    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
Packit 1f69a5
    gst_caps_unref (fcopy);
Packit 1f69a5
  } else
Packit 1f69a5
    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
Packit 1f69a5
Packit 1f69a5
  if (peercaps) {
Packit 1f69a5
    /* Remove the parsed field */
Packit 1f69a5
    peercaps = gst_caps_make_writable (peercaps);
Packit 1f69a5
    remove_fields (peercaps);
Packit 1f69a5
Packit 1f69a5
    res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
Packit 1f69a5
    gst_caps_unref (peercaps);
Packit 1f69a5
    gst_caps_unref (templ);
Packit 1f69a5
  } else {
Packit 1f69a5
    res = templ;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  if (filter) {
Packit 1f69a5
    GstCaps *intersection;
Packit 1f69a5
Packit 1f69a5
    intersection =
Packit 1f69a5
        gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
Packit 1f69a5
    gst_caps_unref (res);
Packit 1f69a5
    res = intersection;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  return res;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static const guint8 crc_table[256] = {
Packit 1f69a5
  0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
Packit 1f69a5
  0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
Packit 1f69a5
  0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
Packit 1f69a5
  0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
Packit 1f69a5
  0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
Packit 1f69a5
  0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
Packit 1f69a5
  0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
Packit 1f69a5
  0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
Packit 1f69a5
  0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
Packit 1f69a5
  0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
Packit 1f69a5
  0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
Packit 1f69a5
  0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
Packit 1f69a5
  0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
Packit 1f69a5
  0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
Packit 1f69a5
  0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
Packit 1f69a5
  0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
Packit 1f69a5
  0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
Packit 1f69a5
  0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
Packit 1f69a5
  0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
Packit 1f69a5
  0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
Packit 1f69a5
  0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
Packit 1f69a5
  0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
Packit 1f69a5
  0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
Packit 1f69a5
  0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
Packit 1f69a5
  0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
Packit 1f69a5
  0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
Packit 1f69a5
  0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
Packit 1f69a5
  0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
Packit 1f69a5
  0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
Packit 1f69a5
  0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
Packit 1f69a5
  0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
Packit 1f69a5
  0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
Packit 1f69a5
};
Packit 1f69a5
Packit 1f69a5
static guint8
Packit 1f69a5
gst_sbc_calculate_crc8 (const guint8 * data, gint crc_bits)
Packit 1f69a5
{
Packit 1f69a5
  guint8 crc = 0x0f;
Packit 1f69a5
  guint8 octet;
Packit 1f69a5
Packit 1f69a5
  while (crc_bits >= 8) {
Packit 1f69a5
    crc = crc_table[crc ^ *data];
Packit 1f69a5
    crc_bits -= 8;
Packit 1f69a5
    ++data;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  octet = *data;
Packit 1f69a5
  while (crc_bits > 0) {
Packit 1f69a5
    gchar bit = ((octet ^ crc) & 0x80) >> 7;
Packit 1f69a5
Packit 1f69a5
    crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
Packit 1f69a5
Packit 1f69a5
    octet = octet << 1;
Packit 1f69a5
    --crc_bits;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  return crc;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static gsize
Packit 1f69a5
gst_sbc_calc_framelen (guint subbands, GstSbcChannelMode ch_mode,
Packit 1f69a5
    guint blocks, guint bitpool)
Packit 1f69a5
{
Packit 1f69a5
  switch (ch_mode) {
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_MONO:
Packit 1f69a5
      return 4 + (subbands * 1) / 2 + ((blocks * 1 * bitpool) + 7) / 8;
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_DUAL:
Packit 1f69a5
      return 4 + (subbands * 2) / 2 + ((blocks * 2 * bitpool) + 7) / 8;
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_STEREO:
Packit 1f69a5
      return 4 + (subbands * 2) / 2 + ((blocks * bitpool) + 7) / 8;
Packit 1f69a5
    case GST_SBC_CHANNEL_MODE_JOINT_STEREO:
Packit 1f69a5
      return 4 + (subbands * 2) / 2 + ((subbands + blocks * bitpool) + 7) / 8;
Packit 1f69a5
    default:
Packit 1f69a5
      break;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  g_return_val_if_reached (0);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static gsize
Packit 1f69a5
gst_sbc_parse_header (const guint8 * data, guint * rate, guint * n_blocks,
Packit 1f69a5
    GstSbcChannelMode * ch_mode, GstSbcAllocationMethod * alloc_method,
Packit 1f69a5
    guint * n_subbands, guint * bitpool)
Packit 1f69a5
{
Packit 1f69a5
  static const guint16 sbc_rates[4] = { 16000, 32000, 44100, 48000 };
Packit 1f69a5
  static const guint8 sbc_blocks[4] = { 4, 8, 12, 16 };
Packit 1f69a5
  guint8 crc_data[2 + 1 + 8], crc_bits, i;
Packit 1f69a5
Packit 1f69a5
  GST_MEMDUMP ("header", data, 8);
Packit 1f69a5
Packit 1f69a5
  if (data[0] != SBC_SYNCBYTE)
Packit 1f69a5
    return 0;
Packit 1f69a5
Packit 1f69a5
  *rate = sbc_rates[(data[1] >> 6) & 0x03];
Packit 1f69a5
  *n_blocks = sbc_blocks[(data[1] >> 4) & 0x03];
Packit 1f69a5
  *ch_mode = (GstSbcChannelMode) ((data[1] >> 2) & 0x03);
Packit 1f69a5
  *alloc_method = (data[1] >> 1) & 0x01;
Packit 1f69a5
  *n_subbands = (data[1] & 0x01) ? 8 : 4;
Packit 1f69a5
  *bitpool = data[2];
Packit 1f69a5
Packit 1f69a5
  GST_TRACE ("rate=%u, n_blocks=%u, ch_mode=%u, alloc_method=%u, "
Packit 1f69a5
      "n_subbands=%u, bitpool=%u", *rate, *n_blocks, *ch_mode, *alloc_method,
Packit 1f69a5
      *n_subbands, *bitpool);
Packit 1f69a5
Packit 1f69a5
  if (*bitpool < 2)
Packit 1f69a5
    return 0;
Packit 1f69a5
Packit 1f69a5
  /* check CRC */
Packit 1f69a5
  crc_data[0] = data[1];
Packit 1f69a5
  crc_data[1] = data[2];
Packit 1f69a5
  crc_bits = 16;
Packit 1f69a5
Packit 1f69a5
  /* joint flags and RFA */
Packit 1f69a5
  if (*ch_mode == GST_SBC_CHANNEL_MODE_JOINT_STEREO)
Packit 1f69a5
    crc_bits += *n_subbands;
Packit 1f69a5
Packit 1f69a5
  /* scale factors */
Packit 1f69a5
  if (*ch_mode == GST_SBC_CHANNEL_MODE_MONO)
Packit 1f69a5
    crc_bits += *n_subbands * 1 * 4;
Packit 1f69a5
  else
Packit 1f69a5
    crc_bits += *n_subbands * 2 * 4;
Packit 1f69a5
Packit 1f69a5
  for (i = 16; i < crc_bits; i += 8) {
Packit 1f69a5
    crc_data[i / 8] = data[1 + (i / 8) + 1];
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  if (i > crc_bits) {
Packit 1f69a5
    crc_data[(i / 8) - 1] &= 0xF0;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  GST_MEMDUMP ("crc bytes", crc_data, GST_ROUND_UP_8 (crc_bits) / 8);
Packit 1f69a5
  if (gst_sbc_calculate_crc8 (crc_data, crc_bits) != data[3]) {
Packit 1f69a5
    GST_LOG ("header CRC check failed, bits=%u, got 0x%02x, expected 0x%02x",
Packit 1f69a5
        crc_bits, gst_sbc_calculate_crc8 (crc_data, crc_bits), data[3]);
Packit 1f69a5
    return 0;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  return gst_sbc_calc_framelen (*n_subbands, *ch_mode, *n_blocks, *bitpool);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstFlowReturn
Packit 1f69a5
gst_sbc_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
Packit 1f69a5
{
Packit 1f69a5
  GstSbcParse *sbcparse = GST_SBC_PARSE (parse);
Packit 1f69a5
Packit 1f69a5
  if (!sbcparse->sent_codec_tag) {
Packit 1f69a5
    GstTagList *taglist;
Packit 1f69a5
    GstCaps *caps;
Packit 1f69a5
Packit 1f69a5
    /* codec tag */
Packit 1f69a5
    caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
Packit 1f69a5
    if (G_UNLIKELY (caps == NULL)) {
Packit 1f69a5
      if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
Packit 1f69a5
        GST_INFO_OBJECT (parse, "Src pad is flushing");
Packit 1f69a5
        return GST_FLOW_FLUSHING;
Packit 1f69a5
      } else {
Packit 1f69a5
        GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
Packit 1f69a5
        return GST_FLOW_NOT_NEGOTIATED;
Packit 1f69a5
      }
Packit 1f69a5
    }
Packit 1f69a5
Packit 1f69a5
    taglist = gst_tag_list_new_empty ();
Packit 1f69a5
    gst_pb_utils_add_codec_description_to_tag_list (taglist,
Packit 1f69a5
        GST_TAG_AUDIO_CODEC, caps);
Packit 1f69a5
    gst_caps_unref (caps);
Packit 1f69a5
Packit 1f69a5
    gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
Packit 1f69a5
    gst_tag_list_unref (taglist);
Packit 1f69a5
Packit 1f69a5
    /* also signals the end of first-frame processing */
Packit 1f69a5
    sbcparse->sent_codec_tag = TRUE;
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  return GST_FLOW_OK;
Packit 1f69a5
}