gstflacdec.c 25.6 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
171

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

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
  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (flacdec), TRUE);
182
183
}

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

190
  dec = GST_FLAC_DEC (audio_dec);
191
192
193

  dec->adapter = gst_adapter_new ();

194
  dec->decoder = FLAC__stream_decoder_new ();
195

196
197
198
  gst_audio_info_init (&dec->info);
  dec->depth = 0;

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

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

218
219
static gboolean
gst_flac_dec_stop (GstAudioDecoder * dec)
Colin Walters's avatar
Colin Walters committed
220
{
221
222
223
224
225
226
  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
227

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

234
  return TRUE;
Colin Walters's avatar
Colin Walters committed
235
236
}

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

  flacdec = GST_FLAC_DEC (dec);

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

  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);
272
273
    GST_INFO_OBJECT (dec, "pushing header buffer of %" G_GSIZE_FORMAT " bytes "
        "into adapter", gst_buffer_get_size (header_buf));
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");
Vineeth TM's avatar
Vineeth TM committed
280
281
282
283
284
285
286
287
    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);
    }
288
289
  }
  GST_INFO_OBJECT (dec, "headers and metadata are now processed");
290
291
  return TRUE;
}
Colin Walters's avatar
Colin Walters committed
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
/* 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
330
gst_flac_calculate_crc8 (const guint8 * data, guint length)
331
332
333
334
335
336
337
338
339
340
341
{
  guint8 crc = 0;

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

  return crc;
}

342
343
/* 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 */
344
static gboolean
345
346
gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, const guint8 * data,
    guint size)
347
348
349
350
351
352
{
  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;
353
  gboolean vbs;
354
355
356
357
358

  if (size < 10)
    return FALSE;

  /* sync */
359
  if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8)
360
361
    return FALSE;

362
  vbs = ! !(data[1] & 1);       /* variable blocksize */
363
  bs = (data[2] & 0xF0) >> 4;   /* blocksize marker   */
364
  sr = (data[2] & 0x0F);        /* samplerate marker  */
365
  ca = (data[3] & 0xF0) >> 4;   /* channel assignment */
366
367
368
  ss = (data[3] & 0x0F) >> 1;   /* sample size marker */
  pb = (data[3] & 0x01);        /* padding bit        */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
369
  GST_LOG_OBJECT (flacdec,
370
371
      "got sync, vbs=%d,bs=%x,sr=%x,ca=%x,ss=%x,pb=%x", vbs, bs, sr, ca, ss,
      pb);
372

373
  if (bs == 0 || sr == 0x0F || ca >= 0x0B || ss == 0x03 || ss == 0x07) {
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    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;

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  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;
  }

418
419
420
  return TRUE;
}

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
450
  switch (metadata->type) {
451
452
    case FLAC__METADATA_TYPE_STREAMINFO:{
      gint64 samples;
453
      guint depth, width, gdepth, channels;
454
455
456
457
458

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

461
462
463
464
465
466
      if (depth < 9) {
        gdepth = width = 8;
      } else if (depth < 17) {
        gdepth = width = 16;
      } else if (depth < 25) {
        gdepth = 24;
467
        width = 32;
468
469
470
      } else {
        gdepth = width = 32;
      }
471

472
473
474
475
476
477
478
479
      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);

480
      gst_audio_info_set_format (&flacdec->info,
481
          gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
482
          metadata->data.stream_info.sample_rate,
483
          metadata->data.stream_info.channels, position);
484
485
486

      GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
          flacdec->min_blocksize, flacdec->max_blocksize);
487
      GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u",
488
          flacdec->info.rate, flacdec->info.channels);
489
      GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth,
490
          flacdec->info.finfo->width);
491
492

      GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
493
      break;
494
    }
495
    default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496
      break;
497
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498
499
}

500
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
501
502
gst_flac_dec_error_cb (const FLAC__StreamDecoder * d,
    FLAC__StreamDecoderErrorStatus status, void *client_data)
503
504
{
  const gchar *error;
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
505
506
507
  GstFlacDec *dec;

  dec = GST_FLAC_DEC (client_data);
508
509

  switch (status) {
510
    case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
511
512
      /* Ignore this error and keep processing */
      return;
513
    case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
514
515
      error = "bad header";
      break;
516
    case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
517
518
519
      error = "CRC mismatch";
      break;
    default:
Johan Dahlin's avatar
Johan Dahlin committed
520
      error = "unknown error";
521
522
523
      break;
  }

524
525
  if (gst_flac_dec_handle_decoder_error (dec, FALSE))
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s (%d)", error, status));
526
527
}

528
529
530
static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
    FLAC__byte buffer[], size_t * bytes, void *client_data)
531
532
533
534
535
536
537
538
539
540
541
{
  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;
  }

542
543
  GST_LOG_OBJECT (dec, "feeding %u bytes to decoder "
      "(available=%" G_GSIZE_FORMAT ", bytes=%u)",
544
      len, gst_adapter_available (dec->adapter), (guint) * bytes);
545
  gst_adapter_copy (dec->adapter, buffer, 0, len);
546
547
548
549
550
551
552
  *bytes = len;

  gst_adapter_flush (dec->adapter, len);

  return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
553
static FLAC__StreamDecoderWriteStatus
554
555
gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
    const FLAC__int32 * const buffer[])
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
556
{
557
  GstFlowReturn ret = GST_FLOW_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
558
559
  GstBuffer *outbuf;
  guint depth = frame->header.bits_per_sample;
560
  guint width, gdepth;
561
  guint sample_rate = frame->header.sample_rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
562
563
564
  guint channels = frame->header.channels;
  guint samples = frame->header.blocksize;
  guint j, i;
Wim Taymans's avatar
Wim Taymans committed
565
  GstMapInfo map;
566
  gboolean caps_changed;
567
  GstAudioChannelPosition chanpos[8];
568

569
570
  GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);

Wim Taymans's avatar
Wim Taymans committed
571
572
573
574
575
576
577
578
579
580
581
  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;
  }

582
583
  switch (depth) {
    case 8:
584
      gdepth = width = 8;
585
586
587
      break;
    case 12:
    case 16:
588
      gdepth = width = 16;
589
590
591
      break;
    case 20:
    case 24:
592
      gdepth = 24;
593
594
      width = 32;
      break;
595
596
597
    case 32:
      gdepth = width = 32;
      break;
598
    default:
599
600
601
      GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth);
      ret = GST_FLOW_ERROR;
      goto done;
602
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
603

604
  if (sample_rate == 0) {
605
606
    if (flacdec->info.rate != 0) {
      sample_rate = flacdec->info.rate;
607
608
609
610
611
612
613
    } else {
      GST_ERROR_OBJECT (flacdec, "unknown sample rate");
      ret = GST_FLOW_ERROR;
      goto done;
    }
  }

614
615
616
617
  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));
618
619
620
621
622

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

624
625
626
627
    memcpy (chanpos, channel_positions[flacdec->info.channels - 1],
        sizeof (chanpos));
    gst_audio_channel_positions_to_valid_order (chanpos,
        flacdec->info.channels);
628
    gst_audio_info_set_format (&flacdec->info,
629
        gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth),
630
        sample_rate, channels, chanpos);
631

632
633
634
635
    /* 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);
636

637
    flacdec->depth = depth;
638

639
640
    gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (flacdec),
        &flacdec->info);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
641
642
  }

Wim Taymans's avatar
Wim Taymans committed
643
644
  outbuf =
      gst_buffer_new_allocate (NULL, samples * channels * (width / 8), NULL);
645

Wim Taymans's avatar
Wim Taymans committed
646
  gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
647
  if (width == 8) {
Wim Taymans's avatar
Wim Taymans committed
648
    gint8 *outbuffer = (gint8 *) map.data;
649
    gint *reorder_map = flacdec->channel_reorder_map;
650

Vincent Penquerc'h's avatar
Vincent Penquerc'h committed
651
652
653
654
    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
655
      }
656
    }
657
  } else if (width == 16) {
Wim Taymans's avatar
Wim Taymans committed
658
    gint16 *outbuffer = (gint16 *) map.data;
659
660
    gint *reorder_map = flacdec->channel_reorder_map;

661
    if (gdepth != depth) {
662
663
664
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
665
              (gint16) (buffer[reorder_map[j]][i] << (gdepth - depth));
666
667
668
669
670
671
672
        }
      }
    } 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
673
      }
674
    }
675
  } else if (width == 32) {
Wim Taymans's avatar
Wim Taymans committed
676
    gint32 *outbuffer = (gint32 *) map.data;
677
678
    gint *reorder_map = flacdec->channel_reorder_map;

679
    if (gdepth != depth) {
680
681
682
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ =
683
              (gint32) (buffer[reorder_map[j]][i] << (gdepth - depth));
684
685
686
687
688
689
690
        }
      }
    } else {
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
          *outbuffer++ = (gint32) buffer[reorder_map[j]][i];
        }
691
692
      }
    }
693
  } else {
694
    g_assert_not_reached ();
695
  }
Wim Taymans's avatar
Wim Taymans committed
696
  gst_buffer_unmap (outbuf, &map);
697

698
  GST_DEBUG_OBJECT (flacdec, "pushing %d samples", samples);
699
700
  if (flacdec->error_count)
    flacdec->error_count--;
701

702
  ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1);
703

704
705
  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
706
  }
707
708
709

done:

710
711
712
  /* 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
713
714
   * to continue later. So just pretend everything's dandy and act later. */
  flacdec->last_flow = ret;
715

716
  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
717
718
}

719
720
721
722
723
724
725
726
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);
}

727
728
static void
gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard)
729
{
730
  GstFlacDec *dec = GST_FLAC_DEC (audio_dec);
731

732
733
  if (!hard) {
    guint available = gst_adapter_available (dec->adapter);
734

735
736
737
    if (available > 0) {
      GST_INFO_OBJECT (dec, "draining, %u bytes left in adapter", available);
      FLAC__stream_decoder_process_until_end_of_stream (dec->decoder);
738
739
740
    }
  }

741
742
  FLAC__stream_decoder_flush (dec->decoder);
  gst_adapter_clear (dec->adapter);
743
744
745
}

static GstFlowReturn
746
gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf)
747
748
749
{
  GstFlacDec *dec;

750
751
752
753
754
755
756
757
  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;
  }

758
759
760
  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));
761

762
  /* drop any in-stream headers, we've processed those in set_format already */
763
  if (G_UNLIKELY (!dec->got_headers)) {
764
    gboolean got_audio_frame;
Wim Taymans's avatar
Wim Taymans committed
765
    GstMapInfo map;
766
767

    /* check if this is a flac audio frame (rather than a header or junk) */
Wim Taymans's avatar
Wim Taymans committed
768
    gst_buffer_map (buf, &map, GST_MAP_READ);
769
    got_audio_frame = gst_flac_dec_scan_got_frame (dec, map.data, map.size);
Wim Taymans's avatar
Wim Taymans committed
770
    gst_buffer_unmap (buf, &map);
771
772

    if (!got_audio_frame) {
773
      GST_INFO_OBJECT (dec, "dropping in-stream header, %" G_GSIZE_FORMAT " "
Wim Taymans's avatar
Wim Taymans committed
774
          "bytes", map.size);
775
      gst_audio_decoder_finish_frame (audio_dec, NULL, 1);
776
777
778
779
780
      return GST_FLOW_OK;
    }

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

783
  gst_adapter_push (dec->adapter, gst_buffer_ref (buf));
784
785
786
787
  buf = NULL;

  dec->last_flow = GST_FLOW_OK;

788
  /* framed - there should always be enough data to decode something */
789
  GST_LOG_OBJECT (dec, "%" G_GSIZE_FORMAT " bytes available",
790
      gst_adapter_available (dec->adapter));
791

792
793
  if (!FLAC__stream_decoder_process_single (dec->decoder)) {
    GST_INFO_OBJECT (dec, "process_single failed");
Vineeth TM's avatar
Vineeth TM committed
794
795
796
797
798
799
800
801
    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);
    }
802
803
804
805
  }

  return dec->last_flow;
}