gstflacdec.c 24.9 KB
Newer Older
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) <2006,2011> Tim-Philipp Müller <tim centricular net>
4
 * Copyright (C) <2006> Jan Schmidt <thaytan at mad scientist com>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18 19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
20 21
 */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
22 23
/**
 * SECTION:element-flacdec
24
 * @see_also: #GstFlacEnc
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
25 26 27 28
 *
 * flacdec decodes FLAC streams.
 * <ulink url="http://flac.sourceforge.net/">FLAC</ulink>
 * is a Free Lossless Audio Codec.
29 30
 *
 * <refsect2>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
31
 * <title>Example launch line</title>
32
 * |[
33
 * gst-launch-1.0 filesrc location=media/small/dark.441-16-s.flac ! flacparse ! flacdec ! audioconvert ! audioresample ! autoaudiosink
34 35
 * ]|
 * |[
36
 * gst-launch-1.0 souphttpsrc location=http://gstreamer.freedesktop.org/media/small/dark.441-16-s.flac ! flacparse ! flacdec ! audioconvert ! audioresample ! queue min-threshold-buffers=10 ! autoaudiosink
37
 * ]|
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
38 39 40
 * </refsect2>
 */

41 42 43
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
44

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
45 46 47
#include <string.h>

#include "gstflacdec.h"
48
#include <gst/gst-i18n-plugin.h>
49
#include <gst/tag/tag.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50

51 52
/* Taken from http://flac.sourceforge.net/format.html#frame_header */
static const GstAudioChannelPosition channel_positions[8][8] = {
53
  {GST_AUDIO_CHANNEL_POSITION_MONO},
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
      GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
      GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
71
        GST_AUDIO_CHANNEL_POSITION_LFE1,
72 73 74 75 76 77 78 79 80
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
  /* FIXME: 7/8 channel layouts are not defined in the FLAC specs */
  {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
        GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
81
        GST_AUDIO_CHANNEL_POSITION_LFE1,
82 83 84 85 86 87
      GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
        GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
88
        GST_AUDIO_CHANNEL_POSITION_LFE1,
89 90 91 92
        GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
      GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
};

93 94 95
GST_DEBUG_CATEGORY_STATIC (flacdec_debug);
#define GST_CAT_DEFAULT flacdec_debug

96 97 98 99 100 101 102
static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
    FLAC__byte buffer[], size_t * bytes, void *client_data);
static FLAC__StreamDecoderWriteStatus
gst_flac_dec_write_stream (const FLAC__StreamDecoder * decoder,
    const FLAC__Frame * frame,
    const FLAC__int32 * const buffer[], void *client_data);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
103
static void gst_flac_dec_metadata_cb (const FLAC__StreamDecoder *
104
    decoder, const FLAC__StreamMetadata * metadata, void *client_data);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
105
static void gst_flac_dec_error_cb (const FLAC__StreamDecoder *
106
    decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107

108 109 110 111 112 113 114 115
static void gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard);
static gboolean gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps);
static gboolean gst_flac_dec_start (GstAudioDecoder * dec);
static gboolean gst_flac_dec_stop (GstAudioDecoder * dec);
static GstFlowReturn gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec,
    GstBuffer * buf);

G_DEFINE_TYPE (GstFlacDec, gst_flac_dec, GST_TYPE_AUDIO_DECODER);
116

Wim Taymans's avatar
Wim Taymans committed
117
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
118
#define FORMATS "{ S8, S16LE, S24_32LE, S32LE } "
Wim Taymans's avatar
Wim Taymans committed
119
#else
120
#define FORMATS "{ S8, S16BE, S24_32BE, S32BE } "
Wim Taymans's avatar
Wim Taymans committed
121 122
#endif

123
#define GST_FLAC_DEC_SRC_CAPS                             \
Wim Taymans's avatar
Wim Taymans committed
124 125
    "audio/x-raw, "                                       \
    "format = (string) " FORMATS ", "                     \
126
    "layout = (string) interleaved, "                     \
127
    "rate = (int) [ 1, 655350 ], "                        \
128
    "channels = (int) [ 1, 8 ]"
129

130 131 132 133 134 135
#define GST_FLAC_DEC_SINK_CAPS                            \
    "audio/x-flac, "                                      \
    "framed = (boolean) true, "                           \
    "rate = (int) [ 1, 655350 ], "                        \
    "channels = (int) [ 1, 8 ]"

136 137 138 139 140 141 142 143 144
static GstStaticPadTemplate flac_dec_src_factory =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_FLAC_DEC_SRC_CAPS));
static GstStaticPadTemplate flac_dec_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
145
    GST_STATIC_CAPS (GST_FLAC_DEC_SINK_CAPS));
146

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
static void
148
gst_flac_dec_class_init (GstFlacDecClass * klass)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
{
150
  GstAudioDecoderClass *audiodecoder_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
151 152
  GstElementClass *gstelement_class;

153
  audiodecoder_class = (GstAudioDecoderClass *) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
154
  gstelement_class = (GstElementClass *) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155

Wim Taymans's avatar
Wim Taymans committed
156 157
  GST_DEBUG_CATEGORY_INIT (flacdec_debug, "flacdec", 0, "flac decoder");

158 159 160 161 162 163
  audiodecoder_class->stop = GST_DEBUG_FUNCPTR (gst_flac_dec_stop);
  audiodecoder_class->start = GST_DEBUG_FUNCPTR (gst_flac_dec_start);
  audiodecoder_class->flush = GST_DEBUG_FUNCPTR (gst_flac_dec_flush);
  audiodecoder_class->set_format = GST_DEBUG_FUNCPTR (gst_flac_dec_set_format);
  audiodecoder_class->handle_frame =
      GST_DEBUG_FUNCPTR (gst_flac_dec_handle_frame);
Wim Taymans's avatar
Wim Taymans committed
164 165 166 167 168 169

  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&flac_dec_src_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&flac_dec_sink_factory));

170
  gst_element_class_set_static_metadata (gstelement_class, "FLAC audio decoder",
171 172 173
      "Codec/Decoder/Audio", "Decodes FLAC lossless audio streams",
      "Tim-Philipp Müller <tim@centricular.net>, "
      "Wim Taymans <wim.taymans@gmail.com>");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
174 175
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176
static void
Wim Taymans's avatar
Wim Taymans committed
177
gst_flac_dec_init (GstFlacDec * flacdec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
{
179
  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (flacdec), TRUE);
180 181
}

182 183
static gboolean
gst_flac_dec_start (GstAudioDecoder * audio_dec)
184
{
185 186
  FLAC__StreamDecoderInitStatus s;
  GstFlacDec *dec;
187

188
  dec = GST_FLAC_DEC (audio_dec);
189 190 191

  dec->adapter = gst_adapter_new ();

192
  dec->decoder = FLAC__stream_decoder_new ();
193

194 195 196
  gst_audio_info_init (&dec->info);
  dec->depth = 0;

197
  /* no point calculating MD5 since it's never checked here */
198
  FLAC__stream_decoder_set_md5_checking (dec->decoder, false);
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

  GST_DEBUG_OBJECT (dec, "initializing decoder");
  s = FLAC__stream_decoder_init_stream (dec->decoder,
      gst_flac_dec_read_stream, NULL, NULL, NULL, NULL,
      gst_flac_dec_write_stream, gst_flac_dec_metadata_cb,
      gst_flac_dec_error_cb, dec);

  if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), LIBRARY, INIT, (NULL), (NULL));
    return FALSE;
  }

  dec->got_headers = FALSE;

  return TRUE;
214
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
215

216 217
static gboolean
gst_flac_dec_stop (GstAudioDecoder * dec)
Colin Walters's avatar
Colin Walters committed
218
{
219 220 221 222 223 224
  GstFlacDec *flacdec = GST_FLAC_DEC (dec);

  if (flacdec->decoder) {
    FLAC__stream_decoder_delete (flacdec->decoder);
    flacdec->decoder = NULL;
  }
Colin Walters's avatar
Colin Walters committed
225

226 227 228 229 230
  if (flacdec->adapter) {
    gst_adapter_clear (flacdec->adapter);
    g_object_unref (flacdec->adapter);
    flacdec->adapter = NULL;
  }
Colin Walters's avatar
Colin Walters committed
231

232
  return TRUE;
Colin Walters's avatar
Colin Walters committed
233 234
}

235 236 237
static gboolean
gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
238 239 240 241 242 243 244
  const GValue *headers;
  GstFlacDec *flacdec;
  GstStructure *s;
  guint i, num;

  flacdec = GST_FLAC_DEC (dec);

245
  GST_LOG_OBJECT (dec, "sink caps: %" GST_PTR_FORMAT, caps);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

  s = gst_caps_get_structure (caps, 0);
  headers = gst_structure_get_value (s, "streamheader");
  if (headers == NULL || !GST_VALUE_HOLDS_ARRAY (headers)) {
    GST_WARNING_OBJECT (dec, "no 'streamheader' field in input caps, try "
        "adding a flacparse element upstream");
    return FALSE;
  }

  if (gst_adapter_available (flacdec->adapter) > 0) {
    GST_WARNING_OBJECT (dec, "unexpected data left in adapter");
    gst_adapter_clear (flacdec->adapter);
  }

  num = gst_value_array_get_size (headers);
  for (i = 0; i < num; ++i) {
    const GValue *header_val;
    GstBuffer *header_buf;

    header_val = gst_value_array_get_value (headers, i);
    if (header_val == NULL || !GST_VALUE_HOLDS_BUFFER (header_val))
      return FALSE;

    header_buf = g_value_dup_boxed (header_val);
270 271
    GST_INFO_OBJECT (dec, "pushing header buffer of %" G_GSIZE_FORMAT " bytes "
        "into adapter", gst_buffer_get_size (header_buf));
272 273 274 275 276 277 278 279
    gst_adapter_push (flacdec->adapter, header_buf);
  }

  GST_DEBUG_OBJECT (dec, "Processing headers and metadata");
  if (!FLAC__stream_decoder_process_until_end_of_metadata (flacdec->decoder)) {
    GST_WARNING_OBJECT (dec, "process_until_end_of_metadata failed");
  }
  GST_INFO_OBJECT (dec, "headers and metadata are now processed");
280 281
  return TRUE;
}
Colin Walters's avatar
Colin Walters committed
282

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */
static const guint8 crc8_table[256] = {
  0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
  0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
  0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
  0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
  0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
  0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
  0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
  0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
  0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
  0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
  0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
  0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
  0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
  0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
  0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
  0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
  0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
  0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
  0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
  0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
  0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
  0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
  0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
  0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
  0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
  0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
  0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
  0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
  0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
  0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
  0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
  0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};

static guint8
gst_flac_calculate_crc8 (guint8 * data, guint length)
{
  guint8 crc = 0;

  while (length--) {
    crc = crc8_table[crc ^ *data];
    ++data;
  }

  return crc;
}

332 333
/* FIXME: for our purposes it's probably enough to just check for the sync
 * marker - we just want to know if it's a header frame or not */
334
static gboolean
335
gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, guint8 * data, guint size)
336 337 338 339 340 341
{
  guint headerlen;
  guint sr_from_end = 0;        /* can be 0, 8 or 16 */
  guint bs_from_end = 0;        /* can be 0, 8 or 16 */
  guint32 val = 0;
  guint8 bs, sr, ca, ss, pb;
342
  gboolean vbs;
343 344 345 346 347

  if (size < 10)
    return FALSE;

  /* sync */
348
  if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8)
349 350
    return FALSE;

351
  vbs = ! !(data[1] & 1);       /* variable blocksize */
352
  bs = (data[2] & 0xF0) >> 4;   /* blocksize marker   */
353
  sr = (data[2] & 0x0F);        /* samplerate marker  */
354
  ca = (data[3] & 0xF0) >> 4;   /* channel assignment */
355 356 357
  ss = (data[3] & 0x0F) >> 1;   /* sample size marker */
  pb = (data[3] & 0x01);        /* padding bit        */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
358
  GST_LOG_OBJECT (flacdec,
359 360
      "got sync, vbs=%d,bs=%x,sr=%x,ca=%x,ss=%x,pb=%x", vbs, bs, sr, ca, ss,
      pb);
361

362
  if (bs == 0 || sr == 0x0F || ca >= 0x0B || ss == 0x03 || ss == 0x07) {
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
    return FALSE;
  }

  /* read block size from end of header? */
  if (bs == 6)
    bs_from_end = 8;
  else if (bs == 7)
    bs_from_end = 16;

  /* read sample rate from end of header? */
  if (sr == 0x0C)
    sr_from_end = 8;
  else if (sr == 0x0D || sr == 0x0E)
    sr_from_end = 16;

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  val = data[4];
  /* This is slightly faster than a loop */
  if (!(val & 0x80)) {
    val = 0;
  } else if ((val & 0xc0) && !(val & 0x20)) {
    val = 1;
  } else if ((val & 0xe0) && !(val & 0x10)) {
    val = 2;
  } else if ((val & 0xf0) && !(val & 0x08)) {
    val = 3;
  } else if ((val & 0xf8) && !(val & 0x04)) {
    val = 4;
  } else if ((val & 0xfc) && !(val & 0x02)) {
    val = 5;
  } else if ((val & 0xfe) && !(val & 0x01)) {
    val = 6;
  } else {
    GST_LOG_OBJECT (flacdec, "failed to read sample/frame");
    return FALSE;
  }

  val++;
  headerlen = 4 + val + (bs_from_end / 8) + (sr_from_end / 8);

  if (gst_flac_calculate_crc8 (data, headerlen) != data[headerlen]) {
    GST_LOG_OBJECT (flacdec, "invalid checksum");
    return FALSE;
  }

407 408 409
  return TRUE;
}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
static gboolean
gst_flac_dec_handle_decoder_error (GstFlacDec * dec, gboolean msg)
{
  gboolean ret;

  dec->error_count++;
  if (dec->error_count > 10) {
    if (msg)
      GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), (NULL));
    dec->last_flow = GST_FLOW_ERROR;
    ret = TRUE;
  } else {
    GST_DEBUG_OBJECT (dec, "ignoring error for now at count %d",
        dec->error_count);
    ret = FALSE;
  }

  return ret;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
431 432
gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder,
    const FLAC__StreamMetadata * metadata, void *client_data)
433
{
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
434 435 436 437
  GstFlacDec *flacdec = GST_FLAC_DEC (client_data);

  GST_LOG_OBJECT (flacdec, "metadata type: %d", metadata->type);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438
  switch (metadata->type) {
439 440
    case FLAC__METADATA_TYPE_STREAMINFO:{
      gint64 samples;
441
      guint depth, width, gdepth;
442 443 444 445 446

      samples = metadata->data.stream_info.total_samples;

      flacdec->min_blocksize = metadata->data.stream_info.min_blocksize;
      flacdec->max_blocksize = metadata->data.stream_info.max_blocksize;
447 448
      flacdec->depth = depth = metadata->data.stream_info.bits_per_sample;

449 450 451 452 453 454
      if (depth < 9) {
        gdepth = width = 8;
      } else if (depth < 17) {
        gdepth = width = 16;
      } else if (depth < 25) {
        gdepth = 24;
455
        width = 32;
456 457 458
      } else {
        gdepth = width = 32;
      }
459 460

      gst_audio_info_set_format (&flacdec->info,
461
          gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
462 463 464 465 466 467 468 469 470 471 472 473
          metadata->data.stream_info.sample_rate,
          metadata->data.stream_info.channels, NULL);

      memcpy (flacdec->info.position,
          channel_positions[flacdec->info.channels - 1],
          sizeof (GstAudioChannelPosition) * flacdec->info.channels);
      gst_audio_channel_positions_to_valid_order (flacdec->info.position,
          flacdec->info.channels);
      /* Note: we create the inverse reordering map here */
      gst_audio_get_channel_reorder_map (flacdec->info.channels,
          flacdec->info.position, channel_positions[flacdec->info.channels - 1],
          flacdec->channel_reorder_map);
474 475 476

      GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
          flacdec->min_blocksize, flacdec->max_blocksize);
477
      GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u",
478
          flacdec->info.rate, flacdec->info.channels);
479
      GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth,
480
          flacdec->info.finfo->width);
481 482

      GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
      break;
484
    }
485
    default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
486
      break;
487
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488 489
}

490
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
491 492
gst_flac_dec_error_cb (const FLAC__StreamDecoder * d,
    FLAC__StreamDecoderErrorStatus status, void *client_data)
493 494
{
  const gchar *error;
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
495 496 497
  GstFlacDec *dec;

  dec = GST_FLAC_DEC (client_data);
498 499

  switch (status) {
500
    case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
501 502
      /* Ignore this error and keep processing */
      return;
503
    case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
504 505
      error = "bad header";
      break;
506
    case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
507 508 509
      error = "CRC mismatch";
      break;
    default:
Johan Dahlin's avatar
Johan Dahlin committed
510
      error = "unknown error";
511 512 513
      break;
  }

514 515
  if (gst_flac_dec_handle_decoder_error (dec, FALSE))
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s (%d)", error, status));
516 517
}

518 519 520
static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
    FLAC__byte buffer[], size_t * bytes, void *client_data)
521 522 523 524 525 526 527 528 529 530 531
{
  GstFlacDec *dec = GST_FLAC_DEC (client_data);
  guint len;

  len = MIN (gst_adapter_available (dec->adapter), *bytes);

  if (len == 0) {
    GST_LOG_OBJECT (dec, "0 bytes available at the moment");
    return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
  }

532 533
  GST_LOG_OBJECT (dec, "feeding %u bytes to decoder "
      "(available=%" G_GSIZE_FORMAT ", bytes=%u)",
534
      len, gst_adapter_available (dec->adapter), (guint) * bytes);
535
  gst_adapter_copy (dec->adapter, buffer, 0, len);
536 537 538 539 540 541 542
  *bytes = len;

  gst_adapter_flush (dec->adapter, len);

  return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
543
static FLAC__StreamDecoderWriteStatus
544 545
gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
    const FLAC__int32 * const buffer[])
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
546
{
547
  GstFlowReturn ret = GST_FLOW_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
548 549
  GstBuffer *outbuf;
  guint depth = frame->header.bits_per_sample;
550
  guint width, gdepth;
551
  guint sample_rate = frame->header.sample_rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
552 553 554
  guint channels = frame->header.channels;
  guint samples = frame->header.blocksize;
  guint j, i;
Wim Taymans's avatar
Wim Taymans committed
555
  GstMapInfo map;
556
  gboolean caps_changed;
557

558 559
  GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);

Wim Taymans's avatar
Wim Taymans committed
560 561 562 563 564 565 566 567 568 569 570
  if (depth == 0) {
    if (flacdec->depth < 4 || flacdec->depth > 32) {
      GST_ERROR_OBJECT (flacdec, "unsupported depth %d from STREAMINFO",
          flacdec->depth);
      ret = GST_FLOW_ERROR;
      goto done;
    }

    depth = flacdec->depth;
  }

571 572
  switch (depth) {
    case 8:
573
      gdepth = width = 8;
574 575 576
      break;
    case 12:
    case 16:
577
      gdepth = width = 16;
578 579 580
      break;
    case 20:
    case 24:
581
      gdepth = 24;
582 583
      width = 32;
      break;
584 585 586
    case 32:
      gdepth = width = 32;
      break;
587
    default:
588 589 590
      GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth);
      ret = GST_FLOW_ERROR;
      goto done;
591
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592

593
  if (sample_rate == 0) {
594 595
    if (flacdec->info.rate != 0) {
      sample_rate = flacdec->info.rate;
596 597 598 599 600 601 602
    } else {
      GST_ERROR_OBJECT (flacdec, "unknown sample rate");
      ret = GST_FLOW_ERROR;
      goto done;
    }
  }

603 604 605 606
  caps_changed = (sample_rate != GST_AUDIO_INFO_RATE (&flacdec->info))
      || (width != GST_AUDIO_INFO_WIDTH (&flacdec->info))
      || (gdepth != GST_AUDIO_INFO_DEPTH (&flacdec->info))
      || (channels != GST_AUDIO_INFO_CHANNELS (&flacdec->info));
607 608 609 610 611

  if (caps_changed
      || !gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (flacdec))) {
    GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels", sample_rate,
        channels);
612

613
    gst_audio_info_set_format (&flacdec->info,
614
        gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
615
        sample_rate, channels, NULL);
616

617 618 619 620 621 622 623 624 625
    memcpy (flacdec->info.position,
        channel_positions[flacdec->info.channels - 1],
        sizeof (GstAudioChannelPosition) * flacdec->info.channels);
    gst_audio_channel_positions_to_valid_order (flacdec->info.position,
        flacdec->info.channels);
    /* Note: we create the inverse reordering map here */
    gst_audio_get_channel_reorder_map (flacdec->info.channels,
        flacdec->info.position, channel_positions[flacdec->info.channels - 1],
        flacdec->channel_reorder_map);
626

627
    flacdec->depth = depth;
628

629 630
    gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (flacdec),
        &flacdec->info);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
631 632
  }

Wim Taymans's avatar
Wim Taymans committed
633 634
  outbuf =
      gst_buffer_new_allocate (NULL, samples * channels * (width / 8), NULL);
635

Wim Taymans's avatar
Wim Taymans committed
636
  gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
637
  if (width == 8) {
Wim Taymans's avatar
Wim Taymans committed
638
    gint8 *outbuffer = (gint8 *) map.data;
639
    gint *reorder_map = flacdec->channel_reorder_map;
640

Vincent Penquerc'h's avatar
Vincent Penquerc'h committed
641 642 643 644
    g_assert (gdepth == 8 && depth == 8);
    for (i = 0; i < samples; i++) {
      for (j = 0; j < channels; j++) {
        *outbuffer++ = (gint8) buffer[reorder_map[j]][i];
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
645
      }
646
    }
647
  } else if (width == 16) {
Wim Taymans's avatar
Wim Taymans committed
648
    gint16 *outbuffer = (gint16 *) map.data;
649 650
    gint *reorder_map = flacdec->channel_reorder_map;

651
    if (gdepth != depth) {
652 653 654
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
655
              (gint16) (buffer[reorder_map[j]][i] << (gdepth - depth));
656 657 658 659 660 661 662
        }
      }
    } else {
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ = (gint16) buffer[reorder_map[j]][i];
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
663
      }
664
    }
665
  } else if (width == 32) {
Wim Taymans's avatar
Wim Taymans committed
666
    gint32 *outbuffer = (gint32 *) map.data;
667 668
    gint *reorder_map = flacdec->channel_reorder_map;

669
    if (gdepth != depth) {
670 671 672
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
673
              (gint32) (buffer[reorder_map[j]][i] << (gdepth - depth));
674 675 676 677 678 679 680
        }
      }
    } else {
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ = (gint32) buffer[reorder_map[j]][i];
        }
681 682
      }
    }
683
  } else {
684
    g_assert_not_reached ();
685
  }
Wim Taymans's avatar
Wim Taymans committed
686
  gst_buffer_unmap (outbuf, &map);
687

688
  GST_DEBUG_OBJECT (flacdec, "pushing %d samples", samples);
689 690
  if (flacdec->error_count)
    flacdec->error_count--;
691

692
  ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1);
693

694 695
  if (G_UNLIKELY (ret != GST_FLOW_OK)) {
    GST_DEBUG_OBJECT (flacdec, "finish_frame flow %s", gst_flow_get_name (ret));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
696
  }
697 698 699

done:

700 701 702
  /* we act on the flow return value later in the handle_frame function, as we
   * don't want to mess up the internal decoder state by returning ABORT when
   * the error is in fact non-fatal (like a pad in flushing mode) and we want
703 704
   * to continue later. So just pretend everything's dandy and act later. */
  flacdec->last_flow = ret;
705

706
  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
707 708
}

709 710 711 712 713 714 715 716
static FLAC__StreamDecoderWriteStatus
gst_flac_dec_write_stream (const FLAC__StreamDecoder * decoder,
    const FLAC__Frame * frame,
    const FLAC__int32 * const buffer[], void *client_data)
{
  return gst_flac_dec_write (GST_FLAC_DEC (client_data), frame, buffer);
}

717 718
static void
gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard)
719
{
720
  GstFlacDec *dec = GST_FLAC_DEC (audio_dec);
721

722 723
  if (!hard) {
    guint available = gst_adapter_available (dec->adapter);
724

725 726 727
    if (available > 0) {
      GST_INFO_OBJECT (dec, "draining, %u bytes left in adapter", available);
      FLAC__stream_decoder_process_until_end_of_stream (dec->decoder);
728 729 730
    }
  }

731 732
  FLAC__stream_decoder_flush (dec->decoder);
  gst_adapter_clear (dec->adapter);
733 734 735
}

static GstFlowReturn
736
gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf)
737 738 739
{
  GstFlacDec *dec;

740 741 742 743 744 745 746 747
  dec = GST_FLAC_DEC (audio_dec);

  /* drain remaining data? */
  if (G_UNLIKELY (buf == NULL)) {
    gst_flac_dec_flush (audio_dec, FALSE);
    return GST_FLOW_OK;
  }

748 749 750
  GST_LOG_OBJECT (dec, "frame: ts %" GST_TIME_FORMAT ", flags 0x%04x, "
      "%" G_GSIZE_FORMAT " bytes", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
      GST_BUFFER_FLAGS (buf), gst_buffer_get_size (buf));
751

752
  /* drop any in-stream headers, we've processed those in set_format already */
753
  if (G_UNLIKELY (!dec->got_headers)) {
754
    gboolean got_audio_frame;
Wim Taymans's avatar
Wim Taymans committed
755
    GstMapInfo map;
756 757

    /* check if this is a flac audio frame (rather than a header or junk) */
Wim Taymans's avatar
Wim Taymans committed
758
    gst_buffer_map (buf, &map, GST_MAP_READ);
759
    got_audio_frame = gst_flac_dec_scan_got_frame (dec, map.data, map.size);
Wim Taymans's avatar
Wim Taymans committed
760
    gst_buffer_unmap (buf, &map);
761 762

    if (!got_audio_frame) {
763
      GST_INFO_OBJECT (dec, "dropping in-stream header, %" G_GSIZE_FORMAT " "
Wim Taymans's avatar
Wim Taymans committed
764
          "bytes", map.size);
765
      gst_audio_decoder_finish_frame (audio_dec, NULL, 1);
766 767 768 769 770
      return GST_FLOW_OK;
    }

    GST_INFO_OBJECT (dec, "first audio frame, got all in-stream headers now");
    dec->got_headers = TRUE;
771 772
  }

773
  gst_adapter_push (dec->adapter, gst_buffer_ref (buf));
774 775 776 777
  buf = NULL;

  dec->last_flow = GST_FLOW_OK;

778
  /* framed - there should always be enough data to decode something */
779
  GST_LOG_OBJECT (dec, "%" G_GSIZE_FORMAT " bytes available",
780
      gst_adapter_available (dec->adapter));
781

782 783
  if (!FLAC__stream_decoder_process_single (dec->decoder)) {
    GST_INFO_OBJECT (dec, "process_single failed");
784 785 786 787
  }

  return dec->last_flow;
}