gstwavparse.c 40.2 KB
Newer Older
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2
/* GStreamer
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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.
 */


22 23 24
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
25 26
#include <string.h>

27
#include "gstwavparse.h"
28 29
#include "gst/riff/riff-ids.h"
#include "gst/riff/riff-media.h"
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
30
#include <gst/gst-i18n-plugin.h>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
31

32 33 34 35
#ifndef G_MAXUINT32
#define G_MAXUINT32 0xffffffff
#endif

36
GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
37 38
#define GST_CAT_DEFAULT (wavparse_debug)

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
39 40 41 42
static void gst_wavparse_base_init (gpointer g_class);
static void gst_wavparse_class_init (GstWavParseClass * klass);
static void gst_wavparse_init (GstWavParse * wavparse);

43 44 45
static gboolean gst_wavparse_sink_activate (GstPad * sinkpad);
static gboolean gst_wavparse_sink_activate_pull (GstPad * sinkpad,
    gboolean active);
46 47
static GstStateChangeReturn gst_wavparse_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48

49
static gboolean gst_wavparse_pad_query (GstPad * pad, GstQuery * query);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50 51 52 53 54
static const GstQueryType *gst_wavparse_get_query_types (GstPad * pad);
static gboolean gst_wavparse_pad_convert (GstPad * pad,
    GstFormat src_format,
    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);

55
static void gst_wavparse_loop (GstPad * pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
56 57 58
static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstEvent * event);
static void gst_wavparse_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
59

David Schleef's avatar
David Schleef committed
60
static GstStaticPadTemplate sink_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61 62 63 64 65
GST_STATIC_PAD_TEMPLATE ("wavparse_sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-wav")
    );
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
66

David Schleef's avatar
David Schleef committed
67
static GstStaticPadTemplate src_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69
    GST_STATIC_PAD_TEMPLATE ("wavparse_src",
    GST_PAD_SRC,
70
    GST_PAD_SOMETIMES,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
71
    GST_STATIC_CAPS ("audio/x-raw-int, "
72 73
        "endianness = (int) little_endian, "
        "signed = (boolean) { true, false }, "
74 75
        "width = (int) { 8, 16, 24, 32 }, "
        "depth = (int) { 8, 16, 24, 32 }, "
76
        "rate = (int) [ 8000, 96000 ], "
77
        "channels = (int) [ 1, 8 ]; "
78 79 80 81 82 83 84 85 86
        "audio/mpeg, "
        "mpegversion = (int) 1, "
        "layer = (int) [ 1, 3 ], "
        "rate = (int) [ 8000, 48000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/x-alaw, "
        "rate = (int) [ 8000, 48000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/x-mulaw, "
87 88 89 90
        "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ];"
        "audio/x-adpcm, "
        "layout = (string) microsoft, "
        "block_align = (int) [ 1, 8192 ], "
91 92 93 94 95
        "rate = (int) [ 8000, 48000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/x-adpcm, "
        "layout = (string) dvi, "
        "block_align = (int) [ 1, 8192 ], "
96
        "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ];"
97 98
        "audio/x-vnd.sony.atrac3;"
        "audio/x-wma, " "wmaversion = (int) [ 1, 2 ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
99
    );
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
100 101 102


static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
103

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105
gst_wavparse_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
106
{
107
  static GType wavparse_type = 0;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
108

109 110
  if (!wavparse_type) {
    static const GTypeInfo wavparse_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
111
      sizeof (GstWavParseClass),
Benjamin Otte's avatar
Benjamin Otte committed
112
      gst_wavparse_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
113
      NULL,
114
      (GClassInitFunc) gst_wavparse_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
115 116
      NULL,
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
117
      sizeof (GstWavParse),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
118
      0,
119
      (GInstanceInitFunc) gst_wavparse_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
120
    };
121

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
    wavparse_type =
123
        g_type_register_static (GST_TYPE_ELEMENT, "GstWavParse",
124
        &wavparse_info, 0);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
125
  }
126
  return wavparse_type;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
127 128
}

Benjamin Otte's avatar
Benjamin Otte committed
129 130

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
131
gst_wavparse_base_init (gpointer g_class)
Benjamin Otte's avatar
Benjamin Otte committed
132 133
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
134
  GstPadTemplate *templ;
135 136 137 138 139
  static GstElementDetails gst_wavparse_details =
      GST_ELEMENT_DETAILS (".wav demuxer",
      "Codec/Demuxer/Audio",
      "Parse a .wav file into raw audio",
      "Erik Walthinsen <omega@cse.ogi.edu>");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140

Benjamin Otte's avatar
Benjamin Otte committed
141 142 143
  gst_element_class_set_details (element_class, &gst_wavparse_details);

  /* register src pads */
144 145 146 147 148 149
  templ = gst_static_pad_template_get (&sink_template_factory);
  gst_element_class_add_pad_template (element_class, templ);
  gst_object_unref (templ);
  templ = gst_static_pad_template_get (&src_template_factory);
  gst_element_class_add_pad_template (element_class, templ);
  gst_object_unref (templ);
Benjamin Otte's avatar
Benjamin Otte committed
150
}
151

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
152
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153
gst_wavparse_class_init (GstWavParseClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
154 155
{
  GstElementClass *gstelement_class;
Iain Holmes's avatar
Iain Holmes committed
156
  GObjectClass *object_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157 158

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

161
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
162

Iain Holmes's avatar
Iain Holmes committed
163
  object_class->get_property = gst_wavparse_get_property;
164
  gstelement_class->change_state = gst_wavparse_change_state;
165 166

  GST_DEBUG_CATEGORY_INIT (wavparse_debug, "wavparse", 0, "WAV parser");
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
167 168
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
169
static void
170
gst_wavparse_reset (GstWavParse * wavparse)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
171
{
172 173 174 175 176 177 178
  wavparse->state = GST_WAVPARSE_START;

  /* These will all be set correctly in the fmt chunk */
  wavparse->depth = 0;
  wavparse->rate = 0;
  wavparse->width = 0;
  wavparse->channels = 0;
179 180
  wavparse->blockalign = 0;
  wavparse->bps = 0;
181 182
  wavparse->offset = 0;
  wavparse->end_offset = 0;
183 184 185 186 187 188 189
  wavparse->dataleft = 0;
  wavparse->datasize = 0;
  wavparse->datastart = 0;

  if (wavparse->seek_event)
    gst_event_unref (wavparse->seek_event);
  wavparse->seek_event = NULL;
190

191 192
  /* we keep the segment info in time */
  gst_segment_init (&wavparse->segment, GST_FORMAT_TIME);
193 194 195 196 197 198 199 200 201 202
}

static void
gst_wavparse_init (GstWavParse * wavparse)
{
  /* sink */
  wavparse->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&sink_template_factory), "sink");
  gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->sinkpad);
203 204
  gst_pad_set_activate_function (wavparse->sinkpad,
      GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate));
205
  gst_pad_set_activatepull_function (wavparse->sinkpad,
206
      GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate_pull));
207
  gst_wavparse_reset (wavparse);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
208 209
}

210 211 212
static void
gst_wavparse_destroy_sourcepad (GstWavParse * wavparse)
{
213 214 215 216
  if (wavparse->srcpad) {
    gst_element_remove_pad (GST_ELEMENT (wavparse), wavparse->srcpad);
    wavparse->srcpad = NULL;
  }
217 218 219 220 221
}

static void
gst_wavparse_create_sourcepad (GstWavParse * wavparse)
{
222 223
  GstPadTemplate *templ;

224
  gst_wavparse_destroy_sourcepad (wavparse);
225 226

  /* source */
227 228 229
  templ = gst_static_pad_template_get (&src_template_factory);
  wavparse->srcpad = gst_pad_new_from_template (templ, "src");
  gst_object_unref (templ);
230
  gst_pad_use_fixed_caps (wavparse->srcpad);
231
  gst_pad_set_query_type_function (wavparse->srcpad,
232 233 234 235 236
      GST_DEBUG_FUNCPTR (gst_wavparse_get_query_types));
  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));
237 238
}

Iain Holmes's avatar
Iain Holmes committed
239
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
240 241
gst_wavparse_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
Iain Holmes's avatar
Iain Holmes committed
242 243 244 245 246 247
{
  GstWavParse *wavparse;

  wavparse = GST_WAVPARSE (object);

  switch (prop_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248 249
    default:
      break;
Iain Holmes's avatar
Iain Holmes committed
250 251 252
  }
}

David Schleef's avatar
David Schleef committed
253
#if 0
Iain Holmes's avatar
Iain Holmes committed
254
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
255
gst_wavparse_parse_adtl (GstWavParse * wavparse, int len)
Iain Holmes's avatar
Iain Holmes committed
256
{
257 258
  guint32 got_bytes;
  GstByteStream *bs = wavparse->bs;
Iain Holmes's avatar
Iain Holmes committed
259
  gst_riff_chunk *temp_chunk, chunk;
260 261 262 263 264
  guint8 *tempdata;
  struct _gst_riff_labl labl, *temp_labl;
  struct _gst_riff_ltxt ltxt, *temp_ltxt;
  struct _gst_riff_note note, *temp_note;
  char *label_name;
Iain Holmes's avatar
Iain Holmes committed
265
  GstProps *props;
266 267 268
  GstPropsEntry *entry;
  GstCaps *new_caps;
  GList *caps = NULL;
Iain Holmes's avatar
Iain Holmes committed
269

270
  props = wavparse->metadata->properties;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
271

272
  while (len > 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
273
    got_bytes =
274
        gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk));
275 276 277 278
    if (got_bytes != sizeof (gst_riff_chunk)) {
      return;
    }
    temp_chunk = (gst_riff_chunk *) tempdata;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279

280 281 282 283 284 285 286 287 288
    chunk.id = GUINT32_FROM_LE (temp_chunk->id);
    chunk.size = GUINT32_FROM_LE (temp_chunk->size);

    if (chunk.size == 0) {
      gst_bytestream_flush (bs, sizeof (gst_riff_chunk));
      len -= sizeof (gst_riff_chunk);
      continue;
    }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
289 290
    switch (chunk.id) {
      case GST_RIFF_adtl_labl:
291 292 293 294 295 296
        got_bytes =
            gst_bytestream_peek_bytes (bs, &tempdata,
            sizeof (struct _gst_riff_labl));
        if (got_bytes != sizeof (struct _gst_riff_labl)) {
          return;
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
297

298 299 300 301
        temp_labl = (struct _gst_riff_labl *) tempdata;
        labl.id = GUINT32_FROM_LE (temp_labl->id);
        labl.size = GUINT32_FROM_LE (temp_labl->size);
        labl.identifier = GUINT32_FROM_LE (temp_labl->identifier);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302

303 304
        gst_bytestream_flush (bs, sizeof (struct _gst_riff_labl));
        len -= sizeof (struct _gst_riff_labl);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
305

306 307 308 309
        got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, labl.size - 4);
        if (got_bytes != labl.size - 4) {
          return;
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
310

311
        label_name = (char *) tempdata;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
312

313 314
        gst_bytestream_flush (bs, ((labl.size - 4) + 1) & ~1);
        len -= (((labl.size - 4) + 1) & ~1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315

316 317 318 319
        new_caps = gst_caps_new ("label",
            "application/x-gst-metadata",
            gst_props_new ("identifier", G_TYPE_INT (labl.identifier),
                "name", G_TYPE_STRING (label_name), NULL));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
320

321 322 323 324
        if (gst_props_get (props, "labels", &caps, NULL)) {
          caps = g_list_append (caps, new_caps);
        } else {
          caps = g_list_append (NULL, new_caps);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
325

326 327 328
          entry = gst_props_entry_new ("labels", GST_PROPS_GLIST (caps));
          gst_props_add_entry (props, entry);
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
329

330
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
331 332

      case GST_RIFF_adtl_ltxt:
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
        got_bytes =
            gst_bytestream_peek_bytes (bs, &tempdata,
            sizeof (struct _gst_riff_ltxt));
        if (got_bytes != sizeof (struct _gst_riff_ltxt)) {
          return;
        }

        temp_ltxt = (struct _gst_riff_ltxt *) tempdata;
        ltxt.id = GUINT32_FROM_LE (temp_ltxt->id);
        ltxt.size = GUINT32_FROM_LE (temp_ltxt->size);
        ltxt.identifier = GUINT32_FROM_LE (temp_ltxt->identifier);
        ltxt.length = GUINT32_FROM_LE (temp_ltxt->length);
        ltxt.purpose = GUINT32_FROM_LE (temp_ltxt->purpose);
        ltxt.country = GUINT16_FROM_LE (temp_ltxt->country);
        ltxt.language = GUINT16_FROM_LE (temp_ltxt->language);
        ltxt.dialect = GUINT16_FROM_LE (temp_ltxt->dialect);
        ltxt.codepage = GUINT16_FROM_LE (temp_ltxt->codepage);

        gst_bytestream_flush (bs, sizeof (struct _gst_riff_ltxt));
        len -= sizeof (struct _gst_riff_ltxt);

        if (ltxt.size - 20 > 0) {
          got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, ltxt.size - 20);
          if (got_bytes != ltxt.size - 20) {
            return;
          }

          gst_bytestream_flush (bs, ((ltxt.size - 20) + 1) & ~1);
          len -= (((ltxt.size - 20) + 1) & ~1);

          label_name = (char *) tempdata;
        } else {
          label_name = "";
        }

        new_caps = gst_caps_new ("ltxt",
            "application/x-gst-metadata",
            gst_props_new ("identifier", G_TYPE_INT (ltxt.identifier),
                "name", G_TYPE_STRING (label_name),
                "length", G_TYPE_INT (ltxt.length), NULL));

        if (gst_props_get (props, "ltxts", &caps, NULL)) {
          caps = g_list_append (caps, new_caps);
        } else {
          caps = g_list_append (NULL, new_caps);

          entry = gst_props_entry_new ("ltxts", GST_PROPS_GLIST (caps));
          gst_props_add_entry (props, entry);
        }

        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
384 385

      case GST_RIFF_adtl_note:
386 387 388 389 390 391
        got_bytes =
            gst_bytestream_peek_bytes (bs, &tempdata,
            sizeof (struct _gst_riff_note));
        if (got_bytes != sizeof (struct _gst_riff_note)) {
          return;
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
392

393 394 395 396
        temp_note = (struct _gst_riff_note *) tempdata;
        note.id = GUINT32_FROM_LE (temp_note->id);
        note.size = GUINT32_FROM_LE (temp_note->size);
        note.identifier = GUINT32_FROM_LE (temp_note->identifier);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
397

398 399
        gst_bytestream_flush (bs, sizeof (struct _gst_riff_note));
        len -= sizeof (struct _gst_riff_note);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
400

401 402 403 404
        got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, note.size - 4);
        if (got_bytes != note.size - 4) {
          return;
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
405

406 407
        gst_bytestream_flush (bs, ((note.size - 4) + 1) & ~1);
        len -= (((note.size - 4) + 1) & ~1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
408

409
        label_name = (char *) tempdata;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410

411 412 413 414
        new_caps = gst_caps_new ("note",
            "application/x-gst-metadata",
            gst_props_new ("identifier", G_TYPE_INT (note.identifier),
                "name", G_TYPE_STRING (label_name), NULL));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
415

416 417 418 419
        if (gst_props_get (props, "notes", &caps, NULL)) {
          caps = g_list_append (caps, new_caps);
        } else {
          caps = g_list_append (NULL, new_caps);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420

421 422 423
          entry = gst_props_entry_new ("notes", GST_PROPS_GLIST (caps));
          gst_props_add_entry (props, entry);
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
424

425
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
426 427

      default:
428
        g_print ("Unknown chunk: %" GST_FOURCC_FORMAT "\n",
429 430
            GST_FOURCC_ARGS (chunk.id));
        return;
431 432
    }
  }
433 434

  g_object_notify (G_OBJECT (wavparse), "metadata");
435
}
436
#endif
437

438
#if 0
439
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
440
gst_wavparse_parse_cues (GstWavParse * wavparse, int len)
441
{
442 443 444 445 446 447 448 449
  guint32 got_bytes;
  GstByteStream *bs = wavparse->bs;
  struct _gst_riff_cue *temp_cue, cue;
  struct _gst_riff_cuepoints *points;
  guint8 *tempdata;
  int i;
  GList *cues = NULL;
  GstPropsEntry *entry;
450

451
  while (len > 0) {
452
    int required;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453 454

    got_bytes =
455 456
        gst_bytestream_peek_bytes (bs, &tempdata,
        sizeof (struct _gst_riff_cue));
457
    temp_cue = (struct _gst_riff_cue *) tempdata;
458

459 460 461 462 463 464 465 466 467 468 469 470 471 472
    /* fixup for our big endian friends */
    cue.id = GUINT32_FROM_LE (temp_cue->id);
    cue.size = GUINT32_FROM_LE (temp_cue->size);
    cue.cuepoints = GUINT32_FROM_LE (temp_cue->cuepoints);

    gst_bytestream_flush (bs, sizeof (struct _gst_riff_cue));
    if (got_bytes != sizeof (struct _gst_riff_cue)) {
      return;
    }

    len -= sizeof (struct _gst_riff_cue);

    /* -4 because cue.size contains the cuepoints size
       and we've already flushed that out of the system */
473 474 475 476
    required = cue.size - 4;
    got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, required);
    gst_bytestream_flush (bs, ((required) + 1) & ~1);
    if (got_bytes != required) {
477 478 479
      return;
    }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480
    len -= (((cue.size - 4) + 1) & ~1);
481 482 483

    /* now we have an array of struct _gst_riff_cuepoints in tempdata */
    points = (struct _gst_riff_cuepoints *) tempdata;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
484

485 486 487 488
    for (i = 0; i < cue.cuepoints; i++) {
      GstCaps *caps;

      caps = gst_caps_new ("cues",
489 490 491
          "application/x-gst-metadata",
          gst_props_new ("identifier", G_TYPE_INT (points[i].identifier),
              "position", G_TYPE_INT (points[i].offset), NULL));
492 493
      cues = g_list_append (cues, caps);
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
494

495 496 497
    entry = gst_props_entry_new ("cues", GST_PROPS_GLIST (cues));
    gst_props_add_entry (wavparse->metadata->properties, entry);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
498

499 500
  g_object_notify (G_OBJECT (wavparse), "metadata");
}
501
#endif
502

503
static gboolean
504
gst_wavparse_parse_file_header (GstElement * element, GstBuffer * buf)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
505
{
506
  guint32 doctype;
Wim Taymans's avatar
Wim Taymans committed
507

508
  if (!gst_riff_parse_file_header (element, buf, &doctype))
509
    return FALSE;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
510

511 512 513 514 515 516 517
  if (doctype != GST_RIFF_RIFF_WAVE)
    goto not_wav;

  return TRUE;

not_wav:
  {
518
    GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
519
        ("File is not an WAVE file: %" GST_FOURCC_FORMAT,
520
            GST_FOURCC_ARGS (doctype)));
521 522 523
    return FALSE;
  }
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
524

525 526 527 528 529 530 531 532 533
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;
534

535 536 537 538 539 540 541 542 543
  else if (!gst_wavparse_parse_file_header (GST_ELEMENT (wav), buf))
    return GST_FLOW_ERROR;

  wav->offset += 12;

  return GST_FLOW_OK;
}

#if 0
544 545 546 547
/* Read 'fmt ' header */
static gboolean
gst_wavparse_fmt (GstWavParse * wav)
{
548
  gst_riff_strf_auds *header = NULL;
549
  GstCaps *caps;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550

551
  if (!gst_riff_read_strf_auds (wav, &header)) {
552 553
    g_warning ("Not fmt");
    return FALSE;
554
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
555

556 557 558
  wav->format = header->format;
  wav->rate = header->rate;
  wav->channels = header->channels;
559 560 561 562 563 564
  if (wav->channels == 0) {
    GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
        ("Stream claims to contain zero channels - invalid data"));
    g_free (header);
    return FALSE;
  }
565
  wav->blockalign = header->blockalign;
566 567
  wav->width = (header->blockalign * 8) / header->channels;
  wav->depth = header->size;
568
  wav->bps = header->av_bps;
569 570 571 572 573 574 575 576 577 578
  if (wav->bps <= 0) {
    GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
        ("Stream claims to bitrate of <= zero - invalid data"));
    g_free (header);
    return FALSE;
  }

  /* Note: gst_riff_create_audio_caps might nedd to fix values in
   * the header header depending on the format, so call it first */
  caps = gst_riff_create_audio_caps (header->format, NULL, header, NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
579

580
  g_free (header);
581

582
  if (caps) {
583
    gst_wavparse_create_sourcepad (wav);
584 585 586
    gst_pad_use_fixed_caps (wav->srcpad);
    gst_pad_set_active (wav->srcpad, TRUE);
    gst_pad_set_caps (wav->srcpad, caps);
587
    gst_caps_free (caps);
588
    gst_element_add_pad (GST_ELEMENT (wav), wav->srcpad);
589
    gst_element_no_more_pads (GST_ELEMENT (wav));
590 591 592 593
    GST_DEBUG ("frequency %d, channels %d", wav->rate, wav->channels);
  } else {
    GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
    return FALSE;
594
  }
595

596 597
  return TRUE;
}
598

599 600 601 602
static gboolean
gst_wavparse_other (GstWavParse * wav)
{
  guint32 tag, length;
603

604
  if (!gst_riff_peek_head (wav, &tag, &length, NULL)) {
605
    GST_WARNING_OBJECT (wav, "could not peek head");
606
    return FALSE;
607
  }
608 609
  GST_DEBUG_OBJECT (wav, "got tag (%08x) %4.4s, length %d", tag,
      (gchar *) & tag, length);
610

611 612
  switch (tag) {
    case GST_RIFF_TAG_LIST:
613
      if (!(tag = gst_riff_peek_list (wav))) {
614
        GST_WARNING_OBJECT (wav, "could not peek list");
615 616
        return FALSE;
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
617

618 619
      switch (tag) {
        case GST_RIFF_LIST_INFO:
620
          if (!gst_riff_read_list (wav, &tag) || !gst_riff_read_info (wav)) {
621
            GST_WARNING_OBJECT (wav, "could not read list");
622
            return FALSE;
623
          }
624
          break;
625

626
        case GST_RIFF_LIST_adtl:
627
          if (!gst_riff_read_skip (wav)) {
628
            GST_WARNING_OBJECT (wav, "could not read skip");
629
            return FALSE;
630
          }
631 632 633
          break;

        default:
634 635
          GST_DEBUG_OBJECT (wav, "skipping tag (%08x) %4.4s", tag,
              (gchar *) & tag);
636
          if (!gst_riff_read_skip (wav)) {
637
            GST_WARNING_OBJECT (wav, "could not read skip");
638
            return FALSE;
639
          }
640
          break;
Wim Taymans's avatar
Wim Taymans committed
641
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
642

643
      break;
Wim Taymans's avatar
Wim Taymans committed
644

645
    case GST_RIFF_TAG_data:
646
      if (!gst_bytestream_flush (wav->bs, 8)) {
647
        GST_WARNING_OBJECT (wav, "could not flush 8 bytes");
648
        return FALSE;
649
      }
650

651
      GST_DEBUG_OBJECT (wav, "switching to data mode");
652
      wav->state = GST_WAVPARSE_DATA;
653
      wav->datastart = gst_bytestream_tell (wav->bs);
654 655 656 657 658 659 660
      if (length == 0) {
        guint64 file_length;

        /* length is 0, data probably stretches to the end
         * of file */
        GST_DEBUG_OBJECT (wav, "length is 0 trying to find length");
        /* get length of file */
661
        file_length = gst_bytestream_length (wav->bs);
662 663 664 665 666 667 668 669 670 671 672
        if (file_length == -1) {
          GST_DEBUG_OBJECT (wav,
              "could not get file length, assuming data to eof");
          /* could not get length, assuming till eof */
          length = G_MAXUINT32;
        }
        if (file_length > G_MAXUINT32) {
          GST_DEBUG_OBJECT (wav, "file length %lld, clipping to 32 bits");
          /* could not get length, assuming till eof */
          length = G_MAXUINT32;
        } else {
673 674
          GST_DEBUG_OBJECT (wav, "file length %lld, datalength",
              file_length, length);
675 676 677 678 679
          /* substract offset of datastart from length */
          length = file_length - wav->datastart;
          GST_DEBUG_OBJECT (wav, "datalength %lld", length);
        }
      }
680
      wav->datasize = (guint64) length;
681
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
682

683
    case GST_RIFF_TAG_cue:
684
      if (!gst_riff_read_skip (wav)) {
685
        GST_WARNING_OBJECT (wav, "could not read skip");
686
        return FALSE;
687
      }
688
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
689

690
    default:
691
      GST_DEBUG_OBJECT (wav, "skipping tag (%08x) %4.4s", tag, (gchar *) & tag);
692
      if (!gst_riff_read_skip (wav))
693
        return FALSE;
694 695
      break;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
696

697 698
  return TRUE;
}
699
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
700

701
static gboolean
702
gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
703
{
704 705 706 707 708 709
  gboolean res;
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
710
  gboolean flush;
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
  gboolean update;
  GstSegment seeksegment;

  GST_DEBUG_OBJECT (wav, "doing seek");

  if (event) {
    gst_event_parse_seek (event, &rate, &format, &flags,
        &cur_type, &cur, &stop_type, &stop);

    /* we have to have a format as the segment format. Try to convert
     * if not. */
    if (format != GST_FORMAT_TIME) {
      GstFormat fmt;

      fmt = GST_FORMAT_TIME;
      res = TRUE;
      if (cur_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (wav->srcpad, format, cur, &fmt, &cur);
      if (res && stop_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (wav->srcpad, format, stop, &fmt, &stop);
      if (!res)
        goto no_format;

      format = fmt;
    }
  } else {
    flags = 0;
  }
739

740
  flush = flags & GST_SEEK_FLAG_FLUSH;
741 742 743 744 745

  if (flush)
    gst_pad_push_event (wav->srcpad, gst_event_new_flush_start ());
  else
    gst_pad_pause_task (wav->sinkpad);
746

747
  GST_PAD_STREAM_LOCK (wav->sinkpad);
748

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
  /* copy segment, we need this because we still need the old
   * segment when we close the current segment. */
  memcpy (&seeksegment, &wav->segment, sizeof (GstSegment));

  if (event) {
    gst_segment_set_seek (&seeksegment, rate, format, flags,
        cur_type, cur, stop_type, stop, &update);
  }

  if ((stop = seeksegment.stop) == -1)
    stop = seeksegment.duration;

  if (cur_type != GST_SEEK_TYPE_NONE) {
    wav->offset =
        gst_util_uint64_scale_int (seeksegment.last_stop, wav->bps, GST_SECOND);
    wav->offset += wav->datastart;
    wav->offset -= wav->offset % wav->bytes_per_sample;
  }

  if (stop != -1) {
    wav->end_offset = gst_util_uint64_scale_int (stop, wav->bps, GST_SECOND);
    wav->end_offset += wav->datastart;
    wav->end_offset +=
        wav->bytes_per_sample - (wav->end_offset % wav->bytes_per_sample);
773
  } else {
774
    wav->end_offset = wav->datasize + wav->datastart;
775
  }
776 777
  wav->offset = MIN (wav->offset, wav->end_offset);
  wav->dataleft = wav->end_offset - wav->offset;
778

779
  GST_DEBUG ("seek: offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT
780
      ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT,
781 782
      wav->offset, wav->end_offset, GST_TIME_ARGS (seeksegment.start),
      GST_TIME_ARGS (stop));
783

784 785
  /* prepare for streaming again */
  if (flush) {
786
    gst_pad_push_event (wav->srcpad, gst_event_new_flush_stop ());
787 788 789 790 791 792 793 794 795 796 797
  } else if (wav->segment_running) {
    /* we are running the current segment and doing a non-flushing seek,
     * close the segment first based on the last_stop. */
    GST_DEBUG_OBJECT (wav, "closing running segment %" G_GINT64_FORMAT
        " to %" G_GINT64_FORMAT, wav->segment.start, wav->segment.last_stop);

    gst_pad_push_event (wav->srcpad,
        gst_event_new_new_segment (TRUE,
            wav->segment.rate, wav->segment.format,
            wav->segment.start, wav->segment.last_stop, wav->segment.time));
  }
798

799 800 801
  memcpy (&wav->segment, &seeksegment, sizeof (GstSegment));

  if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) {
802
    gst_element_post_message (GST_ELEMENT (wav),
803 804
        gst_message_new_segment_start (GST_OBJECT (wav),
            wav->segment.format, wav->segment.last_stop));
805
  }
806

807 808 809 810 811 812 813 814 815 816
  /* now send the newsegment */
  GST_DEBUG_OBJECT (wav, "Sending newsegment from %" G_GINT64_FORMAT
      " to %" G_GINT64_FORMAT, wav->segment.start, stop);

  gst_pad_push_event (wav->srcpad,
      gst_event_new_new_segment (FALSE,
          wav->segment.rate, wav->segment.format,
          wav->segment.last_stop, stop, wav->segment.time));

  wav->segment_running = TRUE;
817 818 819
  gst_pad_start_task (wav->sinkpad, (GstTaskFunction) gst_wavparse_loop,
      wav->sinkpad);

820
  GST_PAD_STREAM_UNLOCK (wav->sinkpad);
821 822 823

  return TRUE;

824 825 826 827 828 829
  /* ERRORS */
no_format:
  {
    GST_DEBUG_OBJECT (wav, "unsupported format given, seek aborted.");
    return FALSE;
  }
830 831 832 833 834 835 836 837 838 839 840
}

static GstFlowReturn
gst_wavparse_stream_headers (GstWavParse * wav)
{
  GstFlowReturn res;
  GstBuffer *buf, *extra;
  gst_riff_strf_auds *header = NULL;
  guint32 tag;
  gboolean gotdata = FALSE;
  GstCaps *caps;
841
  gint64 duration;
842
  gchar *codec_name = NULL;
843 844 845 846 847

  /* The header start with a 'fmt ' tag */
  if ((res = gst_riff_read_chunk (GST_ELEMENT (wav), wav->sinkpad,
              &wav->offset, &tag, &buf)) != GST_FLOW_OK)
    return res;
848 849 850 851 852 853

  else if (tag != GST_RIFF_TAG_fmt)
    goto invalid_wav;

  if (!(gst_riff_parse_strf_auds (GST_ELEMENT (wav), buf, &header, &extra)))
    goto parse_header_error;
854

855 856
  /* Note: gst_riff_create_audio_caps might nedd to fix values in
   * the header header depending on the format, so call it first */
857
  caps =
858 859 860 861 862
      gst_riff_create_audio_caps (header->format, NULL, header, extra,
      NULL, &codec_name);

  if (extra)
    gst_buffer_unref (extra);
863

864 865 866
  wav->format = header->format;
  wav->rate = header->rate;
  wav->channels = header->channels;
867 868 869 870

  if (wav->channels == 0)
    goto no_channels;

871 872 873 874
  wav->blockalign = header->blockalign;
  wav->width = (header->blockalign * 8) / header->channels;
  wav->depth = header->size;
  wav->bps = header->av_bps;
875 876 877 878 879 880 881

  if (wav->bps <= 0)
    goto no_bitrate;

  wav->bytes_per_sample = wav->channels * wav->width / 8;
  if (wav->bytes_per_sample <= 0)
    goto no_bytes_per_sample;
882

883 884
  g_free (header);

885 886 887 888 889 890 891 892 893 894 895
  if (!caps)
    goto unknown_format;

  gst_wavparse_create_sourcepad (wav);
  gst_pad_set_active (wav->srcpad, TRUE);
  gst_pad_set_caps (wav->srcpad, caps);
  gst_caps_unref (caps);
  caps = NULL;

  gst_element_add_pad (GST_ELEMENT (wav), wav->srcpad);
  gst_element_no_more_pads (GST_ELEMENT (wav));
896

897 898 899 900 901 902 903 904 905 906 907 908
  if (codec_name) {
    GstTagList *tags = gst_tag_list_new ();

    gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
        GST_TAG_AUDIO_CODEC, codec_name, NULL);

    gst_element_found_tags_for_pad (GST_ELEMENT (wav), wav->srcpad, tags);

    g_free (codec_name);
    codec_name = NULL;
  }

909
  GST_DEBUG ("frequency %d, channels %d", wav->rate, wav->channels);
910 911 912 913 914 915 916 917

  /* loop headers until we get data */
  while (!gotdata) {
    guint size;
    guint32 tag;

    if ((res =
            gst_pad_pull_range (wav->sinkpad, wav->offset, 8,
918 919
                &buf)) != GST_FLOW_OK)
      goto header_read_error;
920 921 922 923 924 925 926

    /*
       wav is a st00pid format, we don't know for sure where data starts.
       So we have to go bit by bit until we find the 'data' header
     */
    tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
    size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
927

928 929 930 931 932 933 934 935
    switch (tag) {
        /* TODO : Implement the various cases */
      case GST_RIFF_TAG_data:
        GST_DEBUG ("Got 'data' TAG, size : %d", size);
        gotdata = TRUE;
        wav->offset += 8;
        wav->datastart = wav->offset;
        wav->datasize = size;
936 937
        wav->dataleft = size;
        wav->end_offset = size + wav->datastart;
938 939
        break;
      default:
940
        GST_DEBUG ("Ignoring tag %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag));
941 942 943 944 945 946
        wav->offset += 8 + ((size + 1) & ~1);
    }
    gst_buffer_unref (buf);
  }

  GST_DEBUG ("Finished parsing headers");
947

948 949
  duration = gst_util_uint64_scale_int (wav->datasize, GST_SECOND, wav->bps);
  gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, duration);
950

951 952 953 954
  gst_pad_push_event (wav->srcpad,
      gst_event_new_new_segment (FALSE, wav->segment.rate,
          wav->segment.format, wav->segment.start,
          wav->segment.duration, wav->segment.start));
955 956

  return GST_FLOW_OK;
957 958 959 960 961

  /* ERROR */
invalid_wav:
  {
    GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
962
        ("Invalid WAV header (no fmt at start): %"
963
            GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
964
    g_free (codec_name);
965 966 967 968 969 970 971
    return GST_FLOW_ERROR;
  }
parse_header_error:
  {
    GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
        ("Couldn't parse audio header"));
    gst_buffer_unref (buf);
972
    g_free (codec_name);
973 974 975 976 977 978 979
    return GST_FLOW_ERROR;
  }
no_channels:
  {
    GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
        ("Stream claims to contain no channels - invalid data"));
    g_free (header);
980
    g_free (codec_name);
981 982 983 984 985 986 987
    return GST_FLOW_ERROR;
  }
no_bitrate:
  {
    GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
        ("Stream claims to have a bitrate of <= zero - invalid data"));
    g_free (header);
988
    g_free (codec_name);
989 990 991 992 993 994 995
    return GST_FLOW_ERROR;
  }
no_bytes_per_sample:
  {
    GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
        ("could not caluclate bytes per sample - invalid data"));
    g_free (header);
996
    g_free (codec_name);
997 998 999 1000 1001 1002 1003
    return GST_FLOW_ERROR;
  }
unknown_format:
  {
    GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL),
        ("No caps found for format 0x%x, %d channels, %d Hz",
            wav->format, wav->channels, wav->rate));
1004
    g_free (codec_name);
1005 1006 1007 1008 1009
    return GST_FLOW_ERROR;
  }
header_read_error:
  {
    GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL), ("Couldn't read in header"));
1010
    g_free (codec_name);
1011 1012
    return GST_FLOW_ERROR;
  }
1013 1014
}

1015
#define MAX_BUFFER_SIZE 4096
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1016

1017 1018
static GstFlowReturn
gst_wavparse_stream_data (GstWavParse * wav)
1019
{
1020 1021 1022
  GstBuffer *buf = NULL;
  GstFlowReturn res = GST_FLOW_OK;
  guint64 desired, obtained;
1023 1024
  GstClockTime timestamp, next_timestamp;
  guint64 pos, nextpos;
1025

1026
  GST_DEBUG ("offset : %lld , end : %lld", wav->offset, wav->end_offset);
1027

1028 1029 1030 1031
  /* Get the next n bytes and output them */
  if (wav->dataleft == 0)
    goto found_eos;

1032
  desired = MIN (wav->dataleft, MAX_BUFFER_SIZE * ABS (wav->segment.rate));
1033 1034
  if (desired >= wav->blockalign && wav->blockalign > 0)
    desired -= (desired % wav->blockalign);
1035

1036
  GST_DEBUG ("Fetching %lld bytes of data from the sinkpad.", desired);
1037

1038
  if ((res = gst_pad_pull_range (wav->sinkpad, wav->offset,
1039 1040
              desired, &buf)) != GST_FLOW_OK)
    goto pull_error;
1041

1042
  obtained = GST_BUFFER_SIZE (buf);
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062

  /* our positions */
  pos = wav->offset - wav->datastart;
  nextpos = pos + obtained;

  /* update offsets, does not overflow. */
  GST_BUFFER_OFFSET (buf) = pos / wav->bytes_per_sample;
  GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample;

  /* and timestamps, be carefull for overflows */
  timestamp = gst_util_uint64_scale_int (pos, GST_SECOND, wav->bps);
  next_timestamp = gst_util_uint64_scale_int (nextpos, GST_SECOND, wav->bps);

  GST_BUFFER_TIMESTAMP (buf) = timestamp;
  GST_BUFFER_DURATION (buf) = next_timestamp - timestamp;

  /* update current running segment position */
  gst_segment_set_last_stop (&wav->segment, GST_FORMAT_TIME, next_timestamp);

  /* don't forget to set the caps on the buffer */
1063
  gst_buffer_set_caps (buf, GST_PAD_CAPS (wav->srcpad));
1064

1065 1066 1067 1068 1069 1070 1071
  GST_DEBUG ("Got buffer. timestamp:%" GST_TIME_FORMAT " , duration:%"
      GST_TIME_FORMAT ", size:%u",
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
      GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_SIZE (buf));

  if ((res = gst_pad_push (wav->srcpad, buf)) != GST_FLOW_OK)
    goto push_error;
1072

1073 1074 1075 1076 1077
  if (obtained < wav->dataleft) {
    wav->dataleft -= obtained;
    wav->offset += obtained;
  } else {
    wav->dataleft = 0;
1078
  }
1079
  return res;
1080 1081 1082 1083 1084

  /* ERROR */
found_eos:
  {
    GST_DEBUG ("found EOS");
1085 1086 1087 1088
    /* we completed the segment */
    wav->segment_running = FALSE;
    if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) {
      GstClockTime stop;
1089

1090 1091
      if ((stop = wav->segment.stop) == -1)
        stop = wav->segment.duration;
1092 1093

      gst_element_post_message (GST_ELEMENT (wav),
1094
          gst_message_new_segment_done (GST_OBJECT (wav), GST_FORMAT_TIME,
1095
              stop));
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
    } else {
      gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
    }
    return GST_FLOW_WRONG_STATE;
  }
pull_error:
  {
    GST_DEBUG ("Error getting %ldd bytes from the sinkpad!", desired);
    return res;
  }
push_error:
  {
    GST_DEBUG ("Error pushing on srcpad");
    return res;
  }
1111 1112 1113 1114 1115 1116 1117 1118
}

static void
gst_wavparse_loop (GstPad * pad)
{
  GstFlowReturn ret;
  GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad));

1119 1120
  switch (wav->state) {
    case GST_WAVPARSE_START:
1121 1122
      if ((ret = gst_wavparse_stream_init (wav)) != GST_FLOW_OK)
        goto pause;
1123

1124
      wav->state = GST_WAVPARSE_HEADER;
1125
      /* fall-through */
1126

1127
    case GST_WAVPARSE_HEADER:
1128
      if ((ret = gst_wavparse_stream_headers (wav)) != GST_FLOW_OK)
1129
        goto pause;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1130

1131
      wav->state = GST_WAVPARSE_DATA;
1132
      /* fall-through */