gstwavparse.c 86.3 KB
Newer Older
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
Andy Wingo's avatar
Andy Wingo committed
2
/* GStreamer
Andy Wingo's avatar
Andy Wingo committed
3
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4
 * Copyright (C) <2006> Nokia Corporation, Stefan Kost <stefan.kost@nokia.com>.
Andy Wingo's avatar
Andy Wingo 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.
Andy Wingo's avatar
Andy Wingo committed
20
21
 */

22
23
24
/**
 * SECTION:element-wavparse
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
25
 * Parse a .wav file into raw or compressed audio.
26
 *
27
28
 * Wavparse supports both push and pull mode operations, making it possible to
 * stream from a network source.
29
30
 *
 * <refsect2>
31
 * <title>Example launch line</title>
32
 * |[
33
 * gst-launch-1.0 filesrc location=sine.wav ! wavparse ! audioconvert ! alsasink
34
 * ]| Read a wav file and output to the soundcard using the ALSA element. The
35
 * wav file is assumed to contain raw uncompressed samples.
36
 * |[
37
 * gst-launch-1.0 gnomevfssrc location=http://www.example.org/sine.wav ! queue ! wavparse ! audioconvert ! alsasink
38
 * ]| Stream data from a network url.
39
40
 * </refsect2>
 */
Andy Wingo's avatar
Andy Wingo committed
41

42
43
44
45
46
/*
 * TODO:
 * http://replaygain.hydrogenaudio.org/file_format_wav.html
 */

47
48
49
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
50

Andy Wingo's avatar
Andy Wingo committed
51
#include <string.h>
52
#include <math.h>
Andy Wingo's avatar
Andy Wingo committed
53

54
#include "gstwavparse.h"
55
#include "gst/riff/riff-media.h"
56
#include <gst/base/gsttypefindhelper.h>
57
#include <gst/pbutils/descriptions.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58
#include <gst/gst-i18n-plugin.h>
Andy Wingo's avatar
Andy Wingo committed
59

60
GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
61
62
#define GST_CAT_DEFAULT (wavparse_debug)

Peter G. Baum's avatar
Peter G. Baum committed
63
64
65
66
/* Data size chunk of RF64,
 * see http://tech.ebu.ch/docs/tech/tech3306-2009.pdf */
#define GST_RS64_TAG_DS64 GST_MAKE_FOURCC ('d','s','6','4')

67
static void gst_wavparse_dispose (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68

Wim Taymans's avatar
Wim Taymans committed
69
70
static gboolean gst_wavparse_sink_activate (GstPad * sinkpad,
    GstObject * parent);
Wim Taymans's avatar
Wim Taymans committed
71
72
static gboolean gst_wavparse_sink_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
73
74
static gboolean gst_wavparse_send_event (GstElement * element,
    GstEvent * event);
75
76
static GstStateChangeReturn gst_wavparse_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
77

Wim Taymans's avatar
Wim Taymans committed
78
79
80
static gboolean gst_wavparse_pad_query (GstPad * pad, GstObject * parent,
    GstQuery * query);
static gboolean gst_wavparse_pad_convert (GstPad * pad, GstFormat src_format,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
81
82
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);

Wim Taymans's avatar
Wim Taymans committed
83
84
85
86
static GstFlowReturn gst_wavparse_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buf);
static gboolean gst_wavparse_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
87
static void gst_wavparse_loop (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
88
89
static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
Andy Wingo's avatar
Andy Wingo committed
90

91
92
93
94
95
96
97
98
99
100
101
102
103
static void gst_wavparse_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_wavparse_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

#define DEFAULT_IGNORE_LENGTH FALSE

enum
{
  PROP_0,
  PROP_IGNORE_LENGTH,
};

David Schleef's avatar
David Schleef committed
104
static GstStaticPadTemplate sink_template_factory =
105
GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106
107
108
109
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-wav")
    );
Andy Wingo's avatar
Andy Wingo committed
110

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
111
#define DEBUG_INIT \
112
  GST_DEBUG_CATEGORY_INIT (wavparse_debug, "wavparse", 0, "WAV parser");
Andy Wingo's avatar
Andy Wingo committed
113

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
114
115
116
#define gst_wavparse_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWavParse, gst_wavparse, GST_TYPE_ELEMENT,
    DEBUG_INIT);
117

Anton Belka's avatar
Anton Belka committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
typedef struct
{
  /* Offset Size    Description   Value
   * 0x00   4       ID            unique identification value
   * 0x04   4       Position      play order position
   * 0x08   4       Data Chunk ID RIFF ID of corresponding data chunk
   * 0x0c   4       Chunk Start   Byte Offset of Data Chunk *
   * 0x10   4       Block Start   Byte Offset to sample of First Channel
   * 0x14   4       Sample Offset Byte Offset to sample byte of First Channel
   */
  guint32 id;
  guint32 position;
  guint32 data_chunk_id;
  guint32 chunk_start;
  guint32 block_start;
  guint32 sample_offset;
} GstWavParseCue;

typedef struct
{
  /* Offset Size    Description     Value
   * 0x08   4       Cue Point ID    0 - 0xFFFFFFFF
   * 0x0c           Text
   */
  guint32 cue_point_id;
  gchar *text;
144
} GstWavParseLabl, GstWavParseNote;
Anton Belka's avatar
Anton Belka committed
145

Andy Wingo's avatar
Andy Wingo committed
146
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
gst_wavparse_class_init (GstWavParseClass * klass)
Andy Wingo's avatar
Andy Wingo committed
148
149
{
  GstElementClass *gstelement_class;
Iain Holmes's avatar
Iain Holmes committed
150
  GObjectClass *object_class;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
151
  GstPadTemplate *src_template;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
152
153

  gstelement_class = (GstElementClass *) klass;
Iain Holmes's avatar
Iain Holmes committed
154
  object_class = (GObjectClass *) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155

156
  parent_class = g_type_class_peek_parent (klass);
157

158
  object_class->dispose = gst_wavparse_dispose;
159

160
161
162
163
  object_class->set_property = gst_wavparse_set_property;
  object_class->get_property = gst_wavparse_get_property;

  /**
164
   * GstWavParse:ignore-length:
165
   *
166
167
168
169
170
171
172
173
174
175
176
177
178
   * This selects whether the length found in a data chunk
   * should be ignored. This may be useful for streamed audio
   * where the length is unknown until the end of streaming,
   * and various software/hardware just puts some random value
   * in there and hopes it doesn't break too much.
   */
  g_object_class_install_property (object_class, PROP_IGNORE_LENGTH,
      g_param_spec_boolean ("ignore-length",
          "Ignore length",
          "Ignore length from the Wave header",
          DEFAULT_IGNORE_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
      );

179
  gstelement_class->change_state = gst_wavparse_change_state;
180
  gstelement_class->send_event = gst_wavparse_send_event;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
181
182

  /* register pads */
183
184
  gst_element_class_add_static_pad_template (gstelement_class,
      &sink_template_factory);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
185

186
187
  src_template = gst_pad_template_new ("src", GST_PAD_SRC,
      GST_PAD_ALWAYS, gst_riff_create_audio_template_caps ());
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
188
189
  gst_element_class_add_pad_template (gstelement_class, src_template);

190
  gst_element_class_set_static_metadata (gstelement_class, "WAV audio demuxer",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
191
192
193
      "Codec/Demuxer/Audio",
      "Parse a .wav file into raw audio",
      "Erik Walthinsen <omega@cse.ogi.edu>");
194
}
195

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
static void
gst_wavparse_notes_free (GstWavParseNote * note)
{
  if (note)
    g_free (note->text);
  g_free (note);
}

static void
gst_wavparse_labls_free (GstWavParseLabl * labl)
{
  if (labl)
    g_free (labl->text);
  g_free (labl);
}

212
static void
213
gst_wavparse_reset (GstWavParse * wav)
214
{
215
  wav->state = GST_WAVPARSE_START;
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  /* These will all be set correctly in the fmt chunk */
  wav->depth = 0;
  wav->rate = 0;
  wav->width = 0;
  wav->channels = 0;
  wav->blockalign = 0;
  wav->bps = 0;
  wav->fact = 0;
  wav->offset = 0;
  wav->end_offset = 0;
  wav->dataleft = 0;
  wav->datasize = 0;
  wav->datastart = 0;
  wav->duration = 0;
  wav->got_fmt = FALSE;
  wav->first = TRUE;

  if (wav->seek_event)
    gst_event_unref (wav->seek_event);
  wav->seek_event = NULL;
237
  if (wav->adapter) {
238
    gst_adapter_clear (wav->adapter);
239
    g_object_unref (wav->adapter);
240
241
    wav->adapter = NULL;
  }
242
  if (wav->tags)
243
    gst_tag_list_unref (wav->tags);
244
  wav->tags = NULL;
Anton Belka's avatar
Anton Belka committed
245
246
247
248
249
250
251
  if (wav->toc)
    gst_toc_unref (wav->toc);
  wav->toc = NULL;
  if (wav->cues)
    g_list_free_full (wav->cues, g_free);
  wav->cues = NULL;
  if (wav->labls)
252
    g_list_free_full (wav->labls, (GDestroyNotify) gst_wavparse_labls_free);
Anton Belka's avatar
Anton Belka committed
253
  wav->labls = NULL;
254
255
256
  if (wav->notes)
    g_list_free_full (wav->notes, (GDestroyNotify) gst_wavparse_notes_free);
  wav->notes = NULL;
257
258
259
260
261
262
  if (wav->caps)
    gst_caps_unref (wav->caps);
  wav->caps = NULL;
  if (wav->start_segment)
    gst_event_unref (wav->start_segment);
  wav->start_segment = NULL;
263
264
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
265
static void
266
gst_wavparse_dispose (GObject * object)
Andy Wingo's avatar
Andy Wingo committed
267
{
268
  GstWavParse *wav = GST_WAVPARSE (object);
269

270
271
272
273
  GST_DEBUG_OBJECT (wav, "WAV: Dispose");
  gst_wavparse_reset (wav);

  G_OBJECT_CLASS (parent_class)->dispose (object);
274
275
276
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
277
gst_wavparse_init (GstWavParse * wavparse)
278
{
279
280
  gst_wavparse_reset (wavparse);

281
282
  /* sink */
  wavparse->sinkpad =
283
      gst_pad_new_from_static_template (&sink_template_factory, "sink");
284
285
  gst_pad_set_activate_function (wavparse->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate));
Wim Taymans's avatar
Wim Taymans committed
286
287
  gst_pad_set_activatemode_function (wavparse->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate_mode));
288
289
  gst_pad_set_chain_function (wavparse->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_chain));
290
291
  gst_pad_set_event_function (wavparse->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_sink_event));
292
  gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->sinkpad);
293

294
295
296
297
298
299
300
301
302
303
  /* src */
  wavparse->srcpad =
      gst_pad_new_from_template (gst_element_class_get_pad_template
      (GST_ELEMENT_GET_CLASS (wavparse), "src"), "src");
  gst_pad_use_fixed_caps (wavparse->srcpad);
  gst_pad_set_query_function (wavparse->srcpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_pad_query));
  gst_pad_set_event_function (wavparse->srcpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_srcpad_event));
  gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->srcpad);
Andy Wingo's avatar
Andy Wingo committed
304
305
}

306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
static gboolean
gst_wavparse_parse_file_header (GstElement * element, GstBuffer * buf)
{
  guint32 doctype;

  if (!gst_riff_parse_file_header (element, buf, &doctype))
    return FALSE;

  if (doctype != GST_RIFF_RIFF_WAVE)
    goto not_wav;

  return TRUE;

  /* ERRORS */
not_wav:
  {
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
323
        ("File is not a WAVE file: 0x%" G_GINT32_MODIFIER "x", doctype));
324
325
326
327
328
329
330
331
332
333
334
335
336
    return FALSE;
  }
}

static GstFlowReturn
gst_wavparse_stream_init (GstWavParse * wav)
{
  GstFlowReturn res;
  GstBuffer *buf = NULL;

  if ((res = gst_pad_pull_range (wav->sinkpad,
              wav->offset, 12, &buf)) != GST_FLOW_OK)
    return res;
337
  else if (!gst_wavparse_parse_file_header (GST_ELEMENT_CAST (wav), buf))
338
339
340
341
342
343
344
    return GST_FLOW_ERROR;

  wav->offset += 12;

  return GST_FLOW_OK;
}

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
static gboolean
gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos)
{
  /* -1 always maps to -1 */
  if (ts == -1) {
    *bytepos = -1;
    return TRUE;
  }

  /* 0 always maps to 0 */
  if (ts == 0) {
    *bytepos = 0;
    return TRUE;
  }

  if (wav->bps > 0) {
361
    *bytepos = gst_util_uint64_scale_ceil (ts, (guint64) wav->bps, GST_SECOND);
362
363
    return TRUE;
  } else if (wav->fact) {
364
    guint64 bps = gst_util_uint64_scale (wav->datasize, wav->rate, wav->fact);
365
    *bytepos = gst_util_uint64_scale_ceil (ts, bps, GST_SECOND);
366
367
368
369
370
371
372
    return TRUE;
  }

  return FALSE;
}

/* This function is used to perform seeks on the element.
373
374
375
376
377
378
 *
 * It also works when event is NULL, in which case it will just
 * start from the last configured segment. This technique is
 * used when activating the element and to perform the seek in
 * READY.
 */
379
static gboolean
380
gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
381
{
382
383
  gboolean res;
  gdouble rate;
384
  GstFormat format, bformat;
385
  GstSeekFlags flags;
386
  GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
387
  gint64 cur, stop, upstream_size;
388
  gboolean flush;
389
  gboolean update;
390
  GstSegment seeksegment = { 0, };
391
  gint64 last_stop;
392
  guint32 seqnum = 0;
393
394

  if (event) {
395
396
    GST_DEBUG_OBJECT (wav, "doing seek with event");

397
398
    gst_event_parse_seek (event, &rate, &format, &flags,
        &cur_type, &cur, &stop_type, &stop);
399
    seqnum = gst_event_get_seqnum (event);
400

401
402
403
404
405
    /* no negative rates yet */
    if (rate < 0.0)
      goto negative_rate;

    if (format != wav->segment.format) {
406
407
408
      GST_INFO_OBJECT (wav, "converting seek-event from %s to %s",
          gst_format_get_name (format),
          gst_format_get_name (wav->segment.format));
409
410
      res = TRUE;
      if (cur_type != GST_SEEK_TYPE_NONE)
411
412
        res =
            gst_pad_query_convert (wav->srcpad, format, cur,
413
            wav->segment.format, &cur);
414
      if (res && stop_type != GST_SEEK_TYPE_NONE)
415
416
        res =
            gst_pad_query_convert (wav->srcpad, format, stop,
417
            wav->segment.format, &stop);
418
419
420
      if (!res)
        goto no_format;

421
      format = wav->segment.format;
422
423
    }
  } else {
424
    GST_DEBUG_OBJECT (wav, "doing seek without event");
425
    flags = 0;
426
    rate = 1.0;
427
428
    cur_type = GST_SEEK_TYPE_SET;
    stop_type = GST_SEEK_TYPE_SET;
429
  }
430

431
432
433
434
435
436
437
438
  /* in push mode, we must delegate to upstream */
  if (wav->streaming) {
    gboolean res = FALSE;

    /* if streaming not yet started; only prepare initial newsegment */
    if (!event || wav->state != GST_WAVPARSE_DATA) {
      if (wav->start_segment)
        gst_event_unref (wav->start_segment);
439
      wav->start_segment = gst_event_new_segment (&wav->segment);
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
      res = TRUE;
    } else {
      /* convert seek positions to byte positions in data sections */
      if (format == GST_FORMAT_TIME) {
        /* should not fail */
        if (!gst_wavparse_time_to_bytepos (wav, cur, &cur))
          goto no_position;
        if (!gst_wavparse_time_to_bytepos (wav, stop, &stop))
          goto no_position;
      }
      /* mind sample boundary and header */
      if (cur >= 0) {
        cur -= (cur % wav->bytes_per_sample);
        cur += wav->datastart;
      }
      if (stop >= 0) {
        stop -= (stop % wav->bytes_per_sample);
        stop += wav->datastart;
      }
      GST_DEBUG_OBJECT (wav, "Pushing BYTE seek rate %g, "
          "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur,
          stop);
      /* BYTE seek event */
      event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur,
          stop_type, stop);
465
      gst_event_set_seqnum (event, seqnum);
466
467
468
469
470
      res = gst_pad_push_event (wav->sinkpad, event);
    }
    return res;
  }

471
  /* get flush flag */
472
  flush = flags & GST_SEEK_FLAG_FLUSH;
473

474
475
476
477
478
479
480
  /* now we need to make sure the streaming thread is stopped. We do this by
   * either sending a FLUSH_START event downstream which will cause the
   * streaming thread to stop with a WRONG_STATE.
   * For a non-flushing seek we simply pause the task, which will happen as soon
   * as it completes one iteration (and thus might block when the sink is
   * blocking in preroll). */
  if (flush) {
481
    GstEvent *fevent;
Wim Taymans's avatar
Wim Taymans committed
482
    GST_DEBUG_OBJECT (wav, "sending flush start");
483
484
485
486
487

    fevent = gst_event_new_flush_start ();
    gst_event_set_seqnum (fevent, seqnum);
    gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
    gst_pad_push_event (wav->srcpad, fevent);
488
  } else {
489
    gst_pad_pause_task (wav->sinkpad);
490
  }
491

492
493
  /* we should now be able to grab the streaming thread because we stopped it
   * with the above flush/pause code */
494
  GST_PAD_STREAM_LOCK (wav->sinkpad);
495

496
  /* save current position */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
497
  last_stop = wav->segment.position;
498
499
500

  GST_DEBUG_OBJECT (wav, "stopped streaming at %" G_GINT64_FORMAT, last_stop);

501
502
503
504
  /* copy segment, we need this because we still need the old
   * segment when we close the current segment. */
  memcpy (&seeksegment, &wav->segment, sizeof (GstSegment));

505
506
  /* configure the seek parameters in the seeksegment. We will then have the
   * right values in the segment to perform the seek */
507
  if (event) {
508
    GST_DEBUG_OBJECT (wav, "configuring seek");
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
509
    gst_segment_do_seek (&seeksegment, rate, format, flags,
510
511
512
        cur_type, cur, stop_type, stop, &update);
  }

513
514
515
  /* figure out the last position we need to play. If it's configured (stop !=
   * -1), use that, else we play until the total duration of the file */
  if ((stop = seeksegment.stop) == -1)
516
517
    stop = seeksegment.duration;

518
  GST_DEBUG_OBJECT (wav, "cur_type =%d", cur_type);
519
  if ((cur_type != GST_SEEK_TYPE_NONE)) {
520
521
522
    /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and
     * we can just copy the last_stop. If not, we use the bps to convert TIME to
     * bytes. */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
523
    if (!gst_wavparse_time_to_bytepos (wav, seeksegment.position,
524
            (gint64 *) & wav->offset))
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
525
      wav->offset = seeksegment.position;
526
527
528
    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
    wav->offset -= (wav->offset % wav->bytes_per_sample);
    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
529
    wav->offset += wav->datastart;
530
531
    GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
  } else {
532
533
    GST_LOG_OBJECT (wav, "continue from offset=%" G_GUINT64_FORMAT,
        wav->offset);
534
535
  }

536
  if (stop_type != GST_SEEK_TYPE_NONE) {
537
    if (!gst_wavparse_time_to_bytepos (wav, stop, (gint64 *) & wav->end_offset))
538
      wav->end_offset = stop;
539
540
541
    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
    wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
542
    wav->end_offset += wav->datastart;
543
    GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
544
  } else {
545
546
    GST_LOG_OBJECT (wav, "continue to end_offset=%" G_GUINT64_FORMAT,
        wav->end_offset);
547
  }
548
549
550
551

  /* make sure filesize is not exceeded due to rounding errors or so,
   * same precaution as in _stream_headers */
  bformat = GST_FORMAT_BYTES;
552
  if (gst_pad_peer_query_duration (wav->sinkpad, bformat, &upstream_size))
553
554
    wav->end_offset = MIN (wav->end_offset, upstream_size);

555
556
557
  if (wav->datasize > 0 && wav->end_offset > wav->datastart + wav->datasize)
    wav->end_offset = wav->datastart + wav->datasize;

558
  /* this is the range of bytes we will use for playback */
559
560
  wav->offset = MIN (wav->offset, wav->end_offset);
  wav->dataleft = wav->end_offset - wav->offset;
561

562
  GST_DEBUG_OBJECT (wav,
563
564
565
      "seek: rate %lf, offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT
      ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, wav->offset,
      wav->end_offset, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop));
566

567
  /* prepare for streaming again */
Wim Taymans's avatar
Wim Taymans committed
568
  if (flush) {
569
570
    GstEvent *fevent;

Wim Taymans's avatar
Wim Taymans committed
571
572
    /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
    GST_DEBUG_OBJECT (wav, "sending flush stop");
573
574
575
576
577

    fevent = gst_event_new_flush_stop (TRUE);
    gst_event_set_seqnum (fevent, seqnum);
    gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
    gst_pad_push_event (wav->srcpad, fevent);
578
  }
579

580
  /* now we did the seek and can activate the new segment values */
581
582
  memcpy (&wav->segment, &seeksegment, sizeof (GstSegment));

583
  /* if we're doing a segment seek, post a SEGMENT_START message */
584
  if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) {
585
586
    gst_element_post_message (GST_ELEMENT_CAST (wav),
        gst_message_new_segment_start (GST_OBJECT_CAST (wav),
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
587
            wav->segment.format, wav->segment.position));
588
  }
589

590
591
  /* now create the newsegment */
  GST_DEBUG_OBJECT (wav, "Creating newsegment from %" G_GINT64_FORMAT
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
592
      " to %" G_GINT64_FORMAT, wav->segment.position, stop);
593

594
  /* store the newsegment event so it can be sent from the streaming thread. */
595
596
  if (wav->start_segment)
    gst_event_unref (wav->start_segment);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
597
  wav->start_segment = gst_event_new_segment (&wav->segment);
598
  gst_event_set_seqnum (wav->start_segment, seqnum);
599

600
  /* mark discont if we are going to stream from another position. */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
601
  if (last_stop != wav->segment.position) {
602
603
604
    GST_DEBUG_OBJECT (wav, "mark DISCONT, we did a seek to another position");
    wav->discont = TRUE;
  }
605

606
  /* and start the streaming task again */
607
608
  if (!wav->streaming) {
    gst_pad_start_task (wav->sinkpad, (GstTaskFunction) gst_wavparse_loop,
Wim Taymans's avatar
Wim Taymans committed
609
        wav->sinkpad, NULL);
610
  }
611

612
  GST_PAD_STREAM_UNLOCK (wav->sinkpad);
613
614
615

  return TRUE;

616
  /* ERRORS */
617
618
619
620
621
negative_rate:
  {
    GST_DEBUG_OBJECT (wav, "negative playback rates are not supported yet.");
    return FALSE;
  }
622
623
624
625
626
no_format:
  {
    GST_DEBUG_OBJECT (wav, "unsupported format given, seek aborted.");
    return FALSE;
  }
627
628
629
630
631
632
no_position:
  {
    GST_DEBUG_OBJECT (wav,
        "Could not determine byte position for desired time");
    return FALSE;
  }
633
634
}

635
636
637
638
639
/*
 * gst_wavparse_peek_chunk_info:
 * @wav Wavparse object
 * @tag holder for tag
 * @size holder for tag size
640
641
 *
 * Peek next chunk info (tag and size)
642
 *
643
 * Returns: %TRUE when the chunk info (header) is available
644
645
646
647
648
649
 */
static gboolean
gst_wavparse_peek_chunk_info (GstWavParse * wav, guint32 * tag, guint32 * size)
{
  const guint8 *data = NULL;

650
  if (gst_adapter_available (wav->adapter) < 8)
651
652
    return FALSE;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
653
  data = gst_adapter_map (wav->adapter, 8);
654
655
  *tag = GST_READ_UINT32_LE (data);
  *size = GST_READ_UINT32_LE (data + 4);
Wim Taymans's avatar
Wim Taymans committed
656
  gst_adapter_unmap (wav->adapter);
657

658
  GST_DEBUG ("Next chunk size is %u bytes, type %" GST_FOURCC_FORMAT, *size,
659
660
      GST_FOURCC_ARGS (*tag));

661
662
663
664
665
666
667
668
669
670
671
  return TRUE;
}

/*
 * gst_wavparse_peek_chunk:
 * @wav Wavparse object
 * @tag holder for tag
 * @size holder for tag size
 *
 * Peek enough data for one full chunk
 *
672
 * Returns: %TRUE when the full chunk is available
673
674
675
676
677
 */
static gboolean
gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
{
  guint32 peek_size = 0;
678
679
680
681
  guint available;

  if (!gst_wavparse_peek_chunk_info (wav, tag, size))
    return FALSE;
682

683
684
685
686
687
  /* size 0 -> empty data buffer would surprise most callers,
   * large size -> do not bother trying to squeeze that into adapter,
   * so we throw poor man's exception, which can be caught if caller really
   * wants to handle 0 size chunk */
  if (!(*size) || (*size) >= (1 << 30)) {
688
    GST_INFO ("Invalid/unexpected chunk size %u for tag %" GST_FOURCC_FORMAT,
689
690
691
692
693
        *size, GST_FOURCC_ARGS (*tag));
    /* chain should give up */
    wav->abort_buffering = TRUE;
    return FALSE;
  }
694
  peek_size = (*size + 1) & ~1;
695
  available = gst_adapter_available (wav->adapter);
696

697
  if (available >= (8 + peek_size)) {
698
699
    return TRUE;
  } else {
700
    GST_LOG ("but only %u bytes available now", available);
701
702
703
704
    return FALSE;
  }
}

705
706
/*
 * gst_wavparse_calculate_duration:
707
 * @wav: wavparse object
708
709
710
711
712
713
 *
 * Calculate duration on demand and store in @wav. Prefer bps, but use fact as a
 * fallback.
 *
 * Returns: %TRUE if duration is available.
 */
714
static gboolean
715
gst_wavparse_calculate_duration (GstWavParse * wav)
716
{
717
718
  if (wav->duration > 0)
    return TRUE;
719

720
  if (wav->bps > 0) {
721
    GST_INFO_OBJECT (wav, "Got datasize %" G_GUINT64_FORMAT, wav->datasize);
722
    wav->duration =
723
724
        gst_util_uint64_scale_ceil (wav->datasize, GST_SECOND,
        (guint64) wav->bps);
725
726
727
728
    GST_INFO_OBJECT (wav, "Got duration (bps) %" GST_TIME_FORMAT,
        GST_TIME_ARGS (wav->duration));
    return TRUE;
  } else if (wav->fact) {
729
    wav->duration =
730
        gst_util_uint64_scale_ceil (GST_SECOND, wav->fact, wav->rate);
731
732
733
    GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
        GST_TIME_ARGS (wav->duration));
    return TRUE;
734
  }
735
  return FALSE;
736
}
737

738
static gboolean
739
740
741
742
743
744
745
gst_waveparse_ignore_chunk (GstWavParse * wav, GstBuffer * buf, guint32 tag,
    guint32 size)
{
  guint flush;

  if (wav->streaming) {
    if (!gst_wavparse_peek_chunk (wav, &tag, &size))
746
      return FALSE;
747
748
749
750
751
752
753
754
755
756
  }
  GST_DEBUG_OBJECT (wav, "Ignoring tag %" GST_FOURCC_FORMAT,
      GST_FOURCC_ARGS (tag));
  flush = 8 + ((size + 1) & ~1);
  wav->offset += flush;
  if (wav->streaming) {
    gst_adapter_flush (wav->adapter, flush);
  } else {
    gst_buffer_unref (buf);
  }
757
758

  return TRUE;
759
760
}

Anton Belka's avatar
Anton Belka committed
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
/*
 * gst_wavparse_cue_chunk:
 * @wav GstWavParse object
 * @data holder for data
 * @size holder for data size
 *
 * Parse cue chunk from @data to wav->cues.
 *
 * Returns: %TRUE when cue chunk is available
 */
static gboolean
gst_wavparse_cue_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
{
  guint32 i, ncues;
  GList *cues = NULL;
  GstWavParseCue *cue;

  if (wav->cues) {
    GST_WARNING_OBJECT (wav, "found another cue's");
    return TRUE;
  }

  ncues = GST_READ_UINT32_LE (data);

785
786
  if (size < 4 + ncues * 24) {
    GST_WARNING_OBJECT (wav, "broken file %d %d", size, ncues);
Anton Belka's avatar
Anton Belka committed
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
822
823
    return FALSE;
  }

  /* parse data */
  data += 4;
  for (i = 0; i < ncues; i++) {
    cue = g_new0 (GstWavParseCue, 1);
    cue->id = GST_READ_UINT32_LE (data);
    cue->position = GST_READ_UINT32_LE (data + 4);
    cue->data_chunk_id = GST_READ_UINT32_LE (data + 8);
    cue->chunk_start = GST_READ_UINT32_LE (data + 12);
    cue->block_start = GST_READ_UINT32_LE (data + 16);
    cue->sample_offset = GST_READ_UINT32_LE (data + 20);
    cues = g_list_append (cues, cue);
    data += 24;
  }

  wav->cues = cues;

  return TRUE;
}

/*
 * gst_wavparse_labl_chunk:
 * @wav GstWavParse object
 * @data holder for data
 * @size holder for data size
 *
 * Parse labl from @data to wav->labls.
 *
 * Returns: %TRUE when labl chunk is available
 */
static gboolean
gst_wavparse_labl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
{
  GstWavParseLabl *labl;

824
825
826
  if (size < 5)
    return FALSE;

Anton Belka's avatar
Anton Belka committed
827
828
829
  labl = g_new0 (GstWavParseLabl, 1);

  /* parse data */
830
831
  data += 8;
  labl->cue_point_id = GST_READ_UINT32_LE (data);
832
  labl->text = g_memdup (data + 4, size - 4);
Anton Belka's avatar
Anton Belka committed
833
834
835
836
837
838

  wav->labls = g_list_append (wav->labls, labl);

  return TRUE;
}

839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
/*
 * gst_wavparse_note_chunk:
 * @wav GstWavParse object
 * @data holder for data
 * @size holder for data size
 *
 * Parse note from @data to wav->notes.
 *
 * Returns: %TRUE when note chunk is available
 */
static gboolean
gst_wavparse_note_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
{
  GstWavParseNote *note;

  if (size < 5)
    return FALSE;

  note = g_new0 (GstWavParseNote, 1);

  /* parse data */
  data += 8;
  note->cue_point_id = GST_READ_UINT32_LE (data);
  note->text = g_memdup (data + 4, size - 4);

  wav->notes = g_list_append (wav->notes, note);

  return TRUE;
}

869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
/*
 * gst_wavparse_smpl_chunk:
 * @wav GstWavParse object
 * @data holder for data
 * @size holder for data size
 *
 * Parse smpl chunk from @data.
 *
 * Returns: %TRUE when cue chunk is available
 */
static gboolean
gst_wavparse_smpl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
{
  guint32 note_number;

  /*
     manufacturer_id = GST_READ_UINT32_LE (data);
     product_id = GST_READ_UINT32_LE (data + 4);
     sample_period = GST_READ_UINT32_LE (data + 8);
   */
  note_number = GST_READ_UINT32_LE (data + 12);
  /*
     pitch_fraction = GST_READ_UINT32_LE (data + 16);
     SMPTE_format = GST_READ_UINT32_LE (data + 20);
     SMPTE_offset = GST_READ_UINT32_LE (data + 24);
     num_sample_loops = GST_READ_UINT32_LE (data + 28);
     List of Sample Loops, 24 bytes each
   */

  if (!wav->tags)
    wav->tags = gst_tag_list_new_empty ();
  gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
      GST_TAG_MIDI_BASE_NOTE, (guint) note_number, NULL);
  return TRUE;
}

Anton Belka's avatar
Anton Belka committed
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
/*
 * gst_wavparse_adtl_chunk:
 * @wav GstWavParse object
 * @data holder for data
 * @size holder for data size
 *
 * Parse adtl from @data.
 *
 * Returns: %TRUE when adtl chunk is available
 */
static gboolean
gst_wavparse_adtl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
{
  guint32 ltag, lsize, offset = 0;

  while (size >= 8) {
    ltag = GST_READ_UINT32_LE (data + offset);
    lsize = GST_READ_UINT32_LE (data + offset + 4);
923
924
925
926
927
928

    if (lsize + 8 > size) {
      GST_WARNING_OBJECT (wav, "Invalid adtl size: %u + 8 > %u", lsize, size);
      return FALSE;
    }

Anton Belka's avatar
Anton Belka committed
929
930
931
    switch (ltag) {
      case GST_RIFF_TAG_labl:
        gst_wavparse_labl_chunk (wav, data + offset, size);
932
933
934
935
        break;
      case GST_RIFF_TAG_note:
        gst_wavparse_note_chunk (wav, data + offset, size);
        break;
Anton Belka's avatar
Anton Belka committed
936
      default:
937
938
939
        GST_WARNING_OBJECT (wav, "Unknowm adtl %" GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (ltag));
        GST_MEMDUMP_OBJECT (wav, "Unknowm adtl", &data[offset], lsize);
Anton Belka's avatar
Anton Belka committed
940
941
942
943
944
945
946
947
948
        break;
    }
    offset += 8 + GST_ROUND_UP_2 (lsize);
    size -= 8 + GST_ROUND_UP_2 (lsize);
  }

  return TRUE;
}

949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
static GstTagList *
gst_wavparse_get_tags_toc_entry (GstToc * toc, gchar * id)
{
  GstTagList *tags = NULL;
  GstTocEntry *entry = NULL;

  entry = gst_toc_find_entry (toc, id);
  if (entry != NULL) {
    tags = gst_toc_entry_get_tags (entry);
    if (tags == NULL) {
      tags = gst_tag_list_new_empty ();
      gst_toc_entry_set_tags (entry, tags);
    }
  }

  return tags;
}

Anton Belka's avatar
Anton Belka committed
967
968
969
970
971
972
973
974
975
976
977
978
979
980
/*
 * gst_wavparse_create_toc:
 * @wav GstWavParse object
 *
 * Create TOC from wav->cues and wav->labls.
 */
static gboolean
gst_wavparse_create_toc (GstWavParse * wav)
{
  gint64 start, stop;
  gchar *id;
  GList *list;
  GstWavParseCue *cue;
  GstWavParseLabl *labl;
981
  GstWavParseNote *note;
Anton Belka's avatar
Anton Belka committed
982
983
984
985
986
987
988
989
990
991
992
  GstTagList *tags;
  GstToc *toc;
  GstTocEntry *entry = NULL, *cur_subentry = NULL, *prev_subentry = NULL;

  GST_OBJECT_LOCK (wav);
  if (wav->toc) {
    GST_OBJECT_UNLOCK (wav);
    GST_WARNING_OBJECT (wav, "found another TOC");
    return FALSE;
  }

Anton Belka's avatar
Anton Belka committed
993
994
995
996
997
  if (!wav->cues) {
    GST_OBJECT_UNLOCK (wav);
    return TRUE;
  }

998
999
  /* FIXME: send CURRENT scope toc too */
  toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
Anton Belka's avatar
Anton Belka committed
1000
1001
1002
1003
1004
1005

  /* add cue edition */
  entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "cue");
  gst_toc_entry_set_start_stop_times (entry, 0, wav->duration);
  gst_toc_append_entry (toc, entry);

1006
  /* add tracks in cue edition */
1007
  list = wav->cues;
Anton Belka's avatar
Anton Belka committed
1008
  while (list) {
Anton Belka's avatar
Anton Belka committed
1009
1010
    cue = list->data;
    prev_subentry = cur_subentry;
1011
    /* previous track stop time = current track start time */
Anton Belka's avatar
Anton Belka committed
1012
1013
1014
1015
1016
1017
    if (prev_subentry != NULL) {
      gst_toc_entry_get_start_stop_times (prev_subentry, &start, NULL);
      stop = gst_util_uint64_scale_round (cue->position, GST_SECOND, wav->rate);
      gst_toc_entry_set_start_stop_times (prev_subentry, start, stop);
    }
    id = g_strdup_printf ("%08x", cue->id);
1018
    cur_subentry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_TRACK, id);
Anton Belka's avatar
Anton Belka committed
1019
1020
1021
1022
1023
1024
1025
1026
    g_free (id);
    start = gst_util_uint64_scale_round (cue->position, GST_SECOND, wav->rate);
    stop = wav->duration;
    gst_toc_entry_set_start_stop_times (cur_subentry, start, stop);
    gst_toc_entry_append_sub_entry (entry, cur_subentry);
    list = g_list_next (list);
  }

1027
  /* add tags in tracks */
1028
  list = wav->labls;
Anton Belka's avatar
Anton Belka committed
1029
  while (list) {
Anton Belka's avatar
Anton Belka committed
1030
1031
    labl = list->data;
    id = g_strdup_printf ("%08x", labl->cue_point_id);
1032
    tags = gst_wavparse_get_tags_toc_entry (toc, id);
Anton Belka's avatar
Anton Belka committed
1033
    g_free (id);
1034
    if (tags != NULL) {
Anton Belka's avatar
Anton Belka committed
1035
1036
      gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, labl->text,
          NULL);
1037
1038
1039
    }
    list = g_list_next (list);
  }
1040
  list = wav->notes;
1041
1042
1043
1044
1045
1046
1047
1048
  while (list) {
    note = list->data;
    id = g_strdup_printf ("%08x", note->cue_point_id);
    tags = gst_wavparse_get_tags_toc_entry (toc, id);
    g_free (id);
    if (tags != NULL) {
      gst_tag_list_add (tags, GST_TAG_MERGE_PREPEND, GST_TAG_COMMENT,
          note->text, NULL);
Anton Belka's avatar
Anton Belka committed
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
    }
    list = g_list_next (list);
  }

  /* send data as TOC */
  wav->toc = toc;

  /* send TOC event */
  if (wav->toc) {
    GST_OBJECT_UNLOCK (wav);
    gst_pad_push_event (wav->srcpad, gst_event_new_toc (wav->toc, FALSE));
  }

  return TRUE;
}

1065
1066
#define MAX_BUFFER_SIZE 4096

Peter G. Baum's avatar
Peter G. Baum committed
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
static gboolean
parse_ds64 (GstWavParse * wav, GstBuffer * buf)
{
  GstMapInfo map;
  guint32 dataSizeLow, dataSizeHigh;
  guint32 sampleCountLow, sampleCountHigh;

  gst_buffer_map (buf, &map, GST_MAP_READ);
  dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
  dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
  sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
  sampleCountHigh = GST_READ_UINT32_LE (map.data + 5 * 4);
  gst_buffer_unmap (buf, &map);
  if (dataSizeHigh != 0xFFFFFFFF && dataSizeLow != 0xFFFFFFFF) {
    wav->datasize = ((guint64) dataSizeHigh << 32) | dataSizeLow;
  }
  if (sampleCountHigh != 0xFFFFFFFF && sampleCountLow != 0xFFFFFFFF) {
    wav->fact = ((guint64) sampleCountHigh << 32) | sampleCountLow;
  }

  GST_DEBUG_OBJECT (wav, "Got 'ds64' TAG, datasize : %" G_GINT64_FORMAT
      " fact: %" G_GINT64_FORMAT, wav->datasize, wav->fact);
  return TRUE;
}

1092
1093
1094
static GstFlowReturn
gst_wavparse_stream_headers (GstWavParse * wav)
{
1095
  GstFlowReturn res = GST_FLOW_OK;
1096
  GstBuffer *buf = NULL;
1097
  gst_riff_strf_auds *header = NULL;
1098
  guint32 tag, size;
1099
  gboolean gotdata = FALSE;
1100
  GstCaps *caps = NULL;
1101
  gchar *codec_name = NULL;
1102
  gint64 upstream_size = 0;
1103
  GstStructure *s;
1104

1105
  /* search for "_fmt" chunk, which must be before "data" */
1106
  while (!wav->got_fmt) {
1107
    GstBuffer *extra;
1108

1109
1110
    if (wav->streaming) {
      if (!gst_wavparse_peek_chunk (wav, &tag, &size))
1111
        return res;
1112

1113
1114
      gst_adapter_flush (wav->adapter, 8);
      wav->offset += 8;
1115

1116
1117
1118
1119
1120
1121
1122
1123
      if (size) {
        buf = gst_adapter_take_buffer (wav->adapter, size);
        if (size & 1)
          gst_adapter_flush (wav->adapter, 1);
        wav->offset += GST_ROUND_UP_2 (size);
      } else {
        buf = gst_buffer_new ();
      }
1124
    } else {
1125
      if ((res = gst_riff_read_chunk (GST_ELEMENT_CAST (wav), wav->sinkpad,
1126
1127
1128
                  &wav->offset, &tag, &buf)) != GST_FLOW_OK)
        return res;
    }
1129

Peter G. Baum's avatar
Peter G. Baum committed
1130
1131
1132
1133
1134
1135
1136
    if (tag == GST_RS64_TAG_DS64) {
      if (!parse_ds64 (wav, buf))
        goto fail;
      else
        continue;
    }

1137
1138
1139
1140
1141
1142
1143
    if (tag != GST_RIFF_TAG_fmt) {
      GST_DEBUG_OBJECT (wav, "skipping %" GST_FOURCC_FORMAT " chunk",
          GST_FOURCC_ARGS (tag));
      gst_buffer_unref (buf);
      buf = NULL;
      continue;
    }
1144

1145
1146
    if (!(gst_riff_parse_strf_auds (GST_ELEMENT_CAST (wav), buf, &header,
                &extra)))
1147
      goto parse_header_error;
1148

1149
    buf = NULL;                 /* parse_strf_auds() took ownership of buffer */
1150

1151
    /* do sanity checks of header fields */
1152
1153
    if (header->channels == 0)
      goto no_channels;
Stefan Kost's avatar