gstflacdec.c 23.8 KB
Newer Older
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * 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.
 */

20
21
22
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
23
24
25
#include <string.h>
#include <sys/soundcard.h>

26
/*#define DEBUG_ENABLED */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
27
28
#include "gstflacdec.h"

29
#include "flac_compat.h"
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
30

31
static GstPadTemplate *src_template, *sink_template;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
32
33
34
35

/* elementfactory information */
GstElementDetails flacdec_details = {
  "FLAC decoder",
36
  "Codec/Decoder/Audio",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37
38
39
40
41
42
43
44
45
46
47
48
  "Decodes FLAC lossless audio streams",
  "Wim Taymans <wim.taymans@chello.be>",
};

/* FlacDec signals and args */
enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
49
  ARG_METADATA
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
51
};

52
static void             gst_flacdec_base_init           (gpointer g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
53
54
55
56
static void 		gst_flacdec_class_init		(FlacDecClass *klass);
static void 		gst_flacdec_init		(FlacDec *flacdec);

static void 		gst_flacdec_loop		(GstElement *element);
57
58
static GstElementStateReturn
			gst_flacdec_change_state 	(GstElement *element);
59
static const GstFormat* gst_flacdec_get_src_formats 	(GstPad *pad);
60
61
static gboolean 	gst_flacdec_convert_src	 	(GstPad *pad, GstFormat src_format, gint64 src_value,
		        				 GstFormat *dest_format, gint64 *dest_value);
Wim Taymans's avatar
Wim Taymans committed
62
static const GstQueryType* 
63
			gst_flacdec_get_src_query_types (GstPad *pad);
Wim Taymans's avatar
Wim Taymans committed
64
static gboolean		gst_flacdec_src_query 		(GstPad *pad, GstQueryType type,
65
	               					 GstFormat *format, gint64 *value);
66
67
static const GstEventMask* 
			gst_flacdec_get_src_event_masks (GstPad *pad);
68
static gboolean 	gst_flacdec_src_event 		(GstPad *pad, GstEvent *event);
69
70
71
72
73
74
75
76
static void 	gst_flacdec_get_property 	(GObject *object, 
		            			 guint prop_id, 
						 GValue *value, 
						 GParamSpec *pspec);
static void 	gst_flacdec_set_property 	(GObject *object, 
		            			 guint prop_id, 
						 const GValue *value, 
						 GParamSpec *pspec);
77
78
79

static FLAC__SeekableStreamDecoderReadStatus 	
			gst_flacdec_read 		(const FLAC__SeekableStreamDecoder *decoder, 
Wim Taymans's avatar
Wim Taymans committed
80
81
							 FLAC__byte buffer[], unsigned *bytes, 
							 void *client_data);
82
83
84
85
86
87
88
89
90
91
92
93
94
static FLAC__SeekableStreamDecoderSeekStatus 	
			gst_flacdec_seek 		(const FLAC__SeekableStreamDecoder *decoder, 
							 FLAC__uint64 position, void *client_data);
static FLAC__SeekableStreamDecoderTellStatus 	
			gst_flacdec_tell 		(const FLAC__SeekableStreamDecoder *decoder, 
							 FLAC__uint64 *position, void *client_data);
static FLAC__SeekableStreamDecoderLengthStatus 	
			gst_flacdec_length 		(const FLAC__SeekableStreamDecoder *decoder, 
							 FLAC__uint64 *length, void *client_data);
static FLAC__bool 	gst_flacdec_eof 		(const FLAC__SeekableStreamDecoder *decoder, 
							 void *client_data);
static FLAC__StreamDecoderWriteStatus 	
			gst_flacdec_write 		(const FLAC__SeekableStreamDecoder *decoder, 
Wim Taymans's avatar
Wim Taymans committed
95
96
							 const FLAC__Frame *frame, 
							 const FLAC__int32 * const buffer[], 
97
98
							 void *client_data);
static void 		gst_flacdec_metadata_callback 	(const FLAC__SeekableStreamDecoder *decoder, 
99
							 const FLAC__StreamMetadata *metadata, 
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
100
							 void *client_data);
101
static void 		gst_flacdec_error_callback 	(const FLAC__SeekableStreamDecoder *decoder, 
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102
103
104
105
							 FLAC__StreamDecoderErrorStatus status, 
							 void *client_data);

static GstElementClass *parent_class = NULL;
106
/*static guint gst_flacdec_signals[LAST_SIGNAL] = { 0 }; */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107
108
109
110
111
112
113
114

GType
flacdec_get_type(void) {
  static GType flacdec_type = 0;

  if (!flacdec_type) {
    static const GTypeInfo flacdec_info = {
      sizeof(FlacDecClass),
115
      gst_flacdec_base_init,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
116
117
118
119
120
121
122
123
124
125
126
127
128
      NULL,
      (GClassInitFunc)gst_flacdec_class_init,
      NULL,
      NULL,
      sizeof(FlacDec),
      0,
      (GInstanceInitFunc)gst_flacdec_init,
    };
    flacdec_type = g_type_register_static (GST_TYPE_ELEMENT, "FlacDec", &flacdec_info, 0);
  }
  return flacdec_type;
}

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
static GstCaps*
flac_caps_factory (void)
{
  return
   gst_caps_new (
  	"flac_flac",
  	"application/x-flac",
	/*gst_props_new (
 	    "rate",     	GST_PROPS_INT_RANGE (11025, 48000),
    	    "channels", 	GST_PROPS_INT_RANGE (1, 2),
	    NULL)*/ NULL);
}

static GstCaps*
raw_caps_factory (void)
{
  return
   gst_caps_new (
  	"flac_raw",
  	"audio/x-raw-int",
	gst_props_new (
    	    "endianness", 	GST_PROPS_INT (G_BYTE_ORDER),
    	    "signed", 		GST_PROPS_BOOLEAN (TRUE),
    	    "width", 		GST_PROPS_INT (16),
    	    "depth",    	GST_PROPS_INT (16),
    	    "rate",     	GST_PROPS_INT_RANGE (11025, 48000),
    	    "channels", 	GST_PROPS_INT_RANGE (1, 2),
	    NULL));
}

static void
gst_flacdec_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
  GstCaps *raw_caps, *flac_caps;

  raw_caps = raw_caps_factory ();
  flac_caps = flac_caps_factory ();

  sink_template = gst_pad_template_new ("sink", GST_PAD_SINK, 
		                              GST_PAD_ALWAYS, 
					      flac_caps, NULL);
  src_template = gst_pad_template_new ("src", GST_PAD_SRC, 
		                             GST_PAD_ALWAYS, 
					     raw_caps, NULL);
  gst_element_class_add_pad_template (element_class, sink_template);
  gst_element_class_add_pad_template (element_class, src_template);
  gst_element_class_set_details (element_class, &flacdec_details);
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179
180
181
182
static void
gst_flacdec_class_init (FlacDecClass *klass) 
{
  GstElementClass *gstelement_class;
183
	GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
184
185

  gstelement_class = (GstElementClass*)klass;
186
	gobject_class = (GObjectClass*) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
187
188

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
189
190
191
192
193
194
195
196
	
	g_object_class_install_property (gobject_class, ARG_METADATA,
    g_param_spec_boxed ("metadata", "Metadata", "(logical) Stream metadata",
                         GST_TYPE_CAPS, G_PARAM_READABLE));

	gobject_class->get_property = gst_flacdec_get_property;
  gobject_class->set_property = gst_flacdec_set_property;
	
197
  gstelement_class->change_state = gst_flacdec_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
198
199
200
201
202
}

static void 
gst_flacdec_init (FlacDec *flacdec) 
{
203
  flacdec->sinkpad = gst_pad_new_from_template (sink_template, "sink");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204
  gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->sinkpad);
205
  gst_pad_set_convert_function (flacdec->sinkpad, NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206
207

  gst_element_set_loop_function (GST_ELEMENT (flacdec), gst_flacdec_loop);
208
  flacdec->srcpad = gst_pad_new_from_template (src_template, "src");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
209
  gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->srcpad);
210
  gst_pad_set_formats_function (flacdec->srcpad, gst_flacdec_get_src_formats);
211
  gst_pad_set_convert_function (flacdec->srcpad, gst_flacdec_convert_src);
212
213
214
  gst_pad_set_query_type_function (flacdec->srcpad, gst_flacdec_get_src_query_types);
  gst_pad_set_query_function (flacdec->srcpad, gst_flacdec_src_query);
  gst_pad_set_event_mask_function (flacdec->srcpad, gst_flacdec_get_src_event_masks);
215
216
217
218
219
220
221
  gst_pad_set_event_function (flacdec->srcpad, gst_flacdec_src_event);

  flacdec->decoder = FLAC__seekable_stream_decoder_new ();
  flacdec->total_samples = 0;
  flacdec->init = TRUE;
  flacdec->eos = FALSE;
  flacdec->seek_pending = FALSE;
222
	flacdec->metadata = NULL;
223
224
225
226
227
228

  FLAC__seekable_stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read);
  FLAC__seekable_stream_decoder_set_seek_callback (flacdec->decoder, gst_flacdec_seek);
  FLAC__seekable_stream_decoder_set_tell_callback (flacdec->decoder, gst_flacdec_tell);
  FLAC__seekable_stream_decoder_set_length_callback (flacdec->decoder, gst_flacdec_length);
  FLAC__seekable_stream_decoder_set_eof_callback (flacdec->decoder, gst_flacdec_eof);
229
#if FLAC_VERSION >= 0x010003
230
  FLAC__seekable_stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write);
231
232
233
234
235
236
237
238
239
#else
  FLAC__seekable_stream_decoder_set_write_callback (flacdec->decoder,
	(FLAC__StreamDecoderWriteStatus (*)
		(const FLAC__SeekableStreamDecoder *decoder, 
		 const FLAC__Frame *frame,
		 const FLAC__int32 *buffer[], 
		 void *client_data))
		(gst_flacdec_write));
#endif
240
  FLAC__seekable_stream_decoder_set_metadata_respond (flacdec->decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
241
242
243
244
  FLAC__seekable_stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback);
  FLAC__seekable_stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback);
  FLAC__seekable_stream_decoder_set_client_data (flacdec->decoder, flacdec);
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
245

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
static gboolean
gst_flacdec_update_metadata (FlacDec *flacdec, const FLAC__StreamMetadata *metadata)
{
  guint32 number_of_comments, cursor, str_len;
  gchar *p_value, *value, *name, *str_ptr;
  GstProps *props = NULL;
  GstPropsEntry *entry;
	
  /* clear old one */
  if (flacdec->metadata) {
    gst_caps_unref (flacdec->metadata);
    flacdec->metadata = NULL;
  }

  /* create props to hold the key/value pairs */
  props = gst_props_empty_new ();
  
  number_of_comments = metadata->data.vorbis_comment.num_comments;
  value = NULL;
265
  GST_DEBUG ("%d tag(s) found",  number_of_comments);
266
267
268
269
270
271
272
273
274
275
276
277
278
  for (cursor = 0; cursor < number_of_comments; cursor++)
  {			
    str_ptr = metadata->data.vorbis_comment.comments[cursor].entry;
    str_len = GUINT32_FROM_LE (metadata->data.vorbis_comment.comments[cursor].length);
    p_value = g_strstr_len ( str_ptr, str_len , "=" );
    if (p_value)
    {			
      name = g_strndup (str_ptr, p_value - str_ptr);
      value = g_strndup (p_value + 1, str_ptr + str_len - p_value);
			
      entry = gst_props_entry_new (name, GST_PROPS_STRING_TYPE, value);
      gst_props_add_entry (props, (GstPropsEntry *) entry);

279
      GST_DEBUG ("%s : %s", name, value);
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

      g_free (name);
      g_free (value);
    }
  }
	
  flacdec->metadata = gst_caps_new ("vorbisfile_metadata",
                                    "application/x-gst-metadata",
                                    props);
  g_object_notify (G_OBJECT (flacdec), "metadata");
	
  return TRUE;
}


295
296
static void 
gst_flacdec_metadata_callback (const FLAC__SeekableStreamDecoder *decoder,
297
			       const FLAC__StreamMetadata *metadata, void *client_data)
298
299
{
  FlacDec *flacdec;
300
	
301
  flacdec = GST_FLACDEC (client_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302

303
304
305
306
307
308
309
310
311
312
313
  switch (metadata->type)
  {
    case FLAC__METADATA_TYPE_STREAMINFO:
         flacdec->stream_samples = metadata->data.stream_info.total_samples;
 	 break;
    case FLAC__METADATA_TYPE_VORBIS_COMMENT:
         gst_flacdec_update_metadata (flacdec, metadata);		  
	 break;
    default:
         break;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
315
316
}

static void 
317
318
gst_flacdec_error_callback (const FLAC__SeekableStreamDecoder *decoder, 
			    FLAC__StreamDecoderErrorStatus status, void *client_data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
319
{
320
321
322
323
324
325
  FlacDec *flacdec;
  gchar *error;

  flacdec = GST_FLACDEC (client_data);

  switch (status) {
326
    case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
327
328
      error = "lost sync";
      break;
329
    case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
330
331
      error = "bad header";
      break;
332
    case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
333
334
335
336
337
338
339
      error = "CRC mismatch";
      break;
    default:
      error = "unkown error";
      break;
  }

340
  GST_DEBUG (error);
341
				  
342
  gst_element_error (GST_ELEMENT (flacdec), error);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
344
}

345
static FLAC__SeekableStreamDecoderSeekStatus 	
Wim Taymans's avatar
Wim Taymans committed
346
347
gst_flacdec_seek (const FLAC__SeekableStreamDecoder *decoder, 
		  FLAC__uint64 position, void *client_data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
348
{
349
350
351
352
  FlacDec *flacdec;

  flacdec = GST_FLACDEC (client_data);

353
  GST_DEBUG ("seek %" G_GINT64_FORMAT, position);
354
355
356
357
  if (!gst_bytestream_seek (flacdec->bs, position, GST_SEEK_METHOD_SET)) {
    return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
  }
  return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
358
359
}

360
static FLAC__SeekableStreamDecoderTellStatus 	
Wim Taymans's avatar
Wim Taymans committed
361
362
gst_flacdec_tell (const FLAC__SeekableStreamDecoder *decoder, 
		  FLAC__uint64 *position, void *client_data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
363
364
365
366
367
{
  FlacDec *flacdec;

  flacdec = GST_FLACDEC (client_data);

368
369
370
  *position = gst_bytestream_tell (flacdec->bs);
  if (*position == -1)
    return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371

372
  GST_DEBUG ("tell %" G_GINT64_FORMAT, *position);
373
374
375
376
377

  return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
}

static FLAC__SeekableStreamDecoderLengthStatus 	
Wim Taymans's avatar
Wim Taymans committed
378
379
gst_flacdec_length (const FLAC__SeekableStreamDecoder *decoder, 
		    FLAC__uint64 *length, void *client_data)
380
381
382
383
384
385
386
387
388
{
  FlacDec *flacdec;

  flacdec = GST_FLACDEC (client_data);

  *length = gst_bytestream_length (flacdec->bs);
  if (*length == -1)
    return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;

389
  GST_DEBUG ("length %" G_GINT64_FORMAT, *length);
390
391
392
393
394

  return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
}

static FLAC__bool
Wim Taymans's avatar
Wim Taymans committed
395
396
gst_flacdec_eof (const FLAC__SeekableStreamDecoder *decoder, 
		 void *client_data)
397
398
399
400
{
  FlacDec *flacdec;

  flacdec = GST_FLACDEC (client_data);
401
  GST_DEBUG ("eof %d", flacdec->eos);
402
403
404
405
406

  return flacdec->eos;
}

static FLAC__SeekableStreamDecoderReadStatus
Wim Taymans's avatar
Wim Taymans committed
407
408
409
gst_flacdec_read (const FLAC__SeekableStreamDecoder *decoder, 
		  FLAC__byte buffer[], unsigned *bytes, 
		  void *client_data)
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
{
  FlacDec *flacdec;
  gint insize = 0;
  guint8 *indata;

  flacdec = GST_FLACDEC (client_data);

  //g_print ("read %u\n", *bytes);
  
  while (insize == 0) {
    insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, *bytes);
    if (insize < *bytes) {
      GstEvent *event;
      guint32 avail;
			    
      gst_bytestream_get_status (flacdec->bs, &avail, &event);

      switch (GST_EVENT_TYPE (event)) {
        case GST_EVENT_EOS:
429
          GST_DEBUG ("eos");
430
          flacdec->eos = TRUE; 
431
	  gst_event_unref (event);
432
433
434
435
436
          if (avail == 0) {
            return 0;
          }
          break;
        case GST_EVENT_DISCONTINUOUS:
437
          GST_DEBUG ("discont");
438
439
440

	  /* we are not yet sending the discont, we'll do that in the next write operation */
	  flacdec->need_discont = TRUE;
441
	  gst_event_unref (event);
442
443
444
445
446
447
448
449
450
451
	  break;
        default:
	  gst_pad_event_default (flacdec->sinkpad, event);
          break;
      }
      if (avail > 0)
        insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, avail);
      else
        insize = 0;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
452
453
  }

454
455
456
  memcpy (buffer, indata, insize);
  *bytes = insize;
  gst_bytestream_flush_fast (flacdec->bs, insize);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
457

458
  return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459
460
461
}

static FLAC__StreamDecoderWriteStatus
Wim Taymans's avatar
Wim Taymans committed
462
463
464
465
gst_flacdec_write (const FLAC__SeekableStreamDecoder *decoder, 
		   const FLAC__Frame *frame, 
		   const FLAC__int32 * const buffer[], 
		   void *client_data)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466
467
468
469
470
471
472
473
474
475
{
  FlacDec *flacdec;
  GstBuffer *outbuf;
  guint depth = frame->header.bits_per_sample;
  guint channels = frame->header.channels;
  guint samples = frame->header.blocksize;
  guint j, i;

  flacdec = GST_FLACDEC (client_data);

476
477
478
479
480
481
  if (flacdec->need_discont) {
    gint64 time = 0, bytes = 0;
    GstFormat format;
    GstEvent *discont;

    flacdec->need_discont = FALSE;
482
483
    
    if (!GST_PAD_CAPS (flacdec->srcpad)) {
484
485
486
      if (flacdec->seek_pending) {
        flacdec->total_samples = flacdec->seek_value;
      }
487

488
      if (GST_PAD_IS_USABLE (flacdec->srcpad)) {
489
        GST_DEBUG ("send discont");
490
491
492
493
494
495
496
497
498

        format = GST_FORMAT_TIME;
        gst_pad_convert (flacdec->srcpad, GST_FORMAT_DEFAULT,
                         flacdec->total_samples, &format, &time);
        format = GST_FORMAT_BYTES;
        gst_pad_convert (flacdec->srcpad, GST_FORMAT_DEFAULT,
                         flacdec->total_samples, &format, &bytes);
        discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time,
	  	                             GST_FORMAT_BYTES, bytes,
499
		                             GST_FORMAT_DEFAULT, flacdec->total_samples, 
Wim Taymans's avatar
Wim Taymans committed
500
					 NULL);
501
	  
502
        gst_pad_push (flacdec->srcpad, GST_DATA (discont));
503
      }
504
    }
505
506
  }
  
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
507
  if (!GST_PAD_CAPS (flacdec->srcpad)) {
508
    gst_pad_try_set_caps (flacdec->srcpad,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509
510
		    GST_CAPS_NEW (
		      "flac_caps",
511
		      "audio/x-raw-int",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
512
513
514
515
516
517
518
                         "endianness",  GST_PROPS_INT (G_BYTE_ORDER),
                         "signed",      GST_PROPS_BOOLEAN (TRUE),
                         "width",       GST_PROPS_INT (depth),
                         "depth",       GST_PROPS_INT (depth),
                         "rate",     	GST_PROPS_INT (frame->header.sample_rate),
                         "channels", 	GST_PROPS_INT (channels)
		    ));
519
520
521
522

    flacdec->depth = depth;
    flacdec->channels = channels;
    flacdec->frequency = frame->header.sample_rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
523
524
  }

525
526
527
528
529
530
531
532
533
534
535
536
537
  if (GST_PAD_IS_USABLE (flacdec->srcpad)) {
    outbuf = gst_buffer_new ();
    GST_BUFFER_SIZE (outbuf) = samples * channels * ((depth+7)>>3);
    GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
    GST_BUFFER_TIMESTAMP (outbuf) = flacdec->total_samples * GST_SECOND / frame->header.sample_rate;
  
    if (depth == 8) {
      guint8 *outbuffer = (guint8 *)GST_BUFFER_DATA (outbuf);
  
      for (i=0; i<samples; i++) {
        for (j=0; j < channels; j++) {
          *outbuffer++ = (guint8) buffer[j][i];
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
538
539
      }
    }
540
541
542
543
544
545
546
    else if (depth == 16) {
      guint16 *outbuffer = (guint16 *)GST_BUFFER_DATA (outbuf);
  
      for (i=0; i<samples; i++) {
        for (j=0; j < channels; j++) {
          *outbuffer++ = (guint16) buffer[j][i];
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
547
548
      }
    }
549
550
551
552
    else {
      g_warning ("flacdec: invalid depth %d found\n", depth);
      return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }
553
    gst_pad_push (flacdec->srcpad, GST_DATA (outbuf));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
554
  }
555
556
  flacdec->total_samples += samples;

557
  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
558
559
560
561
562
563
}

static void 
gst_flacdec_loop (GstElement *element) 
{
  FlacDec *flacdec;
564
  gboolean res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
565
566

  flacdec = GST_FLACDEC (element);
567

568
  GST_DEBUG ("flacdec: entering loop");
569
  if (flacdec->init) {
570
    GST_DEBUG ("flacdec: initializing decoder");
571
    FLAC__seekable_stream_decoder_init (flacdec->decoder);
572
    /* FLAC__seekable_stream_decoder_process_metadata (flacdec->decoder); */
573
574
575
576
    flacdec->init = FALSE;
  }

  if (flacdec->seek_pending) {
577
    GST_DEBUG ("perform seek to sample %" G_GINT64_FORMAT, 
Wim Taymans's avatar
Wim Taymans committed
578
		              flacdec->seek_value);
579

Wim Taymans's avatar
Wim Taymans committed
580
581
582
    if (FLAC__seekable_stream_decoder_seek_absolute (flacdec->decoder, 
			                             flacdec->seek_value)) 
    {
583
      flacdec->total_samples = flacdec->seek_value;
584
      GST_DEBUG ("seek done");
585
586
    }
    else {
587
      GST_DEBUG ("seek failed");
588
589
590
591
    }
    flacdec->seek_pending = FALSE;
  }

592
  GST_DEBUG ("flacdec: processing single");
Wim Taymans's avatar
Wim Taymans committed
593
  res = FLAC__seekable_stream_decoder_process_single (flacdec->decoder);
594
  GST_DEBUG ("flacdec: checking for EOS");
Wim Taymans's avatar
Wim Taymans committed
595
596
597
  if (FLAC__seekable_stream_decoder_get_state (flacdec->decoder) == 
		  FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) 
  {
598
599
    GstEvent *event;

600
    GST_DEBUG ("flacdec: sending EOS event");
601
602
603
    FLAC__seekable_stream_decoder_finish(flacdec->decoder);
    flacdec->init = TRUE;

604
605
    if (GST_PAD_IS_USABLE (flacdec->srcpad)) {
      event = gst_event_new (GST_EVENT_EOS);
606
      gst_pad_push (flacdec->srcpad, GST_DATA (event));
607
    }
608
609
    gst_element_set_eos (element);
  }
610
  GST_DEBUG ("flacdec: _loop end");
611
612
}

Wim Taymans's avatar
Wim Taymans committed
613
GST_PAD_FORMATS_FUNCTION (gst_flacdec_get_src_formats,
Wim Taymans's avatar
Wim Taymans committed
614
  GST_FORMAT_DEFAULT,
615
616
617
618
  GST_FORMAT_BYTES,
  GST_FORMAT_TIME
)

619
620
621
622
623
624
625
626
627
628
629
630
631
632
static gboolean
gst_flacdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value,
		         GstFormat *dest_format, gint64 *dest_value)
{
  gboolean res = TRUE;
  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));
  guint scale = 1;
  gint bytes_per_sample;

  bytes_per_sample = flacdec->channels * ((flacdec->depth+7)>>3);

  switch (src_format) {
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
Wim Taymans's avatar
Wim Taymans committed
633
        case GST_FORMAT_DEFAULT:
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
          if (bytes_per_sample == 0)
            return FALSE;
          *dest_value = src_value / bytes_per_sample;
          break;
        case GST_FORMAT_TIME:
	{
          gint byterate = bytes_per_sample * flacdec->frequency;

          if (byterate == 0)
            return FALSE;
          *dest_value = src_value * GST_SECOND / byterate;
          break;
	}
        default:
          res = FALSE;
      }
      break;
Wim Taymans's avatar
Wim Taymans committed
651
    case GST_FORMAT_DEFAULT:
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
	  *dest_value = src_value * bytes_per_sample;
          break;
        case GST_FORMAT_TIME:
	  if (flacdec->frequency == 0)
	    return FALSE;
          *dest_value = src_value * GST_SECOND / flacdec->frequency;
          break;
        default:
          res = FALSE;
      }
      break;
    case GST_FORMAT_TIME:
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
          scale = bytes_per_sample;
Wim Taymans's avatar
Wim Taymans committed
669
        case GST_FORMAT_DEFAULT:
670
671
672
673
674
675
676
677
678
679
680
681
	  *dest_value = src_value * scale * flacdec->frequency / GST_SECOND;
          break;
        default:
          res = FALSE;
      }
      break;
    default:
      res = FALSE;
  }
  return res;
}

682
GST_PAD_QUERY_TYPE_FUNCTION (gst_flacdec_get_src_query_types,
Wim Taymans's avatar
Wim Taymans committed
683
684
  GST_QUERY_TOTAL,
  GST_QUERY_POSITION
685
686
)

687
static gboolean
Wim Taymans's avatar
Wim Taymans committed
688
gst_flacdec_src_query (GstPad *pad, GstQueryType type,
689
690
691
692
693
694
	               GstFormat *format, gint64 *value)
{
  gboolean res = TRUE;
  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));

  switch (type) {
Wim Taymans's avatar
Wim Taymans committed
695
    case GST_QUERY_TOTAL:
696
697
698
699
700
701
702
703
    {
      guint64 samples;

      if (flacdec->stream_samples == 0)
        samples = flacdec->total_samples;
      else
        samples = flacdec->stream_samples;

Wim Taymans's avatar
Wim Taymans committed
704
      gst_pad_convert (flacdec->srcpad, 
Wim Taymans's avatar
Wim Taymans committed
705
		       GST_FORMAT_DEFAULT, 
Wim Taymans's avatar
Wim Taymans committed
706
707
		       samples, 
		       format, value);
708
709
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
710
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
711
      gst_pad_convert (flacdec->srcpad, 
Wim Taymans's avatar
Wim Taymans committed
712
		       GST_FORMAT_DEFAULT, 
Wim Taymans's avatar
Wim Taymans committed
713
714
		       flacdec->total_samples, 
		       format, value);
715
716
717
718
719
720
721
722
723
      break;
    default:
      res = FALSE;
      break;
  }

  return res;
}
	  
Wim Taymans's avatar
Wim Taymans committed
724
GST_PAD_EVENT_MASK_FUNCTION (gst_flacdec_get_src_event_masks,
725
726
727
    { GST_EVENT_SEEK, GST_SEEK_FLAG_ACCURATE }
);

728
729
730
731
732
733
734
735
736
static gboolean
gst_flacdec_src_event (GstPad *pad, GstEvent *event)
{ 
  gboolean res = TRUE;
  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));
  GstFormat format;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
Wim Taymans's avatar
Wim Taymans committed
737
      format = GST_FORMAT_DEFAULT;
738

Wim Taymans's avatar
Wim Taymans committed
739
740
741
      if (gst_pad_convert (flacdec->srcpad, 
			   GST_EVENT_SEEK_FORMAT (event), 
			   GST_EVENT_SEEK_OFFSET (event), 
742
743
744
745
746
747
748
749
750
			   &format, &flacdec->seek_value))
        flacdec->seek_pending = TRUE;
      else
	res = FALSE;
      break;
    default:
      res = FALSE;
      break;
  }
751
  gst_event_unref (event);
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
  return res;
}

static GstElementStateReturn
gst_flacdec_change_state (GstElement *element)
{
  FlacDec *flacdec = GST_FLACDEC (element);

  switch (GST_STATE_TRANSITION (element)) {
    case GST_STATE_NULL_TO_READY:
    case GST_STATE_READY_TO_PAUSED:
      flacdec->bs = gst_bytestream_new (flacdec->sinkpad);
      flacdec->seek_pending = FALSE;
      flacdec->total_samples = 0;
      flacdec->init = TRUE;
      flacdec->eos = FALSE;
Wim Taymans's avatar
Wim Taymans committed
768
      FLAC__seekable_stream_decoder_reset (flacdec->decoder);
769
770
771
772
773
774
775
776
777
778
779
      break;
    case GST_STATE_PAUSED_TO_PLAYING:
      flacdec->eos = FALSE;
    case GST_STATE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_PAUSED_TO_READY:
      gst_bytestream_destroy (flacdec->bs);
      break;
    case GST_STATE_READY_TO_NULL:
    default:
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
780
781
  }

782
783
784
785
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
786
787
}

788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
static void
gst_flacdec_set_property (GObject *object, guint prop_id, 
		             const GValue *value, GParamSpec *pspec)
{
  FlacDec *flacdec;
	      
  g_return_if_fail (GST_IS_FLACDEC (object));

  flacdec = GST_FLACDEC (object);

  switch (prop_id) {
    default:
      g_warning ("Unknown property id\n");
  }
}

static void 
gst_flacdec_get_property (GObject *object, guint prop_id, 
		             GValue *value, GParamSpec *pspec)
{
  FlacDec *flacdec;
	      
  g_return_if_fail (GST_IS_FLACDEC(object));

  flacdec = GST_FLACDEC (object);

  switch (prop_id) {
    case ARG_METADATA:
      g_value_set_boxed (value, flacdec->metadata);
      break;
    default:
      g_warning ("Unknown property id\n");
  }
}