gstflacdec.c 25.2 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
18
19
20
21
 *
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

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-0.11 filesrc location=media/small/dark.441-16-s.flac ! flacparse ! flacdec ! audioconvert ! audioresample ! autoaudiosink
34
35
 * ]|
 * |[
36
 * gst-launch-0.11 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
118
119
120
121
122
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define FORMATS "{ S8LE, S16LE, S32LE } "
#else
#define FORMATS "{ S8BE, S16BE, S32BE } "
#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
170

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

  gst_element_class_set_details_simple (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
  /* nothing to do here */
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
335
336
337
338
339
340
341
342
343
344
345
346
347
static gboolean
gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, guint8 * data, guint size,
    gint64 * last_sample_num)
{
  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;

  if (size < 10)
    return FALSE;

  /* sync */
348
  if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8)
349
    return FALSE;
350
351
352
353
  if (data[1] & 1) {
    GST_WARNING_OBJECT (flacdec, "Variable block size FLAC unsupported");
    return FALSE;
  }
354

355
  bs = (data[2] & 0xF0) >> 4;   /* blocksize marker   */
356
  sr = (data[2] & 0x0F);        /* samplerate marker  */
357
  ca = (data[3] & 0xF0) >> 4;   /* channel assignment */
358
359
360
  ss = (data[3] & 0x0F) >> 1;   /* sample size marker */
  pb = (data[3] & 0x01);        /* padding bit        */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
361
362
  GST_LOG_OBJECT (flacdec,
      "got sync, bs=%x,sr=%x,ca=%x,ss=%x,pb=%x", bs, sr, ca, ss, pb);
363

364
  if (bs == 0 || sr == 0x0F || ca >= 0x0B || ss == 0x03 || ss == 0x07) {
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    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;

380
381
382
383
  /* FIXME: This is can be 36 bit if variable block size is used,
   * fortunately not encoder supports this yet and we check for that
   * above.
   */
384
385
386
387
388
389
390
391
392
393
  val = (guint32) g_utf8_get_char_validated ((gchar *) data + 4, -1);

  if (val == (guint32) - 1 || val == (guint32) - 2) {
    GST_LOG_OBJECT (flacdec, "failed to read sample/frame");
    return FALSE;
  }

  headerlen = 4 + g_unichar_to_utf8 ((gunichar) val, NULL) +
      (bs_from_end / 8) + (sr_from_end / 8);

394
395
  if (gst_flac_calculate_crc8 (data, headerlen) != data[headerlen]) {
    GST_LOG_OBJECT (flacdec, "invalid checksum");
396
    return FALSE;
397
  }
398
399
400
401

  if (flacdec->min_blocksize == flacdec->max_blocksize) {
    *last_sample_num = (val + 1) * flacdec->min_blocksize;
  } else {
402
    *last_sample_num = 0;       /* FIXME: + length of last block in samples */
403
404
  }

405
406
407
408
  /* FIXME: only valid for fixed block size streams */
  GST_DEBUG_OBJECT (flacdec, "frame number: %" G_GINT64_FORMAT,
      *last_sample_num);

409
  if (flacdec->info.rate > 0 && *last_sample_num != 0) {
410
411
    GST_DEBUG_OBJECT (flacdec, "last sample %" G_GINT64_FORMAT " = %"
        GST_TIME_FORMAT, *last_sample_num,
412
        GST_TIME_ARGS (*last_sample_num * GST_SECOND / flacdec->info.rate));
413
  }
414
415
416
417

  return TRUE;
}

418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
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
438
static void
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
439
440
gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder,
    const FLAC__StreamMetadata * metadata, void *client_data)
441
{
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
442
443
444
445
  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
446
  switch (metadata->type) {
447
448
    case FLAC__METADATA_TYPE_STREAMINFO:{
      gint64 samples;
449
      guint depth, width;
450
451
452
453
454

      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;
455
456
457
      flacdec->depth = depth = metadata->data.stream_info.bits_per_sample;

      if (depth < 9)
458
        width = 8;
459
      else if (depth < 17)
460
        width = 16;
461
      else
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
        width = 32;

      gst_audio_info_set_format (&flacdec->info,
          gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, width),
          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);
478
479
480

      GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u",
          flacdec->min_blocksize, flacdec->max_blocksize);
481
      GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u",
482
          flacdec->info.rate, flacdec->info.channels);
483
      GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth,
484
          flacdec->info.finfo->width);
485
486

      GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487
      break;
488
    }
489
    default:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
490
      break;
491
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492
493
}

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

  dec = GST_FLAC_DEC (client_data);
502
503

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

518
519
  if (gst_flac_dec_handle_decoder_error (dec, FALSE))
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s (%d)", error, status));
520
521
}

522
523
524
static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
    FLAC__byte buffer[], size_t * bytes, void *client_data)
525
526
527
528
529
530
531
532
533
534
535
{
  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;
  }

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

  gst_adapter_flush (dec->adapter, len);

  return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

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

562
563
  GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);

Wim Taymans's avatar
Wim Taymans committed
564
565
566
567
568
569
570
571
572
573
574
  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;
  }

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

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

604
605
606
607
608
609
610
611
  caps_changed = (sample_rate != flacdec->info.rate)
      || (width != flacdec->info.finfo->width)
      || (channels != flacdec->info.channels);

  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
614
615
    gst_audio_info_set_format (&flacdec->info,
        gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, width),
        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

641
642
643
    if (width != depth) {
      for (i = 0; i < samples; i++) {
        for (j = 0; j < channels; j++) {
644
          *outbuffer++ = (gint8) (buffer[reorder_map[j]][i] << (width - depth));
645
646
647
648
649
650
651
        }
      }
    } else {
      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
652
      }
653
    }
654
  } else if (width == 16) {
Wim Taymans's avatar
Wim Taymans committed
655
    gint16 *outbuffer = (gint16 *) map.data;
656
657
658
659
660
661
    gint *reorder_map = flacdec->channel_reorder_map;

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

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

695
  GST_DEBUG_OBJECT (flacdec, "pushing %d samples", samples);
696
697
  if (flacdec->error_count)
    flacdec->error_count--;
698

699
  ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1);
700

701
702
  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
703
  }
704
705
706

done:

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

713
  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
714
715
}

716
717
718
719
720
721
722
723
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);
}

724
725
static void
gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard)
726
{
727
  GstFlacDec *dec = GST_FLAC_DEC (audio_dec);
728

729
730
  if (!hard) {
    guint available = gst_adapter_available (dec->adapter);
731

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

738
739
  FLAC__stream_decoder_flush (dec->decoder);
  gst_adapter_clear (dec->adapter);
740
741
742
}

static GstFlowReturn
743
gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf)
744
745
746
{
  GstFlacDec *dec;

747
748
749
750
751
752
753
754
  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;
  }

755
756
757
  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));
758

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

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

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

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

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

  dec->last_flow = GST_FLOW_OK;

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

791
792
  if (!FLAC__stream_decoder_process_single (dec->decoder)) {
    GST_INFO_OBJECT (dec, "process_single failed");
793
794
795
796
  }

  return dec->last_flow;
}