gstflacdec.c 26.1 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);
Vineeth TM's avatar
Vineeth TM committed
103
104
static gboolean
gst_flac_dec_handle_decoder_error (GstFlacDec * dec, gboolean msg);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
105
static void gst_flac_dec_metadata_cb (const FLAC__StreamDecoder *
106
    decoder, const FLAC__StreamMetadata * metadata, void *client_data);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
107
static void gst_flac_dec_error_cb (const FLAC__StreamDecoder *
108
    decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109

110
111
112
113
114
115
116
117
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);
118

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

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

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

138
139
140
141
142
143
144
145
146
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,
147
    GST_STATIC_CAPS (GST_FLAC_DEC_SINK_CAPS));
148

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

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

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

160
161
162
163
164
165
  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
166

167
168
169
170
  gst_element_class_add_static_pad_template (gstelement_class,
      &flac_dec_src_factory);
  gst_element_class_add_static_pad_template (gstelement_class,
      &flac_dec_sink_factory);
Wim Taymans's avatar
Wim Taymans committed
171

172
  gst_element_class_set_static_metadata (gstelement_class, "FLAC audio decoder",
173
174
175
      "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
176
177
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
static void
Wim Taymans's avatar
Wim Taymans committed
179
gst_flac_dec_init (GstFlacDec * flacdec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
180
{
181
  flacdec->do_resync = FALSE;
182
  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (flacdec), TRUE);
183
184
185
  gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
      (flacdec), TRUE);
  GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (flacdec));
186
187
}

188
189
static gboolean
gst_flac_dec_start (GstAudioDecoder * audio_dec)
190
{
191
192
  FLAC__StreamDecoderInitStatus s;
  GstFlacDec *dec;
193

194
  dec = GST_FLAC_DEC (audio_dec);
195
196
197

  dec->adapter = gst_adapter_new ();

198
  dec->decoder = FLAC__stream_decoder_new ();
199

200
201
202
  gst_audio_info_init (&dec->info);
  dec->depth = 0;

203
  /* no point calculating MD5 since it's never checked here */
204
  FLAC__stream_decoder_set_md5_checking (dec->decoder, false);
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

  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;
220
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
221

222
223
static gboolean
gst_flac_dec_stop (GstAudioDecoder * dec)
Colin Walters's avatar
Colin Walters committed
224
{
225
226
227
228
229
230
  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
231

232
233
234
235
236
  if (flacdec->adapter) {
    gst_adapter_clear (flacdec->adapter);
    g_object_unref (flacdec->adapter);
    flacdec->adapter = NULL;
  }
Colin Walters's avatar
Colin Walters committed
237

238
  return TRUE;
Colin Walters's avatar
Colin Walters committed
239
240
}

241
242
243
static gboolean
gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
244
245
246
247
248
249
250
  const GValue *headers;
  GstFlacDec *flacdec;
  GstStructure *s;
  guint i, num;

  flacdec = GST_FLAC_DEC (dec);

251
  GST_LOG_OBJECT (dec, "sink caps: %" GST_PTR_FORMAT, caps);
252
253
254
255
256
257
258
259
260
261
262
263
264
265

  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);
  }

266
267
268
  FLAC__stream_decoder_reset (flacdec->decoder);
  flacdec->got_headers = FALSE;

269
270
271
272
273
274
275
276
277
278
  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);
279
280
    GST_INFO_OBJECT (dec, "pushing header buffer of %" G_GSIZE_FORMAT " bytes "
        "into adapter", gst_buffer_get_size (header_buf));
281
282
283
284
285
286
    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");
Vineeth TM's avatar
Vineeth TM committed
287
288
289
290
291
292
293
294
    if (FLAC__stream_decoder_get_state (flacdec->decoder) ==
        FLAC__STREAM_DECODER_ABORTED) {
      GST_WARNING_OBJECT (flacdec, "Read callback caused internal abort");
      /* allow recovery */
      gst_adapter_clear (flacdec->adapter);
      FLAC__stream_decoder_flush (flacdec->decoder);
      gst_flac_dec_handle_decoder_error (flacdec, TRUE);
    }
295
296
  }
  GST_INFO_OBJECT (dec, "headers and metadata are now processed");
297
298
  return TRUE;
}
Colin Walters's avatar
Colin Walters committed
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
332
333
334
335
336
/* 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
337
gst_flac_calculate_crc8 (const guint8 * data, guint length)
338
339
340
341
342
343
344
345
346
347
348
{
  guint8 crc = 0;

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

  return crc;
}

349
350
/* 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 */
351
static gboolean
352
353
gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, const guint8 * data,
    guint size)
354
355
356
357
358
359
{
  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;
360
  gboolean vbs;
361
362
363
364
365

  if (size < 10)
    return FALSE;

  /* sync */
366
  if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8)
367
368
    return FALSE;

369
  vbs = ! !(data[1] & 1);       /* variable blocksize */
370
  bs = (data[2] & 0xF0) >> 4;   /* blocksize marker   */
371
  sr = (data[2] & 0x0F);        /* samplerate marker  */
372
  ca = (data[3] & 0xF0) >> 4;   /* channel assignment */
373
374
375
  ss = (data[3] & 0x0F) >> 1;   /* sample size marker */
  pb = (data[3] & 0x01);        /* padding bit        */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
376
  GST_LOG_OBJECT (flacdec,
377
378
      "got sync, vbs=%d,bs=%x,sr=%x,ca=%x,ss=%x,pb=%x", vbs, bs, sr, ca, ss,
      pb);
379

380
  if (bs == 0 || sr == 0x0F || ca >= 0x0B || ss == 0x03 || ss == 0x07) {
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
    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;

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
  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;
  }

425
426
427
  return TRUE;
}

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
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
448
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
449
450
gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder,
    const FLAC__StreamMetadata * metadata, void *client_data)
451
{
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
452
  GstFlacDec *flacdec = GST_FLAC_DEC (client_data);
453
  GstAudioChannelPosition position[8];
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
454
455
456

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
457
  switch (metadata->type) {
458
459
    case FLAC__METADATA_TYPE_STREAMINFO:{
      gint64 samples;
460
      guint depth, width, gdepth, channels;
461
462
463
464
465

      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;
466
467
      flacdec->depth = depth = metadata->data.stream_info.bits_per_sample;

468
469
470
471
472
473
      if (depth < 9) {
        gdepth = width = 8;
      } else if (depth < 17) {
        gdepth = width = 16;
      } else if (depth < 25) {
        gdepth = 24;
474
        width = 32;
475
476
477
      } else {
        gdepth = width = 32;
      }
478

479
480
481
482
483
484
485
486
      channels = metadata->data.stream_info.channels;
      memcpy (position, channel_positions[channels - 1], sizeof (position));
      gst_audio_channel_positions_to_valid_order (position, channels);
      /* Note: we create the inverse reordering map here */
      gst_audio_get_channel_reorder_map (channels,
          position, channel_positions[channels - 1],
          flacdec->channel_reorder_map);

487
      gst_audio_info_set_format (&flacdec->info,
488
          gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
489
          metadata->data.stream_info.sample_rate,
490
          metadata->data.stream_info.channels, position);
491

492
493
494
495
496
      gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (flacdec),
          &flacdec->info);

      gst_audio_decoder_negotiate (GST_AUDIO_DECODER (flacdec));

497
498
      GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
          flacdec->min_blocksize, flacdec->max_blocksize);
499
      GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u",
500
          flacdec->info.rate, flacdec->info.channels);
501
      GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth,
502
          flacdec->info.finfo->width);
503
504

      GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
505
      break;
506
    }
507
    default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508
      break;
509
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
510
511
}

512
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
513
514
gst_flac_dec_error_cb (const FLAC__StreamDecoder * d,
    FLAC__StreamDecoderErrorStatus status, void *client_data)
515
516
{
  const gchar *error;
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
517
518
519
  GstFlacDec *dec;

  dec = GST_FLAC_DEC (client_data);
520
521

  switch (status) {
522
    case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
523
      dec->do_resync = TRUE;
524
      return;
525
    case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
526
527
      error = "bad header";
      break;
528
    case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
529
530
531
      error = "CRC mismatch";
      break;
    default:
Johan Dahlin's avatar
Johan Dahlin committed
532
      error = "unknown error";
533
534
535
      break;
  }

536
537
  if (gst_flac_dec_handle_decoder_error (dec, FALSE))
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s (%d)", error, status));
538
539
}

540
541
542
static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
    FLAC__byte buffer[], size_t * bytes, void *client_data)
543
544
545
546
547
548
549
550
551
552
553
{
  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;
  }

554
555
  GST_LOG_OBJECT (dec, "feeding %u bytes to decoder "
      "(available=%" G_GSIZE_FORMAT ", bytes=%u)",
556
      len, gst_adapter_available (dec->adapter), (guint) * bytes);
557
  gst_adapter_copy (dec->adapter, buffer, 0, len);
558
559
560
561
562
563
564
  *bytes = len;

  gst_adapter_flush (dec->adapter, len);

  return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
565
static FLAC__StreamDecoderWriteStatus
566
567
gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
    const FLAC__int32 * const buffer[])
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
568
{
569
  GstFlowReturn ret = GST_FLOW_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
570
571
  GstBuffer *outbuf;
  guint depth = frame->header.bits_per_sample;
572
  guint width, gdepth;
573
  guint sample_rate = frame->header.sample_rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
574
575
576
  guint channels = frame->header.channels;
  guint samples = frame->header.blocksize;
  guint j, i;
Wim Taymans's avatar
Wim Taymans committed
577
  GstMapInfo map;
578
  gboolean caps_changed;
579
  GstAudioChannelPosition chanpos[8];
580

581
582
  GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);

Wim Taymans's avatar
Wim Taymans committed
583
584
585
586
587
588
589
590
591
592
593
  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;
  }

594
595
  switch (depth) {
    case 8:
596
      gdepth = width = 8;
597
598
599
      break;
    case 12:
    case 16:
600
      gdepth = width = 16;
601
602
603
      break;
    case 20:
    case 24:
604
      gdepth = 24;
605
606
      width = 32;
      break;
607
608
609
    case 32:
      gdepth = width = 32;
      break;
610
    default:
611
612
613
      GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth);
      ret = GST_FLOW_ERROR;
      goto done;
614
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
615

616
  if (sample_rate == 0) {
617
618
    if (flacdec->info.rate != 0) {
      sample_rate = flacdec->info.rate;
619
620
621
622
623
624
625
    } else {
      GST_ERROR_OBJECT (flacdec, "unknown sample rate");
      ret = GST_FLOW_ERROR;
      goto done;
    }
  }

626
627
628
629
  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));
630
631
632
633
634

  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);
635

636
637
    memcpy (chanpos, channel_positions[channels - 1], sizeof (chanpos));
    gst_audio_channel_positions_to_valid_order (chanpos, channels);
638
    gst_audio_info_set_format (&flacdec->info,
639
        gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
640
        sample_rate, channels, chanpos);
641

642
643
644
645
    /* 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);
646

647
    flacdec->depth = depth;
648

649
650
    gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (flacdec),
        &flacdec->info);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
651
652
  }

Wim Taymans's avatar
Wim Taymans committed
653
654
  outbuf =
      gst_buffer_new_allocate (NULL, samples * channels * (width / 8), NULL);
655

Wim Taymans's avatar
Wim Taymans committed
656
  gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
657
  if (width == 8) {
Wim Taymans's avatar
Wim Taymans committed
658
    gint8 *outbuffer = (gint8 *) map.data;
659
    gint *reorder_map = flacdec->channel_reorder_map;
660

Vincent Penquerc'h's avatar
Vincent Penquerc'h committed
661
662
663
664
    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
665
      }
666
    }
667
  } else if (width == 16) {
Wim Taymans's avatar
Wim Taymans committed
668
    gint16 *outbuffer = (gint16 *) map.data;
669
670
    gint *reorder_map = flacdec->channel_reorder_map;

671
    if (gdepth != depth) {
672
673
674
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
675
              (gint16) (buffer[reorder_map[j]][i] << (gdepth - depth));
676
677
678
679
680
681
682
        }
      }
    } 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
683
      }
684
    }
685
  } else if (width == 32) {
Wim Taymans's avatar
Wim Taymans committed
686
    gint32 *outbuffer = (gint32 *) map.data;
687
688
    gint *reorder_map = flacdec->channel_reorder_map;

689
    if (gdepth != depth) {
690
691
692
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
693
              (gint32) (buffer[reorder_map[j]][i] << (gdepth - depth));
694
695
696
697
698
699
700
        }
      }
    } else {
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ = (gint32) buffer[reorder_map[j]][i];
        }
701
702
      }
    }
703
  } else {
704
    g_assert_not_reached ();
705
  }
Wim Taymans's avatar
Wim Taymans committed
706
  gst_buffer_unmap (outbuf, &map);
707

708
  GST_DEBUG_OBJECT (flacdec, "pushing %d samples", samples);
709
710
  if (flacdec->error_count)
    flacdec->error_count--;
711

712
  ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1);
713

714
715
  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
716
  }
717
718
719

done:

720
721
722
  /* 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
723
724
   * to continue later. So just pretend everything's dandy and act later. */
  flacdec->last_flow = ret;
725

726
  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
727
728
}

729
730
731
732
733
734
735
736
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);
}

737
738
static void
gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard)
739
{
740
  GstFlacDec *dec = GST_FLAC_DEC (audio_dec);
741

742
743
  if (!hard) {
    guint available = gst_adapter_available (dec->adapter);
744

745
746
747
    if (available > 0) {
      GST_INFO_OBJECT (dec, "draining, %u bytes left in adapter", available);
      FLAC__stream_decoder_process_until_end_of_stream (dec->decoder);
748
749
750
    }
  }

751
  dec->do_resync = FALSE;
752
753
  FLAC__stream_decoder_flush (dec->decoder);
  gst_adapter_clear (dec->adapter);
754
755
756
}

static GstFlowReturn
757
gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf)
758
759
760
{
  GstFlacDec *dec;

761
762
763
764
765
766
767
768
  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;
  }

769
770
771
772
773
774
  if (dec->do_resync) {
    GST_WARNING_OBJECT (dec, "Lost sync, flushing decoder");
    FLAC__stream_decoder_flush (dec->decoder);
    dec->do_resync = FALSE;
  }

775
776
777
  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));
778

779
  /* drop any in-stream headers, we've processed those in set_format already */
780
  if (G_UNLIKELY (!dec->got_headers)) {
781
    gboolean got_audio_frame;
Wim Taymans's avatar
Wim Taymans committed
782
    GstMapInfo map;
783
784

    /* check if this is a flac audio frame (rather than a header or junk) */
Wim Taymans's avatar
Wim Taymans committed
785
    gst_buffer_map (buf, &map, GST_MAP_READ);
786
    got_audio_frame = gst_flac_dec_scan_got_frame (dec, map.data, map.size);
Wim Taymans's avatar
Wim Taymans committed
787
    gst_buffer_unmap (buf, &map);
788
789

    if (!got_audio_frame) {
790
      GST_INFO_OBJECT (dec, "dropping in-stream header, %" G_GSIZE_FORMAT " "
Wim Taymans's avatar
Wim Taymans committed
791
          "bytes", map.size);
792
      gst_audio_decoder_finish_frame (audio_dec, NULL, 1);
793
794
795
796
797
      return GST_FLOW_OK;
    }

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

800
  gst_adapter_push (dec->adapter, gst_buffer_ref (buf));
801
802
803
804
  buf = NULL;

  dec->last_flow = GST_FLOW_OK;

805
  /* framed - there should always be enough data to decode something */
806
  GST_LOG_OBJECT (dec, "%" G_GSIZE_FORMAT " bytes available",
807
      gst_adapter_available (dec->adapter));
808

809
810
  if (!FLAC__stream_decoder_process_single (dec->decoder)) {
    GST_INFO_OBJECT (dec, "process_single failed");
Vineeth TM's avatar
Vineeth TM committed
811
812
813
814
815
816
817
818
    if (FLAC__stream_decoder_get_state (dec->decoder) ==
        FLAC__STREAM_DECODER_ABORTED) {
      GST_WARNING_OBJECT (dec, "Read callback caused internal abort");
      /* allow recovery */
      gst_adapter_clear (dec->adapter);
      FLAC__stream_decoder_flush (dec->decoder);
      gst_flac_dec_handle_decoder_error (dec, TRUE);
    }
819
820
821
822
  }

  return dec->last_flow;
}