• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* GStreamer
3  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4  * Copyright (C) <2006> Nokia Corporation, Stefan Kost <stefan.kost@nokia.com>.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-wavparse
24  * @title: wavparse
25  *
26  * Parse a .wav file into raw or compressed audio.
27  *
28  * Wavparse supports both push and pull mode operations, making it possible to
29  * stream from a network source.
30  *
31  * ## Example launch line
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  */
41 
42 /*
43  * TODO:
44  * http://replaygain.hydrogenaudio.org/file_format_wav.html
45  */
46 
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
50 
51 #include <string.h>
52 #include <math.h>
53 
54 #include "gstwavparse.h"
55 #include "gst/riff/riff-media.h"
56 #include <gst/base/gsttypefindhelper.h>
57 #include <gst/pbutils/descriptions.h>
58 #include <gst/gst-i18n-plugin.h>
59 
60 GST_DEBUG_CATEGORY_STATIC (wavparse_debug);
61 #define GST_CAT_DEFAULT (wavparse_debug)
62 
63 /* Data size chunk of RF64,
64  * see http://tech.ebu.ch/docs/tech/tech3306-2009.pdf */
65 #define GST_RS64_TAG_DS64 GST_MAKE_FOURCC ('d','s','6','4')
66 
67 static void gst_wavparse_dispose (GObject * object);
68 
69 static gboolean gst_wavparse_sink_activate (GstPad * sinkpad,
70     GstObject * parent);
71 static gboolean gst_wavparse_sink_activate_mode (GstPad * sinkpad,
72     GstObject * parent, GstPadMode mode, gboolean active);
73 static gboolean gst_wavparse_send_event (GstElement * element,
74     GstEvent * event);
75 static GstStateChangeReturn gst_wavparse_change_state (GstElement * element,
76     GstStateChange transition);
77 
78 static gboolean gst_wavparse_pad_query (GstPad * pad, GstObject * parent,
79     GstQuery * query);
80 static gboolean gst_wavparse_pad_convert (GstPad * pad, GstFormat src_format,
81     gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
82 
83 static GstFlowReturn gst_wavparse_chain (GstPad * pad, GstObject * parent,
84     GstBuffer * buf);
85 static gboolean gst_wavparse_sink_event (GstPad * pad, GstObject * parent,
86     GstEvent * event);
87 static void gst_wavparse_loop (GstPad * pad);
88 static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstObject * parent,
89     GstEvent * event);
90 
91 static void gst_wavparse_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec);
93 static void gst_wavparse_get_property (GObject * object, guint prop_id,
94     GValue * value, GParamSpec * pspec);
95 
96 #define DEFAULT_IGNORE_LENGTH FALSE
97 
98 enum
99 {
100   PROP_0,
101   PROP_IGNORE_LENGTH,
102 };
103 
104 static GstStaticPadTemplate sink_template_factory =
105     GST_STATIC_PAD_TEMPLATE ("sink",
106     GST_PAD_SINK,
107     GST_PAD_ALWAYS,
108     GST_STATIC_CAPS ("audio/x-wav;audio/x-rf64")
109     );
110 
111 #define DEBUG_INIT \
112   GST_DEBUG_CATEGORY_INIT (wavparse_debug, "wavparse", 0, "WAV parser");
113 
114 #define gst_wavparse_parent_class parent_class
115 G_DEFINE_TYPE_WITH_CODE (GstWavParse, gst_wavparse, GST_TYPE_ELEMENT,
116     DEBUG_INIT);
117 
118 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (wavparse, "wavparse", GST_RANK_PRIMARY,
119     GST_TYPE_WAVPARSE, gst_riff_init ();
120     );
121 
122 typedef struct
123 {
124   /* Offset Size    Description   Value
125    * 0x00   4       ID            unique identification value
126    * 0x04   4       Position      play order position
127    * 0x08   4       Data Chunk ID RIFF ID of corresponding data chunk
128    * 0x0c   4       Chunk Start   Byte Offset of Data Chunk *
129    * 0x10   4       Block Start   Byte Offset to sample of First Channel
130    * 0x14   4       Sample Offset Byte Offset to sample byte of First Channel
131    */
132   guint32 id;
133   guint32 position;
134   guint32 data_chunk_id;
135   guint32 chunk_start;
136   guint32 block_start;
137   guint32 sample_offset;
138 } GstWavParseCue;
139 
140 typedef struct
141 {
142   /* Offset Size    Description     Value
143    * 0x08   4       Cue Point ID    0 - 0xFFFFFFFF
144    * 0x0c           Text
145    */
146   guint32 cue_point_id;
147   gchar *text;
148 } GstWavParseLabl, GstWavParseNote;
149 
150 static void
gst_wavparse_class_init(GstWavParseClass * klass)151 gst_wavparse_class_init (GstWavParseClass * klass)
152 {
153   GstElementClass *gstelement_class;
154   GObjectClass *object_class;
155   GstPadTemplate *src_template;
156 
157   gstelement_class = (GstElementClass *) klass;
158   object_class = (GObjectClass *) klass;
159 
160   parent_class = g_type_class_peek_parent (klass);
161 
162   object_class->dispose = gst_wavparse_dispose;
163 
164   object_class->set_property = gst_wavparse_set_property;
165   object_class->get_property = gst_wavparse_get_property;
166 
167   /**
168    * GstWavParse:ignore-length:
169    *
170    * This selects whether the length found in a data chunk
171    * should be ignored. This may be useful for streamed audio
172    * where the length is unknown until the end of streaming,
173    * and various software/hardware just puts some random value
174    * in there and hopes it doesn't break too much.
175    */
176   g_object_class_install_property (object_class, PROP_IGNORE_LENGTH,
177       g_param_spec_boolean ("ignore-length",
178           "Ignore length",
179           "Ignore length from the Wave header",
180           DEFAULT_IGNORE_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
181       );
182 
183   gstelement_class->change_state = gst_wavparse_change_state;
184   gstelement_class->send_event = gst_wavparse_send_event;
185 
186   /* register pads */
187   gst_element_class_add_static_pad_template (gstelement_class,
188       &sink_template_factory);
189 
190   src_template = gst_pad_template_new ("src", GST_PAD_SRC,
191       GST_PAD_ALWAYS, gst_riff_create_audio_template_caps ());
192   gst_element_class_add_pad_template (gstelement_class, src_template);
193 
194   gst_element_class_set_static_metadata (gstelement_class, "WAV audio demuxer",
195       "Codec/Demuxer/Audio",
196       "Parse a .wav file into raw audio",
197       "Erik Walthinsen <omega@cse.ogi.edu>");
198 }
199 
200 static void
gst_wavparse_notes_free(GstWavParseNote * note)201 gst_wavparse_notes_free (GstWavParseNote * note)
202 {
203   if (note)
204     g_free (note->text);
205   g_free (note);
206 }
207 
208 static void
gst_wavparse_labls_free(GstWavParseLabl * labl)209 gst_wavparse_labls_free (GstWavParseLabl * labl)
210 {
211   if (labl)
212     g_free (labl->text);
213   g_free (labl);
214 }
215 
216 static void
gst_wavparse_reset(GstWavParse * wav)217 gst_wavparse_reset (GstWavParse * wav)
218 {
219   wav->state = GST_WAVPARSE_START;
220 
221   /* These will all be set correctly in the fmt chunk */
222   wav->depth = 0;
223   wav->rate = 0;
224   wav->width = 0;
225   wav->channels = 0;
226   wav->blockalign = 0;
227   wav->bps = 0;
228   wav->fact = 0;
229   wav->offset = 0;
230   wav->end_offset = 0;
231   wav->dataleft = 0;
232   wav->datasize = 0;
233   wav->datastart = 0;
234   wav->chunk_size = 0;
235   wav->duration = 0;
236   wav->got_fmt = FALSE;
237   wav->first = TRUE;
238 
239   if (wav->seek_event)
240     gst_event_unref (wav->seek_event);
241   wav->seek_event = NULL;
242   if (wav->adapter) {
243     gst_adapter_clear (wav->adapter);
244     g_object_unref (wav->adapter);
245     wav->adapter = NULL;
246   }
247   if (wav->tags)
248     gst_tag_list_unref (wav->tags);
249   wav->tags = NULL;
250   if (wav->toc)
251     gst_toc_unref (wav->toc);
252   wav->toc = NULL;
253   if (wav->cues)
254     g_list_free_full (wav->cues, g_free);
255   wav->cues = NULL;
256   if (wav->labls)
257     g_list_free_full (wav->labls, (GDestroyNotify) gst_wavparse_labls_free);
258   wav->labls = NULL;
259   if (wav->notes)
260     g_list_free_full (wav->notes, (GDestroyNotify) gst_wavparse_notes_free);
261   wav->notes = NULL;
262   if (wav->caps)
263     gst_caps_unref (wav->caps);
264   wav->caps = NULL;
265   if (wav->start_segment)
266     gst_event_unref (wav->start_segment);
267   wav->start_segment = NULL;
268 }
269 
270 static void
gst_wavparse_dispose(GObject * object)271 gst_wavparse_dispose (GObject * object)
272 {
273   GstWavParse *wav = GST_WAVPARSE (object);
274 
275   GST_DEBUG_OBJECT (wav, "WAV: Dispose");
276   gst_wavparse_reset (wav);
277 
278   G_OBJECT_CLASS (parent_class)->dispose (object);
279 }
280 
281 static void
gst_wavparse_init(GstWavParse * wavparse)282 gst_wavparse_init (GstWavParse * wavparse)
283 {
284   gst_wavparse_reset (wavparse);
285 
286   /* sink */
287   wavparse->sinkpad =
288       gst_pad_new_from_static_template (&sink_template_factory, "sink");
289   gst_pad_set_activate_function (wavparse->sinkpad,
290       GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate));
291   gst_pad_set_activatemode_function (wavparse->sinkpad,
292       GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate_mode));
293   gst_pad_set_chain_function (wavparse->sinkpad,
294       GST_DEBUG_FUNCPTR (gst_wavparse_chain));
295   gst_pad_set_event_function (wavparse->sinkpad,
296       GST_DEBUG_FUNCPTR (gst_wavparse_sink_event));
297   gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->sinkpad);
298 
299   /* src */
300   wavparse->srcpad =
301       gst_pad_new_from_template (gst_element_class_get_pad_template
302       (GST_ELEMENT_GET_CLASS (wavparse), "src"), "src");
303   gst_pad_use_fixed_caps (wavparse->srcpad);
304   gst_pad_set_query_function (wavparse->srcpad,
305       GST_DEBUG_FUNCPTR (gst_wavparse_pad_query));
306   gst_pad_set_event_function (wavparse->srcpad,
307       GST_DEBUG_FUNCPTR (gst_wavparse_srcpad_event));
308   gst_element_add_pad (GST_ELEMENT_CAST (wavparse), wavparse->srcpad);
309 }
310 
311 static gboolean
gst_wavparse_parse_file_header(GstElement * element,GstBuffer * buf)312 gst_wavparse_parse_file_header (GstElement * element, GstBuffer * buf)
313 {
314   guint32 doctype;
315 
316   if (!gst_riff_parse_file_header (element, buf, &doctype))
317     return FALSE;
318 
319   if (doctype != GST_RIFF_RIFF_WAVE)
320     goto not_wav;
321 
322   return TRUE;
323 
324   /* ERRORS */
325 not_wav:
326   {
327     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
328         ("File is not a WAVE file: 0x%" G_GINT32_MODIFIER "x", doctype));
329     return FALSE;
330   }
331 }
332 
333 static GstFlowReturn
gst_wavparse_stream_init(GstWavParse * wav)334 gst_wavparse_stream_init (GstWavParse * wav)
335 {
336   GstFlowReturn res;
337   GstBuffer *buf = NULL;
338 
339   if ((res = gst_pad_pull_range (wav->sinkpad,
340               wav->offset, 12, &buf)) != GST_FLOW_OK)
341     return res;
342   else if (!gst_wavparse_parse_file_header (GST_ELEMENT_CAST (wav), buf))
343     return GST_FLOW_ERROR;
344 
345   wav->offset += 12;
346 
347   return GST_FLOW_OK;
348 }
349 
350 static gboolean
gst_wavparse_time_to_bytepos(GstWavParse * wav,gint64 ts,gint64 * bytepos)351 gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos)
352 {
353   /* -1 always maps to -1 */
354   if (ts == -1) {
355     *bytepos = -1;
356     return TRUE;
357   }
358 
359   /* 0 always maps to 0 */
360   if (ts == 0) {
361     *bytepos = 0;
362     return TRUE;
363   }
364 
365   if (wav->bps > 0) {
366     *bytepos = gst_util_uint64_scale_ceil (ts, (guint64) wav->bps, GST_SECOND);
367     return TRUE;
368   } else if (wav->fact) {
369     guint64 bps = gst_util_uint64_scale (wav->datasize, wav->rate, wav->fact);
370     *bytepos = gst_util_uint64_scale_ceil (ts, bps, GST_SECOND);
371     return TRUE;
372   }
373 
374   return FALSE;
375 }
376 
377 /* This function is used to perform seeks on the element.
378  *
379  * It also works when event is NULL, in which case it will just
380  * start from the last configured segment. This technique is
381  * used when activating the element and to perform the seek in
382  * READY.
383  */
384 static gboolean
gst_wavparse_perform_seek(GstWavParse * wav,GstEvent * event)385 gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
386 {
387   gboolean res;
388   gdouble rate;
389   GstFormat format, bformat;
390   GstSeekFlags flags;
391   GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
392   gint64 cur, stop, upstream_size;
393   gboolean flush;
394   gboolean update;
395   GstSegment seeksegment = { 0, };
396   gint64 last_stop;
397   guint32 seqnum = GST_SEQNUM_INVALID;
398 
399   if (event) {
400     GST_DEBUG_OBJECT (wav, "doing seek with event");
401 
402     gst_event_parse_seek (event, &rate, &format, &flags,
403         &cur_type, &cur, &stop_type, &stop);
404     seqnum = gst_event_get_seqnum (event);
405 
406     /* no negative rates yet */
407     if (rate < 0.0)
408       goto negative_rate;
409 
410     if (format != wav->segment.format) {
411       GST_INFO_OBJECT (wav, "converting seek-event from %s to %s",
412           gst_format_get_name (format),
413           gst_format_get_name (wav->segment.format));
414       res = TRUE;
415       if (cur_type != GST_SEEK_TYPE_NONE)
416         res =
417             gst_pad_query_convert (wav->srcpad, format, cur,
418             wav->segment.format, &cur);
419       if (res && stop_type != GST_SEEK_TYPE_NONE)
420         res =
421             gst_pad_query_convert (wav->srcpad, format, stop,
422             wav->segment.format, &stop);
423       if (!res)
424         goto no_format;
425 
426       format = wav->segment.format;
427     }
428   } else {
429     GST_DEBUG_OBJECT (wav, "doing seek without event");
430     flags = 0;
431     rate = 1.0;
432     cur_type = GST_SEEK_TYPE_SET;
433     stop_type = GST_SEEK_TYPE_SET;
434   }
435 
436   /* in push mode, we must delegate to upstream */
437   if (wav->streaming) {
438     gboolean res = FALSE;
439 
440     /* if streaming not yet started; only prepare initial newsegment */
441     if (!event || wav->state != GST_WAVPARSE_DATA) {
442       if (wav->start_segment)
443         gst_event_unref (wav->start_segment);
444       wav->start_segment = gst_event_new_segment (&wav->segment);
445       res = TRUE;
446     } else {
447       /* convert seek positions to byte positions in data sections */
448       if (format == GST_FORMAT_TIME) {
449         /* should not fail */
450         if (!gst_wavparse_time_to_bytepos (wav, cur, &cur))
451           goto no_position;
452         if (!gst_wavparse_time_to_bytepos (wav, stop, &stop))
453           goto no_position;
454       }
455       /* mind sample boundary and header */
456       if (cur >= 0) {
457         cur -= (cur % wav->bytes_per_sample);
458         cur += wav->datastart;
459       }
460       if (stop >= 0) {
461         stop -= (stop % wav->bytes_per_sample);
462         stop += wav->datastart;
463       }
464       GST_DEBUG_OBJECT (wav, "Pushing BYTE seek rate %g, "
465           "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur,
466           stop);
467       /* BYTE seek event */
468       event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, cur,
469           stop_type, stop);
470       if (seqnum != GST_SEQNUM_INVALID)
471         gst_event_set_seqnum (event, seqnum);
472       res = gst_pad_push_event (wav->sinkpad, event);
473     }
474     return res;
475   }
476 
477   /* get flush flag */
478   flush = flags & GST_SEEK_FLAG_FLUSH;
479 
480   /* now we need to make sure the streaming thread is stopped. We do this by
481    * either sending a FLUSH_START event downstream which will cause the
482    * streaming thread to stop with a WRONG_STATE.
483    * For a non-flushing seek we simply pause the task, which will happen as soon
484    * as it completes one iteration (and thus might block when the sink is
485    * blocking in preroll). */
486   if (flush) {
487     GstEvent *fevent;
488     GST_DEBUG_OBJECT (wav, "sending flush start");
489 
490     fevent = gst_event_new_flush_start ();
491     if (seqnum != GST_SEQNUM_INVALID)
492       gst_event_set_seqnum (fevent, seqnum);
493     gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
494     gst_pad_push_event (wav->srcpad, fevent);
495   } else {
496     gst_pad_pause_task (wav->sinkpad);
497   }
498 
499   /* we should now be able to grab the streaming thread because we stopped it
500    * with the above flush/pause code */
501   GST_PAD_STREAM_LOCK (wav->sinkpad);
502 
503   /* save current position */
504   last_stop = wav->segment.position;
505 
506   GST_DEBUG_OBJECT (wav, "stopped streaming at %" G_GINT64_FORMAT, last_stop);
507 
508   /* copy segment, we need this because we still need the old
509    * segment when we close the current segment. */
510   memcpy (&seeksegment, &wav->segment, sizeof (GstSegment));
511 
512   /* configure the seek parameters in the seeksegment. We will then have the
513    * right values in the segment to perform the seek */
514   if (event) {
515     GST_DEBUG_OBJECT (wav, "configuring seek");
516     gst_segment_do_seek (&seeksegment, rate, format, flags,
517         cur_type, cur, stop_type, stop, &update);
518   }
519 
520   /* figure out the last position we need to play. If it's configured (stop !=
521    * -1), use that, else we play until the total duration of the file */
522   if ((stop = seeksegment.stop) == -1)
523     stop = seeksegment.duration;
524 
525   GST_DEBUG_OBJECT (wav, "cur_type =%d", cur_type);
526   if ((cur_type != GST_SEEK_TYPE_NONE)) {
527     /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and
528      * we can just copy the last_stop. If not, we use the bps to convert TIME to
529      * bytes. */
530     if (!gst_wavparse_time_to_bytepos (wav, seeksegment.position,
531             (gint64 *) & wav->offset))
532       wav->offset = seeksegment.position;
533     GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
534     wav->offset -= (wav->offset % wav->bytes_per_sample);
535     GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
536     wav->offset += wav->datastart;
537     GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
538   } else {
539     GST_LOG_OBJECT (wav, "continue from offset=%" G_GUINT64_FORMAT,
540         wav->offset);
541   }
542 
543   if (stop_type != GST_SEEK_TYPE_NONE) {
544     if (!gst_wavparse_time_to_bytepos (wav, stop, (gint64 *) & wav->end_offset))
545       wav->end_offset = stop;
546     GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
547     wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
548     GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
549     wav->end_offset += wav->datastart;
550     GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
551   } else {
552     GST_LOG_OBJECT (wav, "continue to end_offset=%" G_GUINT64_FORMAT,
553         wav->end_offset);
554   }
555 
556   /* make sure filesize is not exceeded due to rounding errors or so,
557    * same precaution as in _stream_headers */
558   bformat = GST_FORMAT_BYTES;
559   if (gst_pad_peer_query_duration (wav->sinkpad, bformat, &upstream_size))
560     wav->end_offset = MIN (wav->end_offset, upstream_size);
561 
562   if (wav->datasize > 0 && wav->end_offset > wav->datastart + wav->datasize)
563     wav->end_offset = wav->datastart + wav->datasize;
564 
565   /* this is the range of bytes we will use for playback */
566   wav->offset = MIN (wav->offset, wav->end_offset);
567   wav->dataleft = wav->end_offset - wav->offset;
568 
569   GST_DEBUG_OBJECT (wav,
570       "seek: rate %lf, offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT
571       ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, wav->offset,
572       wav->end_offset, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop));
573 
574   /* prepare for streaming again */
575   if (flush) {
576     GstEvent *fevent;
577 
578     /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
579     GST_DEBUG_OBJECT (wav, "sending flush stop");
580 
581     fevent = gst_event_new_flush_stop (TRUE);
582     if (seqnum != GST_SEQNUM_INVALID)
583       gst_event_set_seqnum (fevent, seqnum);
584     gst_pad_push_event (wav->sinkpad, gst_event_ref (fevent));
585     gst_pad_push_event (wav->srcpad, fevent);
586   }
587 
588   /* now we did the seek and can activate the new segment values */
589   memcpy (&wav->segment, &seeksegment, sizeof (GstSegment));
590 
591   /* if we're doing a segment seek, post a SEGMENT_START message */
592   if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) {
593     gst_element_post_message (GST_ELEMENT_CAST (wav),
594         gst_message_new_segment_start (GST_OBJECT_CAST (wav),
595             wav->segment.format, wav->segment.position));
596   }
597 
598   /* now create the newsegment */
599   GST_DEBUG_OBJECT (wav, "Creating newsegment from %" G_GINT64_FORMAT
600       " to %" G_GINT64_FORMAT, wav->segment.position, stop);
601 
602   /* store the newsegment event so it can be sent from the streaming thread. */
603   if (wav->start_segment)
604     gst_event_unref (wav->start_segment);
605   wav->start_segment = gst_event_new_segment (&wav->segment);
606   if (seqnum != GST_SEQNUM_INVALID)
607     gst_event_set_seqnum (wav->start_segment, seqnum);
608 
609   /* mark discont if we are going to stream from another position. */
610   if (last_stop != wav->segment.position) {
611     GST_DEBUG_OBJECT (wav, "mark DISCONT, we did a seek to another position");
612     wav->discont = TRUE;
613   }
614 
615   /* and start the streaming task again */
616   if (!wav->streaming) {
617     gst_pad_start_task (wav->sinkpad, (GstTaskFunction) gst_wavparse_loop,
618         wav->sinkpad, NULL);
619   }
620 
621   GST_PAD_STREAM_UNLOCK (wav->sinkpad);
622 
623   return TRUE;
624 
625   /* ERRORS */
626 negative_rate:
627   {
628     GST_DEBUG_OBJECT (wav, "negative playback rates are not supported yet.");
629     return FALSE;
630   }
631 no_format:
632   {
633     GST_DEBUG_OBJECT (wav, "unsupported format given, seek aborted.");
634     return FALSE;
635   }
636 no_position:
637   {
638     GST_DEBUG_OBJECT (wav,
639         "Could not determine byte position for desired time");
640     return FALSE;
641   }
642 }
643 
644 /*
645  * gst_wavparse_peek_chunk_info:
646  * @wav Wavparse object
647  * @tag holder for tag
648  * @size holder for tag size
649  *
650  * Peek next chunk info (tag and size)
651  *
652  * Returns: %TRUE when the chunk info (header) is available
653  */
654 static gboolean
gst_wavparse_peek_chunk_info(GstWavParse * wav,guint32 * tag,guint32 * size)655 gst_wavparse_peek_chunk_info (GstWavParse * wav, guint32 * tag, guint32 * size)
656 {
657   const guint8 *data = NULL;
658 
659   if (gst_adapter_available (wav->adapter) < 8)
660     return FALSE;
661 
662   data = gst_adapter_map (wav->adapter, 8);
663   *tag = GST_READ_UINT32_LE (data);
664   *size = GST_READ_UINT32_LE (data + 4);
665   gst_adapter_unmap (wav->adapter);
666 
667   GST_DEBUG ("Next chunk size is %u bytes, type %" GST_FOURCC_FORMAT, *size,
668       GST_FOURCC_ARGS (*tag));
669 
670   return TRUE;
671 }
672 
673 /*
674  * gst_wavparse_peek_chunk:
675  * @wav Wavparse object
676  * @tag holder for tag
677  * @size holder for tag size
678  *
679  * Peek enough data for one full chunk
680  *
681  * Returns: %TRUE when the full chunk is available
682  */
683 static gboolean
gst_wavparse_peek_chunk(GstWavParse * wav,guint32 * tag,guint32 * size)684 gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
685 {
686   guint32 peek_size = 0;
687   guint available;
688 
689   if (!gst_wavparse_peek_chunk_info (wav, tag, size))
690     return FALSE;
691 
692   /* size 0 -> empty data buffer would surprise most callers,
693    * large size -> do not bother trying to squeeze that into adapter,
694    * so we throw poor man's exception, which can be caught if caller really
695    * wants to handle 0 size chunk */
696   if (!(*size) || (*size) >= (1 << 30)) {
697     GST_INFO ("Invalid/unexpected chunk size %u for tag %" GST_FOURCC_FORMAT,
698         *size, GST_FOURCC_ARGS (*tag));
699     /* chain should give up */
700     wav->abort_buffering = TRUE;
701     return FALSE;
702   }
703   peek_size = (*size + 1) & ~1;
704   available = gst_adapter_available (wav->adapter);
705 
706   if (available >= (8 + peek_size)) {
707     return TRUE;
708   } else {
709     GST_LOG ("but only %u bytes available now", available);
710     return FALSE;
711   }
712 }
713 
714 /*
715  * gst_wavparse_calculate_duration:
716  * @wav: wavparse object
717  *
718  * Calculate duration on demand and store in @wav. Prefer bps, but use fact as a
719  * fallback.
720  *
721  * Returns: %TRUE if duration is available.
722  */
723 static gboolean
gst_wavparse_calculate_duration(GstWavParse * wav)724 gst_wavparse_calculate_duration (GstWavParse * wav)
725 {
726   if (wav->duration > 0)
727     return TRUE;
728 
729   if (wav->bps > 0) {
730     GST_INFO_OBJECT (wav, "Got datasize %" G_GUINT64_FORMAT, wav->datasize);
731     wav->duration =
732         gst_util_uint64_scale_ceil (wav->datasize, GST_SECOND,
733         (guint64) wav->bps);
734     GST_INFO_OBJECT (wav, "Got duration (bps) %" GST_TIME_FORMAT,
735         GST_TIME_ARGS (wav->duration));
736     return TRUE;
737   } else if (wav->fact) {
738     wav->duration =
739         gst_util_uint64_scale_ceil (GST_SECOND, wav->fact, wav->rate);
740     GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
741         GST_TIME_ARGS (wav->duration));
742     return TRUE;
743   }
744   return FALSE;
745 }
746 
747 static gboolean
gst_waveparse_ignore_chunk(GstWavParse * wav,GstBuffer * buf,guint32 tag,guint32 size)748 gst_waveparse_ignore_chunk (GstWavParse * wav, GstBuffer * buf, guint32 tag,
749     guint32 size)
750 {
751   guint flush;
752 
753   if (wav->streaming) {
754     if (!gst_wavparse_peek_chunk (wav, &tag, &size))
755       return FALSE;
756   }
757   GST_DEBUG_OBJECT (wav, "Ignoring tag %" GST_FOURCC_FORMAT,
758       GST_FOURCC_ARGS (tag));
759   flush = 8 + ((size + 1) & ~1);
760   wav->offset += flush;
761   if (wav->streaming) {
762     gst_adapter_flush (wav->adapter, flush);
763   } else {
764     gst_buffer_unref (buf);
765   }
766 
767   return TRUE;
768 }
769 
770 /*
771  * gst_wavparse_cue_chunk:
772  * @wav GstWavParse object
773  * @data holder for data
774  * @size holder for data size
775  *
776  * Parse cue chunk from @data to wav->cues.
777  *
778  * Returns: %TRUE when cue chunk is available
779  */
780 static gboolean
gst_wavparse_cue_chunk(GstWavParse * wav,const guint8 * data,guint32 size)781 gst_wavparse_cue_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
782 {
783   guint32 i, ncues;
784   GList *cues = NULL;
785   GstWavParseCue *cue;
786 
787   if (wav->cues) {
788     GST_WARNING_OBJECT (wav, "found another cue's");
789     return TRUE;
790   }
791 
792   ncues = GST_READ_UINT32_LE (data);
793 
794   if (size < 4 + ncues * 24) {
795     GST_WARNING_OBJECT (wav, "broken file %d %d", size, ncues);
796     return FALSE;
797   }
798 
799   /* parse data */
800   data += 4;
801   for (i = 0; i < ncues; i++) {
802     cue = g_new0 (GstWavParseCue, 1);
803     cue->id = GST_READ_UINT32_LE (data);
804     cue->position = GST_READ_UINT32_LE (data + 4);
805     cue->data_chunk_id = GST_READ_UINT32_LE (data + 8);
806     cue->chunk_start = GST_READ_UINT32_LE (data + 12);
807     cue->block_start = GST_READ_UINT32_LE (data + 16);
808     cue->sample_offset = GST_READ_UINT32_LE (data + 20);
809     cues = g_list_append (cues, cue);
810     data += 24;
811   }
812 
813   wav->cues = cues;
814 
815   return TRUE;
816 }
817 
818 /*
819  * gst_wavparse_labl_chunk:
820  * @wav GstWavParse object
821  * @data holder for data
822  * @size holder for data size
823  *
824  * Parse labl from @data to wav->labls.
825  *
826  * Returns: %TRUE when labl chunk is available
827  */
828 static gboolean
gst_wavparse_labl_chunk(GstWavParse * wav,const guint8 * data,guint32 size)829 gst_wavparse_labl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
830 {
831   GstWavParseLabl *labl;
832 
833   if (size < 5)
834     return FALSE;
835 
836   labl = g_new0 (GstWavParseLabl, 1);
837 
838   /* parse data */
839   labl->cue_point_id = GST_READ_UINT32_LE (data);
840   labl->text = g_strndup ((const gchar *) data + 4, size - 4);
841 
842   wav->labls = g_list_append (wav->labls, labl);
843 
844   return TRUE;
845 }
846 
847 /*
848  * gst_wavparse_note_chunk:
849  * @wav GstWavParse object
850  * @data holder for data
851  * @size holder for data size
852  *
853  * Parse note from @data to wav->notes.
854  *
855  * Returns: %TRUE when note chunk is available
856  */
857 static gboolean
gst_wavparse_note_chunk(GstWavParse * wav,const guint8 * data,guint32 size)858 gst_wavparse_note_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
859 {
860   GstWavParseNote *note;
861 
862   if (size < 5)
863     return FALSE;
864 
865   note = g_new0 (GstWavParseNote, 1);
866 
867   /* parse data */
868   note->cue_point_id = GST_READ_UINT32_LE (data);
869   note->text = g_strndup ((const gchar *) data + 4, size - 4);
870 
871   wav->notes = g_list_append (wav->notes, note);
872 
873   return TRUE;
874 }
875 
876 /*
877  * gst_wavparse_smpl_chunk:
878  * @wav GstWavParse object
879  * @data holder for data
880  * @size holder for data size
881  *
882  * Parse smpl chunk from @data.
883  *
884  * Returns: %TRUE when cue chunk is available
885  */
886 static gboolean
gst_wavparse_smpl_chunk(GstWavParse * wav,const guint8 * data,guint32 size)887 gst_wavparse_smpl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
888 {
889   guint32 note_number;
890 
891   /*
892      manufacturer_id = GST_READ_UINT32_LE (data);
893      product_id = GST_READ_UINT32_LE (data + 4);
894      sample_period = GST_READ_UINT32_LE (data + 8);
895    */
896   note_number = GST_READ_UINT32_LE (data + 12);
897   /*
898      pitch_fraction = GST_READ_UINT32_LE (data + 16);
899      SMPTE_format = GST_READ_UINT32_LE (data + 20);
900      SMPTE_offset = GST_READ_UINT32_LE (data + 24);
901      num_sample_loops = GST_READ_UINT32_LE (data + 28);
902      List of Sample Loops, 24 bytes each
903    */
904 
905   if (!wav->tags)
906     wav->tags = gst_tag_list_new_empty ();
907   gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
908       GST_TAG_MIDI_BASE_NOTE, (guint) note_number, NULL);
909   return TRUE;
910 }
911 
912 /*
913  * gst_wavparse_adtl_chunk:
914  * @wav GstWavParse object
915  * @data holder for data
916  * @size holder for data size
917  *
918  * Parse adtl from @data.
919  *
920  * Returns: %TRUE when adtl chunk is available
921  */
922 static gboolean
gst_wavparse_adtl_chunk(GstWavParse * wav,const guint8 * data,guint32 size)923 gst_wavparse_adtl_chunk (GstWavParse * wav, const guint8 * data, guint32 size)
924 {
925   guint32 ltag, lsize, offset = 0;
926 
927   while (size >= 8) {
928     ltag = GST_READ_UINT32_LE (data + offset);
929     lsize = GST_READ_UINT32_LE (data + offset + 4);
930 
931     if (lsize > (G_MAXUINT - 8) || lsize + 8 > size) {
932       GST_WARNING_OBJECT (wav, "Invalid adtl size: %u + 8 > %u", lsize, size);
933       return FALSE;
934     }
935 
936     switch (ltag) {
937       case GST_RIFF_TAG_labl:
938         gst_wavparse_labl_chunk (wav, data + offset + 8, lsize);
939         break;
940       case GST_RIFF_TAG_note:
941         gst_wavparse_note_chunk (wav, data + offset + 8, lsize);
942         break;
943       default:
944         GST_WARNING_OBJECT (wav, "Unknowm adtl %" GST_FOURCC_FORMAT,
945             GST_FOURCC_ARGS (ltag));
946         GST_MEMDUMP_OBJECT (wav, "Unknowm adtl", &data[offset], lsize);
947         break;
948     }
949     offset += 8 + GST_ROUND_UP_2 (lsize);
950     size -= 8 + GST_ROUND_UP_2 (lsize);
951   }
952 
953   return TRUE;
954 }
955 
956 static GstTagList *
gst_wavparse_get_tags_toc_entry(GstToc * toc,gchar * id)957 gst_wavparse_get_tags_toc_entry (GstToc * toc, gchar * id)
958 {
959   GstTagList *tags = NULL;
960   GstTocEntry *entry = NULL;
961 
962   entry = gst_toc_find_entry (toc, id);
963   if (entry != NULL) {
964     tags = gst_toc_entry_get_tags (entry);
965     if (tags == NULL) {
966       tags = gst_tag_list_new_empty ();
967       gst_toc_entry_set_tags (entry, tags);
968     }
969   }
970 
971   return tags;
972 }
973 
974 /*
975  * gst_wavparse_create_toc:
976  * @wav GstWavParse object
977  *
978  * Create TOC from wav->cues and wav->labls.
979  */
980 static gboolean
gst_wavparse_create_toc(GstWavParse * wav)981 gst_wavparse_create_toc (GstWavParse * wav)
982 {
983   gint64 start, stop;
984   gchar *id;
985   GList *list;
986   GstWavParseCue *cue;
987   GstWavParseLabl *labl;
988   GstWavParseNote *note;
989   GstTagList *tags;
990   GstToc *toc;
991   GstTocEntry *entry = NULL, *cur_subentry = NULL, *prev_subentry = NULL;
992 
993   GST_OBJECT_LOCK (wav);
994   if (wav->toc) {
995     GST_OBJECT_UNLOCK (wav);
996     GST_WARNING_OBJECT (wav, "found another TOC");
997     return FALSE;
998   }
999 
1000   if (!wav->cues) {
1001     GST_OBJECT_UNLOCK (wav);
1002     return TRUE;
1003   }
1004 
1005   /* FIXME: send CURRENT scope toc too */
1006   toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
1007 
1008   /* add cue edition */
1009   entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "cue");
1010   gst_toc_entry_set_start_stop_times (entry, 0, wav->duration);
1011   gst_toc_append_entry (toc, entry);
1012 
1013   /* add tracks in cue edition */
1014   list = wav->cues;
1015   while (list) {
1016     cue = list->data;
1017     prev_subentry = cur_subentry;
1018     /* previous track stop time = current track start time */
1019     if (prev_subentry != NULL) {
1020       gst_toc_entry_get_start_stop_times (prev_subentry, &start, NULL);
1021       stop = gst_util_uint64_scale_round (cue->position, GST_SECOND, wav->rate);
1022       gst_toc_entry_set_start_stop_times (prev_subentry, start, stop);
1023     }
1024     id = g_strdup_printf ("%08x", cue->id);
1025     cur_subentry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_TRACK, id);
1026     g_free (id);
1027     start = gst_util_uint64_scale_round (cue->position, GST_SECOND, wav->rate);
1028     stop = wav->duration;
1029     gst_toc_entry_set_start_stop_times (cur_subentry, start, stop);
1030     gst_toc_entry_append_sub_entry (entry, cur_subentry);
1031     list = g_list_next (list);
1032   }
1033 
1034   /* add tags in tracks */
1035   list = wav->labls;
1036   while (list) {
1037     labl = list->data;
1038     id = g_strdup_printf ("%08x", labl->cue_point_id);
1039     tags = gst_wavparse_get_tags_toc_entry (toc, id);
1040     g_free (id);
1041     if (tags != NULL) {
1042       gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, labl->text,
1043           NULL);
1044     }
1045     list = g_list_next (list);
1046   }
1047   list = wav->notes;
1048   while (list) {
1049     note = list->data;
1050     id = g_strdup_printf ("%08x", note->cue_point_id);
1051     tags = gst_wavparse_get_tags_toc_entry (toc, id);
1052     g_free (id);
1053     if (tags != NULL) {
1054       gst_tag_list_add (tags, GST_TAG_MERGE_PREPEND, GST_TAG_COMMENT,
1055           note->text, NULL);
1056     }
1057     list = g_list_next (list);
1058   }
1059 
1060   /* send data as TOC */
1061   wav->toc = toc;
1062 
1063   /* send TOC event */
1064   if (wav->toc) {
1065     GST_OBJECT_UNLOCK (wav);
1066     gst_pad_push_event (wav->srcpad, gst_event_new_toc (wav->toc, FALSE));
1067   }
1068 
1069   return TRUE;
1070 }
1071 
1072 #define MAX_BUFFER_SIZE 4096
1073 
1074 static gboolean
parse_ds64(GstWavParse * wav,GstBuffer * buf)1075 parse_ds64 (GstWavParse * wav, GstBuffer * buf)
1076 {
1077   GstMapInfo map;
1078   guint32 dataSizeLow, dataSizeHigh;
1079   guint32 sampleCountLow, sampleCountHigh;
1080 
1081   gst_buffer_map (buf, &map, GST_MAP_READ);
1082   dataSizeLow = GST_READ_UINT32_LE (map.data + 2 * 4);
1083   dataSizeHigh = GST_READ_UINT32_LE (map.data + 3 * 4);
1084   sampleCountLow = GST_READ_UINT32_LE (map.data + 4 * 4);
1085   sampleCountHigh = GST_READ_UINT32_LE (map.data + 5 * 4);
1086   gst_buffer_unmap (buf, &map);
1087   if (dataSizeHigh != 0xFFFFFFFF && dataSizeLow != 0xFFFFFFFF) {
1088     wav->datasize = ((guint64) dataSizeHigh << 32) | dataSizeLow;
1089   }
1090   if (sampleCountHigh != 0xFFFFFFFF && sampleCountLow != 0xFFFFFFFF) {
1091     wav->fact = ((guint64) sampleCountHigh << 32) | sampleCountLow;
1092   }
1093 
1094   GST_DEBUG_OBJECT (wav, "Got 'ds64' TAG, datasize : %" G_GINT64_FORMAT
1095       " fact: %" G_GINT64_FORMAT, wav->datasize, wav->fact);
1096   return TRUE;
1097 }
1098 
1099 static GstFlowReturn
gst_wavparse_stream_headers(GstWavParse * wav)1100 gst_wavparse_stream_headers (GstWavParse * wav)
1101 {
1102   GstFlowReturn res = GST_FLOW_OK;
1103   GstBuffer *buf = NULL;
1104   gst_riff_strf_auds *header = NULL;
1105   guint32 tag, size;
1106   gboolean gotdata = FALSE;
1107   GstCaps *caps = NULL;
1108   gchar *codec_name = NULL;
1109   gint64 upstream_size = 0;
1110   GstStructure *s;
1111 
1112   /* search for "_fmt" chunk, which must be before "data" */
1113   while (!wav->got_fmt) {
1114     GstBuffer *extra;
1115 
1116     if (wav->streaming) {
1117       if (!gst_wavparse_peek_chunk (wav, &tag, &size))
1118         return res;
1119 
1120       gst_adapter_flush (wav->adapter, 8);
1121       wav->offset += 8;
1122 
1123       if (size) {
1124         buf = gst_adapter_take_buffer (wav->adapter, size);
1125         if (size & 1)
1126           gst_adapter_flush (wav->adapter, 1);
1127         wav->offset += GST_ROUND_UP_2 (size);
1128       } else {
1129         buf = gst_buffer_new ();
1130       }
1131     } else {
1132       if ((res = gst_riff_read_chunk (GST_ELEMENT_CAST (wav), wav->sinkpad,
1133                   &wav->offset, &tag, &buf)) != GST_FLOW_OK)
1134         return res;
1135     }
1136 
1137     if (tag == GST_RS64_TAG_DS64) {
1138       if (!parse_ds64 (wav, buf))
1139         goto fail;
1140       else
1141         continue;
1142     }
1143 
1144     if (tag != GST_RIFF_TAG_fmt) {
1145       GST_DEBUG_OBJECT (wav, "skipping %" GST_FOURCC_FORMAT " chunk",
1146           GST_FOURCC_ARGS (tag));
1147       gst_buffer_unref (buf);
1148       buf = NULL;
1149       continue;
1150     }
1151 
1152     if (!(gst_riff_parse_strf_auds (GST_ELEMENT_CAST (wav), buf, &header,
1153                 &extra)))
1154       goto parse_header_error;
1155 
1156     buf = NULL;                 /* parse_strf_auds() took ownership of buffer */
1157 
1158     /* do sanity checks of header fields */
1159     if (header->channels == 0)
1160       goto no_channels;
1161     if (header->rate == 0)
1162       goto no_rate;
1163 
1164     GST_DEBUG_OBJECT (wav, "creating the caps");
1165 
1166     /* Note: gst_riff_create_audio_caps might need to fix values in
1167      * the header header depending on the format, so call it first */
1168     /* FIXME: Need to handle the channel reorder map */
1169     caps = gst_riff_create_audio_caps (header->format, NULL, header, extra,
1170         NULL, &codec_name, NULL);
1171 
1172     if (extra)
1173       gst_buffer_unref (extra);
1174 
1175     if (!caps)
1176       goto unknown_format;
1177 
1178     /* If we got raw audio from upstream, we remove the codec_data field,
1179      * which may have been added if the wav header included an extended
1180      * chunk. We want to keep it for non raw audio.
1181      */
1182     s = gst_caps_get_structure (caps, 0);
1183     if (s && gst_structure_has_name (s, "audio/x-raw")) {
1184       gst_structure_remove_field (s, "codec_data");
1185     }
1186 
1187     /* do more sanity checks of header fields
1188      * (these can be sanitized by gst_riff_create_audio_caps()
1189      */
1190     wav->format = header->format;
1191     wav->rate = header->rate;
1192     wav->channels = header->channels;
1193     wav->blockalign = header->blockalign;
1194     wav->depth = header->bits_per_sample;
1195     wav->av_bps = header->av_bps;
1196     wav->vbr = FALSE;
1197 
1198     g_free (header);
1199     header = NULL;
1200 
1201     /* do format specific handling */
1202     switch (wav->format) {
1203       case GST_RIFF_WAVE_FORMAT_MPEGL12:
1204       case GST_RIFF_WAVE_FORMAT_MPEGL3:
1205       {
1206         /* Note: workaround for mp2/mp3 embedded in wav, that relies on the
1207          * bitrate inside the mpeg stream */
1208         GST_INFO ("resetting bps from %u to 0 for mp2/3", wav->av_bps);
1209         wav->bps = 0;
1210         break;
1211       }
1212       case GST_RIFF_WAVE_FORMAT_PCM:
1213         if (wav->blockalign > wav->channels * ((wav->depth + 7) / 8))
1214           goto invalid_blockalign;
1215         /* fall through */
1216       default:
1217         if (wav->av_bps > wav->blockalign * wav->rate)
1218           goto invalid_bps;
1219         /* use the configured bps */
1220         wav->bps = wav->av_bps;
1221         break;
1222     }
1223 
1224     wav->width = (wav->blockalign * 8) / wav->channels;
1225     wav->bytes_per_sample = wav->channels * wav->width / 8;
1226 
1227     if (wav->bytes_per_sample <= 0)
1228       goto no_bytes_per_sample;
1229 
1230     GST_DEBUG_OBJECT (wav, "blockalign = %u", (guint) wav->blockalign);
1231     GST_DEBUG_OBJECT (wav, "width      = %u", (guint) wav->width);
1232     GST_DEBUG_OBJECT (wav, "depth      = %u", (guint) wav->depth);
1233     GST_DEBUG_OBJECT (wav, "av_bps     = %u", (guint) wav->av_bps);
1234     GST_DEBUG_OBJECT (wav, "frequency  = %u", (guint) wav->rate);
1235     GST_DEBUG_OBJECT (wav, "channels   = %u", (guint) wav->channels);
1236     GST_DEBUG_OBJECT (wav, "bytes_per_sample = %u", wav->bytes_per_sample);
1237 
1238     /* bps can be 0 when we don't have a valid bitrate (mostly for compressed
1239      * formats). This will make the element output a BYTE format segment and
1240      * will not timestamp the outgoing buffers.
1241      */
1242     GST_DEBUG_OBJECT (wav, "bps        = %u", (guint) wav->bps);
1243 
1244     GST_DEBUG_OBJECT (wav, "caps = %" GST_PTR_FORMAT, caps);
1245 
1246     /* create pad later so we can sniff the first few bytes
1247      * of the real data and correct our caps if necessary */
1248     gst_caps_replace (&wav->caps, caps);
1249     gst_caps_replace (&caps, NULL);
1250 
1251     wav->got_fmt = TRUE;
1252 
1253     if (wav->tags == NULL)
1254       wav->tags = gst_tag_list_new_empty ();
1255 
1256     {
1257       GstCaps *templ_caps = gst_pad_get_pad_template_caps (wav->sinkpad);
1258       gst_pb_utils_add_codec_description_to_tag_list (wav->tags,
1259           GST_TAG_CONTAINER_FORMAT, templ_caps);
1260       gst_caps_unref (templ_caps);
1261     }
1262 
1263     /* If bps is nonzero, then we do have a valid bitrate that can be
1264      * announced in a tag list. */
1265     if (wav->bps) {
1266       guint bitrate = wav->bps * 8;
1267       gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
1268           GST_TAG_BITRATE, bitrate, NULL);
1269     }
1270 
1271     if (codec_name) {
1272       gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
1273           GST_TAG_AUDIO_CODEC, codec_name, NULL);
1274 
1275       g_free (codec_name);
1276       codec_name = NULL;
1277     }
1278 
1279   }
1280 
1281   gst_pad_peer_query_duration (wav->sinkpad, GST_FORMAT_BYTES, &upstream_size);
1282   GST_DEBUG_OBJECT (wav, "upstream size %" G_GUINT64_FORMAT, upstream_size);
1283 
1284   /* loop headers until we get data */
1285   while (!gotdata) {
1286     if (wav->streaming) {
1287       if (!gst_wavparse_peek_chunk_info (wav, &tag, &size))
1288         goto exit;
1289     } else {
1290       GstMapInfo map;
1291 
1292       buf = NULL;
1293       if ((res =
1294               gst_pad_pull_range (wav->sinkpad, wav->offset, 8,
1295                   &buf)) != GST_FLOW_OK)
1296         goto header_read_error;
1297       gst_buffer_map (buf, &map, GST_MAP_READ);
1298       tag = GST_READ_UINT32_LE (map.data);
1299       size = GST_READ_UINT32_LE (map.data + 4);
1300       gst_buffer_unmap (buf, &map);
1301     }
1302 
1303     GST_INFO_OBJECT (wav,
1304         "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT ", size %"
1305         G_GUINT32_FORMAT, GST_FOURCC_ARGS (tag), wav->offset, size);
1306 
1307     /* Maximum valid size is INT_MAX */
1308     if (size & 0x80000000) {
1309       GST_WARNING_OBJECT (wav, "Invalid size, clipping to 0x7fffffff");
1310       size = 0x7fffffff;
1311     }
1312 
1313     /* Clip to upstream size if known */
1314     if (upstream_size > 0 && size + wav->offset > upstream_size) {
1315       GST_WARNING_OBJECT (wav, "Clipping chunk size to file size");
1316       g_assert (upstream_size >= wav->offset);
1317       size = upstream_size - wav->offset;
1318     }
1319 
1320     /* wav is a st00pid format, we don't know for sure where data starts.
1321      * So we have to go bit by bit until we find the 'data' header
1322      */
1323     switch (tag) {
1324       case GST_RIFF_TAG_data:{
1325         guint64 size64;
1326 
1327         GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %u", size);
1328         size64 = size;
1329         if (wav->ignore_length) {
1330           GST_DEBUG_OBJECT (wav, "Ignoring length");
1331           size64 = 0;
1332         }
1333         if (wav->streaming) {
1334           gst_adapter_flush (wav->adapter, 8);
1335           gotdata = TRUE;
1336         } else {
1337           gst_buffer_unref (buf);
1338         }
1339         wav->offset += 8;
1340         wav->datastart = wav->offset;
1341         /* use size from ds64 chunk if available */
1342         if (size64 == -1 && wav->datasize > 0) {
1343           GST_DEBUG_OBJECT (wav, "Using ds64 datasize");
1344           size64 = wav->datasize;
1345         }
1346         wav->chunk_size = size64;
1347 
1348         /* If size is zero, then the data chunk probably actually extends to
1349            the end of the file */
1350         if (size64 == 0 && upstream_size) {
1351           size64 = upstream_size - wav->datastart;
1352         }
1353         /* Or the file might be truncated */
1354         else if (upstream_size) {
1355           size64 = MIN (size64, (upstream_size - wav->datastart));
1356         }
1357         wav->datasize = size64;
1358         wav->dataleft = size64;
1359         wav->end_offset = size64 + wav->datastart;
1360         if (!wav->streaming) {
1361           /* We will continue parsing tags 'till end */
1362           wav->offset += size64;
1363         }
1364         GST_DEBUG_OBJECT (wav, "datasize = %" G_GUINT64_FORMAT, size64);
1365         break;
1366       }
1367       case GST_RIFF_TAG_fact:{
1368         if (wav->fact == 0 &&
1369             wav->format != GST_RIFF_WAVE_FORMAT_MPEGL12 &&
1370             wav->format != GST_RIFF_WAVE_FORMAT_MPEGL3) {
1371           const guint data_size = 4;
1372 
1373           GST_INFO_OBJECT (wav, "Have fact chunk");
1374           if (size < data_size) {
1375             if (!gst_waveparse_ignore_chunk (wav, buf, tag, size)) {
1376               /* need more data */
1377               goto exit;
1378             }
1379             GST_DEBUG_OBJECT (wav, "need %u, available %u; ignoring chunk",
1380                 data_size, size);
1381             break;
1382           }
1383           /* number of samples (for compressed formats) */
1384           if (wav->streaming) {
1385             const guint8 *data = NULL;
1386 
1387             if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
1388               goto exit;
1389             }
1390             gst_adapter_flush (wav->adapter, 8);
1391             data = gst_adapter_map (wav->adapter, data_size);
1392             wav->fact = GST_READ_UINT32_LE (data);
1393             gst_adapter_unmap (wav->adapter);
1394             gst_adapter_flush (wav->adapter, GST_ROUND_UP_2 (size));
1395           } else {
1396             gst_buffer_unref (buf);
1397             buf = NULL;
1398             if ((res =
1399                     gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
1400                         data_size, &buf)) != GST_FLOW_OK)
1401               goto header_read_error;
1402             gst_buffer_extract (buf, 0, &wav->fact, 4);
1403             wav->fact = GUINT32_FROM_LE (wav->fact);
1404             gst_buffer_unref (buf);
1405           }
1406           GST_DEBUG_OBJECT (wav, "have fact %" G_GUINT64_FORMAT, wav->fact);
1407           wav->offset += 8 + GST_ROUND_UP_2 (size);
1408           break;
1409         } else {
1410           if (!gst_waveparse_ignore_chunk (wav, buf, tag, size)) {
1411             /* need more data */
1412             goto exit;
1413           }
1414         }
1415         break;
1416       }
1417       case GST_RIFF_TAG_acid:{
1418         const gst_riff_acid *acid = NULL;
1419         const guint data_size = sizeof (gst_riff_acid);
1420         gfloat tempo;
1421 
1422         GST_INFO_OBJECT (wav, "Have acid chunk");
1423         if (size < data_size) {
1424           if (!gst_waveparse_ignore_chunk (wav, buf, tag, size)) {
1425             /* need more data */
1426             goto exit;
1427           }
1428           GST_DEBUG_OBJECT (wav, "need %u, available %u; ignoring chunk",
1429               data_size, size);
1430           break;
1431         }
1432         if (wav->streaming) {
1433           if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
1434             goto exit;
1435           }
1436           gst_adapter_flush (wav->adapter, 8);
1437           acid = (const gst_riff_acid *) gst_adapter_map (wav->adapter,
1438               data_size);
1439           tempo = acid->tempo;
1440           gst_adapter_unmap (wav->adapter);
1441         } else {
1442           GstMapInfo map;
1443           gst_buffer_unref (buf);
1444           buf = NULL;
1445           if ((res =
1446                   gst_pad_pull_range (wav->sinkpad, wav->offset + 8,
1447                       size, &buf)) != GST_FLOW_OK)
1448             goto header_read_error;
1449           gst_buffer_map (buf, &map, GST_MAP_READ);
1450           acid = (const gst_riff_acid *) map.data;
1451           tempo = acid->tempo;
1452           gst_buffer_unmap (buf, &map);
1453         }
1454         /* send data as tags */
1455         if (!wav->tags)
1456           wav->tags = gst_tag_list_new_empty ();
1457         gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
1458             GST_TAG_BEATS_PER_MINUTE, tempo, NULL);
1459 
1460         size = GST_ROUND_UP_2 (size);
1461         if (wav->streaming) {
1462           gst_adapter_flush (wav->adapter, size);
1463         } else {
1464           gst_buffer_unref (buf);
1465         }
1466         wav->offset += 8 + size;
1467         break;
1468       }
1469         /* FIXME: all list tags after data are ignored in streaming mode */
1470       case GST_RIFF_TAG_LIST:{
1471         guint32 ltag;
1472 
1473         if (wav->streaming) {
1474           const guint8 *data = NULL;
1475 
1476           if (gst_adapter_available (wav->adapter) < 12) {
1477             goto exit;
1478           }
1479           data = gst_adapter_map (wav->adapter, 12);
1480           ltag = GST_READ_UINT32_LE (data + 8);
1481           gst_adapter_unmap (wav->adapter);
1482         } else {
1483           gst_buffer_unref (buf);
1484           buf = NULL;
1485           if ((res =
1486                   gst_pad_pull_range (wav->sinkpad, wav->offset, 12,
1487                       &buf)) != GST_FLOW_OK)
1488             goto header_read_error;
1489           gst_buffer_extract (buf, 8, &ltag, 4);
1490           ltag = GUINT32_FROM_LE (ltag);
1491         }
1492         switch (ltag) {
1493           case GST_RIFF_LIST_INFO:{
1494             const gint data_size = size - 4;
1495             GstTagList *new;
1496 
1497             GST_INFO_OBJECT (wav, "Have LIST chunk INFO size %u", data_size);
1498             if (wav->streaming) {
1499               if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
1500                 goto exit;
1501               }
1502               gst_adapter_flush (wav->adapter, 12);
1503               wav->offset += 12;
1504               if (data_size > 0) {
1505                 buf = gst_adapter_take_buffer (wav->adapter, data_size);
1506                 if (data_size & 1)
1507                   gst_adapter_flush (wav->adapter, 1);
1508               }
1509             } else {
1510               wav->offset += 12;
1511               gst_buffer_unref (buf);
1512               buf = NULL;
1513               if (data_size > 0) {
1514                 if ((res =
1515                         gst_pad_pull_range (wav->sinkpad, wav->offset,
1516                             data_size, &buf)) != GST_FLOW_OK)
1517                   goto header_read_error;
1518               }
1519             }
1520             if (data_size > 0) {
1521               /* parse tags */
1522               gst_riff_parse_info (GST_ELEMENT (wav), buf, &new);
1523               if (new) {
1524                 GstTagList *old = wav->tags;
1525                 wav->tags =
1526                     gst_tag_list_merge (old, new, GST_TAG_MERGE_REPLACE);
1527                 if (old)
1528                   gst_tag_list_unref (old);
1529                 gst_tag_list_unref (new);
1530               }
1531               gst_buffer_unref (buf);
1532               wav->offset += GST_ROUND_UP_2 (data_size);
1533             }
1534             break;
1535           }
1536           case GST_RIFF_LIST_adtl:{
1537             const gint data_size = size - 4;
1538 
1539             GST_INFO_OBJECT (wav, "Have 'adtl' LIST, size %u", data_size);
1540             if (wav->streaming) {
1541               const guint8 *data = NULL;
1542 
1543               gst_adapter_flush (wav->adapter, 12);
1544               wav->offset += 12;
1545               data = gst_adapter_map (wav->adapter, data_size);
1546               gst_wavparse_adtl_chunk (wav, data, data_size);
1547               gst_adapter_unmap (wav->adapter);
1548             } else {
1549               GstMapInfo map;
1550 
1551               gst_buffer_unref (buf);
1552               buf = NULL;
1553               wav->offset += 12;
1554               if ((res =
1555                       gst_pad_pull_range (wav->sinkpad, wav->offset,
1556                           data_size, &buf)) != GST_FLOW_OK)
1557                 goto header_read_error;
1558               gst_buffer_map (buf, &map, GST_MAP_READ);
1559               gst_wavparse_adtl_chunk (wav, (const guint8 *) map.data,
1560                   data_size);
1561               gst_buffer_unmap (buf, &map);
1562             }
1563             wav->offset += GST_ROUND_UP_2 (data_size);
1564             break;
1565           }
1566           default:
1567             GST_WARNING_OBJECT (wav, "Ignoring LIST chunk %" GST_FOURCC_FORMAT,
1568                 GST_FOURCC_ARGS (ltag));
1569             if (!gst_waveparse_ignore_chunk (wav, buf, tag, size))
1570               /* need more data */
1571               goto exit;
1572             break;
1573         }
1574         break;
1575       }
1576       case GST_RIFF_TAG_cue:{
1577         const guint data_size = size;
1578 
1579         GST_DEBUG_OBJECT (wav, "Have 'cue' TAG, size : %u", data_size);
1580         if (wav->streaming) {
1581           const guint8 *data = NULL;
1582 
1583           if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
1584             goto exit;
1585           }
1586           gst_adapter_flush (wav->adapter, 8);
1587           wav->offset += 8;
1588           data = gst_adapter_map (wav->adapter, data_size);
1589           if (!gst_wavparse_cue_chunk (wav, data, data_size)) {
1590             goto header_read_error;
1591           }
1592           gst_adapter_unmap (wav->adapter);
1593         } else {
1594           GstMapInfo map;
1595 
1596           wav->offset += 8;
1597           gst_buffer_unref (buf);
1598           buf = NULL;
1599           if ((res =
1600                   gst_pad_pull_range (wav->sinkpad, wav->offset,
1601                       data_size, &buf)) != GST_FLOW_OK)
1602             goto header_read_error;
1603           gst_buffer_map (buf, &map, GST_MAP_READ);
1604           if (!gst_wavparse_cue_chunk (wav, (const guint8 *) map.data,
1605                   data_size)) {
1606             goto header_read_error;
1607           }
1608           gst_buffer_unmap (buf, &map);
1609         }
1610         size = GST_ROUND_UP_2 (size);
1611         if (wav->streaming) {
1612           gst_adapter_flush (wav->adapter, size);
1613         } else {
1614           gst_buffer_unref (buf);
1615         }
1616         size = GST_ROUND_UP_2 (size);
1617         wav->offset += size;
1618         break;
1619       }
1620       case GST_RIFF_TAG_smpl:{
1621         const gint data_size = size;
1622 
1623         GST_DEBUG_OBJECT (wav, "Have 'smpl' TAG, size : %u", data_size);
1624         if (wav->streaming) {
1625           const guint8 *data = NULL;
1626 
1627           if (!gst_wavparse_peek_chunk (wav, &tag, &size)) {
1628             goto exit;
1629           }
1630           gst_adapter_flush (wav->adapter, 8);
1631           wav->offset += 8;
1632           data = gst_adapter_map (wav->adapter, data_size);
1633           if (!gst_wavparse_smpl_chunk (wav, data, data_size)) {
1634             goto header_read_error;
1635           }
1636           gst_adapter_unmap (wav->adapter);
1637         } else {
1638           GstMapInfo map;
1639 
1640           wav->offset += 8;
1641           gst_buffer_unref (buf);
1642           buf = NULL;
1643           if ((res =
1644                   gst_pad_pull_range (wav->sinkpad, wav->offset,
1645                       data_size, &buf)) != GST_FLOW_OK)
1646             goto header_read_error;
1647           gst_buffer_map (buf, &map, GST_MAP_READ);
1648           if (!gst_wavparse_smpl_chunk (wav, (const guint8 *) map.data,
1649                   data_size)) {
1650             goto header_read_error;
1651           }
1652           gst_buffer_unmap (buf, &map);
1653         }
1654         size = GST_ROUND_UP_2 (size);
1655         if (wav->streaming) {
1656           gst_adapter_flush (wav->adapter, size);
1657         } else {
1658           gst_buffer_unref (buf);
1659         }
1660         size = GST_ROUND_UP_2 (size);
1661         wav->offset += size;
1662         break;
1663       }
1664       default:
1665         GST_WARNING_OBJECT (wav, "Ignoring chunk %" GST_FOURCC_FORMAT,
1666             GST_FOURCC_ARGS (tag));
1667         if (!gst_waveparse_ignore_chunk (wav, buf, tag, size))
1668           /* need more data */
1669           goto exit;
1670         break;
1671     }
1672 
1673     if (upstream_size && (wav->offset >= upstream_size)) {
1674       /* Now we are gone through the whole file */
1675       gotdata = TRUE;
1676     }
1677   }
1678 
1679   GST_DEBUG_OBJECT (wav, "Finished parsing headers");
1680 
1681   if (wav->bps <= 0 && wav->fact) {
1682 #if 0
1683     /* not a good idea, as for embedded mp2/mp3 we set bps to 0 earlier */
1684     wav->bps =
1685         (guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize,
1686         (guint64) wav->fact);
1687     GST_INFO_OBJECT (wav, "calculated bps : %u, enabling VBR", wav->bps);
1688 #endif
1689     wav->vbr = TRUE;
1690   }
1691 
1692   if (gst_wavparse_calculate_duration (wav)) {
1693     gst_segment_init (&wav->segment, GST_FORMAT_TIME);
1694     if (!wav->ignore_length)
1695       wav->segment.duration = wav->duration;
1696     if (!wav->toc)
1697       gst_wavparse_create_toc (wav);
1698   } else {
1699     /* no bitrate, let downstream peer do the math, we'll feed it bytes. */
1700     gst_segment_init (&wav->segment, GST_FORMAT_BYTES);
1701     if (!wav->ignore_length)
1702       wav->segment.duration = wav->datasize;
1703   }
1704 
1705   /* now we have all the info to perform a pending seek if any, if no
1706    * event, this will still do the right thing and it will also send
1707    * the right newsegment event downstream. */
1708   gst_wavparse_perform_seek (wav, wav->seek_event);
1709   /* remove pending event */
1710   gst_event_replace (&wav->seek_event, NULL);
1711 
1712   /* we just started, we are discont */
1713   wav->discont = TRUE;
1714 
1715   wav->state = GST_WAVPARSE_DATA;
1716 
1717   /* determine reasonable max buffer size,
1718    * that is, buffers not too small either size or time wise
1719    * so we do not end up with too many of them */
1720   /* var abuse */
1721   if (gst_wavparse_time_to_bytepos (wav, 40 * GST_MSECOND, &upstream_size))
1722     wav->max_buf_size = upstream_size;
1723   else
1724     wav->max_buf_size = 0;
1725   wav->max_buf_size = MAX (wav->max_buf_size, MAX_BUFFER_SIZE);
1726   if (wav->blockalign > 0)
1727     wav->max_buf_size -= (wav->max_buf_size % wav->blockalign);
1728 
1729   GST_DEBUG_OBJECT (wav, "max buffer size %u", wav->max_buf_size);
1730 
1731   return GST_FLOW_OK;
1732 
1733   /* ERROR */
1734 exit:
1735   {
1736     g_free (codec_name);
1737     g_free (header);
1738     if (caps)
1739       gst_caps_unref (caps);
1740     return res;
1741   }
1742 fail:
1743   {
1744     res = GST_FLOW_ERROR;
1745     goto exit;
1746   }
1747 parse_header_error:
1748   {
1749     GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
1750         ("Couldn't parse audio header"));
1751     goto fail;
1752   }
1753 no_channels:
1754   {
1755     GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
1756         ("Stream claims to contain no channels - invalid data"));
1757     goto fail;
1758   }
1759 no_rate:
1760   {
1761     GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
1762         ("Stream with sample_rate == 0 - invalid data"));
1763     goto fail;
1764   }
1765 invalid_blockalign:
1766   {
1767     GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
1768         ("Stream claims blockalign = %u, which is more than %u - invalid data",
1769             wav->blockalign, wav->channels * ((wav->depth + 7) / 8)));
1770     goto fail;
1771   }
1772 invalid_bps:
1773   {
1774     GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
1775         ("Stream claims av_bsp = %u, which is more than %u - invalid data",
1776             wav->av_bps, wav->blockalign * wav->rate));
1777     goto fail;
1778   }
1779 no_bytes_per_sample:
1780   {
1781     GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL),
1782         ("Could not calculate bytes per sample - invalid data"));
1783     goto fail;
1784   }
1785 unknown_format:
1786   {
1787     GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL),
1788         ("No caps found for format 0x%x, %u channels, %u Hz",
1789             wav->format, wav->channels, wav->rate));
1790     goto fail;
1791   }
1792 header_read_error:
1793   {
1794     GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
1795         ("Couldn't read in header %d (%s)", res, gst_flow_get_name (res)));
1796     goto fail;
1797   }
1798 }
1799 
1800 /*
1801  * Read WAV file tag when streaming
1802  */
1803 static GstFlowReturn
gst_wavparse_parse_stream_init(GstWavParse * wav)1804 gst_wavparse_parse_stream_init (GstWavParse * wav)
1805 {
1806   if (gst_adapter_available (wav->adapter) >= 12) {
1807     GstBuffer *tmp;
1808 
1809     /* _take flushes the data */
1810     tmp = gst_adapter_take_buffer (wav->adapter, 12);
1811 
1812     GST_DEBUG ("Parsing wav header");
1813     if (!gst_wavparse_parse_file_header (GST_ELEMENT_CAST (wav), tmp))
1814       return GST_FLOW_ERROR;
1815 
1816     wav->offset += 12;
1817     /* Go to next state */
1818     wav->state = GST_WAVPARSE_HEADER;
1819   }
1820   return GST_FLOW_OK;
1821 }
1822 
1823 /* handle an event sent directly to the element.
1824  *
1825  * This event can be sent either in the READY state or the
1826  * >READY state. The only event of interest really is the seek
1827  * event.
1828  *
1829  * In the READY state we can only store the event and try to
1830  * respect it when going to PAUSED. We assume we are in the
1831  * READY state when our parsing state != GST_WAVPARSE_DATA.
1832  *
1833  * When we are steaming, we can simply perform the seek right
1834  * away.
1835  */
1836 static gboolean
gst_wavparse_send_event(GstElement * element,GstEvent * event)1837 gst_wavparse_send_event (GstElement * element, GstEvent * event)
1838 {
1839   GstWavParse *wav = GST_WAVPARSE (element);
1840   gboolean res = FALSE;
1841 
1842   GST_DEBUG_OBJECT (wav, "received event %s", GST_EVENT_TYPE_NAME (event));
1843 
1844   switch (GST_EVENT_TYPE (event)) {
1845     case GST_EVENT_SEEK:
1846       if (wav->state == GST_WAVPARSE_DATA) {
1847         /* we can handle the seek directly when streaming data */
1848         res = gst_wavparse_perform_seek (wav, event);
1849       } else {
1850         GST_DEBUG_OBJECT (wav, "queuing seek for later");
1851 
1852         gst_event_replace (&wav->seek_event, event);
1853 
1854         /* we always return true */
1855         res = TRUE;
1856       }
1857       break;
1858     default:
1859       break;
1860   }
1861   gst_event_unref (event);
1862   return res;
1863 }
1864 
1865 static gboolean
gst_wavparse_have_dts_caps(const GstCaps * caps,GstTypeFindProbability prob)1866 gst_wavparse_have_dts_caps (const GstCaps * caps, GstTypeFindProbability prob)
1867 {
1868   GstStructure *s;
1869 
1870   s = gst_caps_get_structure (caps, 0);
1871   if (!gst_structure_has_name (s, "audio/x-dts"))
1872     return FALSE;
1873   /* typefind behavior for DTS:
1874    *  MAXIMUM: multiple frame syncs detected, certainly DTS
1875    *  LIKELY: single frame sync at offset 0.  Maybe DTS?
1876    *  POSSIBLE: single frame sync, not at offset 0.  Highly unlikely
1877    *    to be DTS.  */
1878   if (prob > GST_TYPE_FIND_LIKELY)
1879     return TRUE;
1880   if (prob <= GST_TYPE_FIND_POSSIBLE)
1881     return FALSE;
1882   /* for maybe, check for at least a valid-looking rate and channels */
1883   if (!gst_structure_has_field (s, "channels"))
1884     return FALSE;
1885   /* and for extra assurance we could also check the rate from the DTS frame
1886    * against the one in the wav header, but for now let's not do that */
1887   return gst_structure_has_field (s, "rate");
1888 }
1889 
1890 static GstTagList *
gst_wavparse_get_upstream_tags(GstWavParse * wav,GstTagScope scope)1891 gst_wavparse_get_upstream_tags (GstWavParse * wav, GstTagScope scope)
1892 {
1893   GstTagList *tags = NULL;
1894   GstEvent *ev;
1895   gint i;
1896 
1897   i = 0;
1898   while ((ev = gst_pad_get_sticky_event (wav->sinkpad, GST_EVENT_TAG, i++))) {
1899     gst_event_parse_tag (ev, &tags);
1900     if (tags != NULL && gst_tag_list_get_scope (tags) == scope) {
1901       tags = gst_tag_list_copy (tags);
1902       gst_tag_list_remove_tag (tags, GST_TAG_CONTAINER_FORMAT);
1903       gst_event_unref (ev);
1904       break;
1905     }
1906     tags = NULL;
1907     gst_event_unref (ev);
1908   }
1909   return tags;
1910 }
1911 
1912 static void
gst_wavparse_add_src_pad(GstWavParse * wav,GstBuffer * buf)1913 gst_wavparse_add_src_pad (GstWavParse * wav, GstBuffer * buf)
1914 {
1915   GstStructure *s;
1916   GstTagList *tags, *utags;
1917 
1918   GST_DEBUG_OBJECT (wav, "adding src pad");
1919 
1920   g_assert (wav->caps != NULL);
1921 
1922   s = gst_caps_get_structure (wav->caps, 0);
1923   if (s && gst_structure_has_name (s, "audio/x-raw") && buf != NULL
1924       && (GST_BUFFER_OFFSET (buf) == 0 || !GST_BUFFER_OFFSET_IS_VALID (buf))) {
1925     GstTypeFindProbability prob;
1926     GstCaps *tf_caps;
1927 
1928     tf_caps = gst_type_find_helper_for_buffer (GST_OBJECT (wav), buf, &prob);
1929     if (tf_caps != NULL) {
1930       GST_LOG ("typefind caps = %" GST_PTR_FORMAT ", P=%d", tf_caps, prob);
1931       if (gst_wavparse_have_dts_caps (tf_caps, prob)) {
1932         GST_INFO_OBJECT (wav, "Found DTS marker in file marked as raw PCM");
1933         gst_caps_unref (wav->caps);
1934         wav->caps = tf_caps;
1935 
1936         gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE,
1937             GST_TAG_AUDIO_CODEC, "dts", NULL);
1938       } else {
1939         GST_DEBUG_OBJECT (wav, "found caps %" GST_PTR_FORMAT " for stream "
1940             "marked as raw PCM audio, but ignoring for now", tf_caps);
1941         gst_caps_unref (tf_caps);
1942       }
1943     }
1944   }
1945 
1946   gst_pad_set_caps (wav->srcpad, wav->caps);
1947 
1948   if (wav->start_segment) {
1949     GST_DEBUG_OBJECT (wav, "Send start segment event on newpad");
1950     gst_pad_push_event (wav->srcpad, wav->start_segment);
1951     wav->start_segment = NULL;
1952   }
1953 
1954   /* upstream tags, e.g. from id3/ape tag before the wav file; assume for now
1955    * that there'll be only one scope/type of tag list from upstream, if any */
1956   utags = gst_wavparse_get_upstream_tags (wav, GST_TAG_SCOPE_GLOBAL);
1957   if (utags == NULL)
1958     utags = gst_wavparse_get_upstream_tags (wav, GST_TAG_SCOPE_STREAM);
1959 
1960   /* if there's a tag upstream it's probably been added to override the
1961    * tags from inside the wav header, so keep upstream tags if in doubt */
1962   tags = gst_tag_list_merge (utags, wav->tags, GST_TAG_MERGE_KEEP);
1963 
1964   if (wav->tags != NULL) {
1965     gst_tag_list_unref (wav->tags);
1966     wav->tags = NULL;
1967   }
1968 
1969   if (utags != NULL)
1970     gst_tag_list_unref (utags);
1971 
1972   /* send tags downstream, if any */
1973   if (tags != NULL)
1974     gst_pad_push_event (wav->srcpad, gst_event_new_tag (tags));
1975 }
1976 
1977 static GstFlowReturn
gst_wavparse_stream_data(GstWavParse * wav,gboolean flushing)1978 gst_wavparse_stream_data (GstWavParse * wav, gboolean flushing)
1979 {
1980   GstBuffer *buf = NULL;
1981   GstFlowReturn res = GST_FLOW_OK;
1982   guint64 desired, obtained;
1983   GstClockTime timestamp, next_timestamp, duration;
1984   guint64 pos, nextpos;
1985 
1986 iterate_adapter:
1987   GST_LOG_OBJECT (wav,
1988       "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT " , dataleft: %"
1989       G_GINT64_FORMAT, wav->offset, wav->end_offset, wav->dataleft);
1990 
1991   if ((wav->dataleft == 0 || wav->dataleft < wav->blockalign)) {
1992     /* In case chunk size is not declared in the beginning get size from the
1993      * file size directly */
1994     if (wav->chunk_size == 0) {
1995       gint64 upstream_size = 0;
1996 
1997       /* Get the size of the file   */
1998       if (!gst_pad_peer_query_duration (wav->sinkpad, GST_FORMAT_BYTES,
1999               &upstream_size))
2000         goto found_eos;
2001 
2002       if (upstream_size < wav->offset + wav->datastart)
2003         goto found_eos;
2004 
2005       /* If file has updated since the beginning continue reading the file */
2006       wav->dataleft = upstream_size - wav->offset - wav->datastart;
2007       wav->end_offset = upstream_size;
2008 
2009       /* Get the next n bytes and output them, if we can */
2010       if (wav->dataleft == 0 || wav->dataleft < wav->blockalign)
2011         goto found_eos;
2012     } else {
2013       goto found_eos;
2014     }
2015   }
2016 
2017   /* scale the amount of data by the segment rate so we get equal
2018    * amounts of data regardless of the playback rate */
2019   desired =
2020       MIN (gst_guint64_to_gdouble (wav->dataleft),
2021       wav->max_buf_size * ABS (wav->segment.rate));
2022 
2023   if (desired >= wav->blockalign && wav->blockalign > 0)
2024     desired -= (desired % wav->blockalign);
2025 
2026   GST_LOG_OBJECT (wav, "Fetching %" G_GINT64_FORMAT " bytes of data "
2027       "from the sinkpad", desired);
2028 
2029   if (wav->streaming) {
2030     guint avail = gst_adapter_available (wav->adapter);
2031     guint extra;
2032 
2033     /* flush some bytes if evil upstream sends segment that starts
2034      * before data or does is not send sample aligned segment */
2035     if (G_LIKELY (wav->offset >= wav->datastart)) {
2036       extra = (wav->offset - wav->datastart) % wav->bytes_per_sample;
2037     } else {
2038       extra = wav->datastart - wav->offset;
2039     }
2040 
2041     if (G_UNLIKELY (extra)) {
2042       extra = wav->bytes_per_sample - extra;
2043       if (extra <= avail) {
2044         GST_DEBUG_OBJECT (wav, "flushing %u bytes to sample boundary", extra);
2045         gst_adapter_flush (wav->adapter, extra);
2046         wav->offset += extra;
2047         wav->dataleft -= extra;
2048         goto iterate_adapter;
2049       } else {
2050         GST_DEBUG_OBJECT (wav, "flushing %u bytes", avail);
2051         gst_adapter_clear (wav->adapter);
2052         wav->offset += avail;
2053         wav->dataleft -= avail;
2054         return GST_FLOW_OK;
2055       }
2056     }
2057 
2058     if (avail < desired) {
2059       GST_LOG_OBJECT (wav, "Got only %u bytes of data from the sinkpad", avail);
2060 
2061       /* If we are at the end of the stream, we need to flush whatever we have left */
2062       if (avail > 0 && flushing) {
2063         if (avail >= wav->blockalign && wav->blockalign > 0) {
2064           avail -= (avail % wav->blockalign);
2065           buf = gst_adapter_take_buffer (wav->adapter, avail);
2066         } else {
2067           return GST_FLOW_OK;
2068         }
2069       } else {
2070         return GST_FLOW_OK;
2071       }
2072     } else {
2073       buf = gst_adapter_take_buffer (wav->adapter, desired);
2074     }
2075   } else {
2076     if ((res = gst_pad_pull_range (wav->sinkpad, wav->offset,
2077                 desired, &buf)) != GST_FLOW_OK)
2078       goto pull_error;
2079 
2080     /* we may get a short buffer at the end of the file */
2081     if (gst_buffer_get_size (buf) < desired) {
2082       gsize size = gst_buffer_get_size (buf);
2083 
2084       GST_LOG_OBJECT (wav, "Got only %" G_GSIZE_FORMAT " bytes of data", size);
2085       if (size >= wav->blockalign) {
2086         if (wav->blockalign > 0) {
2087           buf = gst_buffer_make_writable (buf);
2088           gst_buffer_resize (buf, 0, size - (size % wav->blockalign));
2089         }
2090       } else {
2091         gst_buffer_unref (buf);
2092         goto found_eos;
2093       }
2094     }
2095   }
2096 
2097   obtained = gst_buffer_get_size (buf);
2098 
2099   /* our positions in bytes */
2100   pos = wav->offset - wav->datastart;
2101   nextpos = pos + obtained;
2102 
2103   /* update offsets, does not overflow. */
2104   buf = gst_buffer_make_writable (buf);
2105   GST_BUFFER_OFFSET (buf) = pos / wav->bytes_per_sample;
2106   GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample;
2107 
2108   /* first chunk of data? create the source pad. We do this only here so
2109    * we can detect broken .wav files with dts disguised as raw PCM (sigh) */
2110   if (G_UNLIKELY (wav->first)) {
2111     wav->first = FALSE;
2112     /* this will also push the segment events */
2113     gst_wavparse_add_src_pad (wav, buf);
2114   } else {
2115     /* If we have a pending start segment, send it now. */
2116     if (G_UNLIKELY (wav->start_segment != NULL)) {
2117       gst_pad_push_event (wav->srcpad, wav->start_segment);
2118       wav->start_segment = NULL;
2119     }
2120   }
2121 
2122   if (wav->bps > 0) {
2123     /* and timestamps if we have a bitrate, be careful for overflows */
2124     timestamp =
2125         gst_util_uint64_scale_ceil (pos, GST_SECOND, (guint64) wav->bps);
2126     next_timestamp =
2127         gst_util_uint64_scale_ceil (nextpos, GST_SECOND, (guint64) wav->bps);
2128     duration = next_timestamp - timestamp;
2129 
2130     /* update current running segment position */
2131     if (G_LIKELY (next_timestamp >= wav->segment.start))
2132       wav->segment.position = next_timestamp;
2133   } else if (wav->fact) {
2134     guint64 bps =
2135         gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
2136     /* and timestamps if we have a bitrate, be careful for overflows */
2137     timestamp = gst_util_uint64_scale_ceil (pos, GST_SECOND, bps);
2138     next_timestamp = gst_util_uint64_scale_ceil (nextpos, GST_SECOND, bps);
2139     duration = next_timestamp - timestamp;
2140   } else {
2141     /* no bitrate, all we know is that the first sample has timestamp 0, all
2142      * other positions and durations have unknown timestamp. */
2143     if (pos == 0)
2144       timestamp = 0;
2145     else
2146       timestamp = GST_CLOCK_TIME_NONE;
2147     duration = GST_CLOCK_TIME_NONE;
2148     /* update current running segment position with byte offset */
2149     if (G_LIKELY (nextpos >= wav->segment.start))
2150       wav->segment.position = nextpos;
2151   }
2152   if ((pos > 0) && wav->vbr) {
2153     /* don't set timestamps for VBR files if it's not the first buffer */
2154     timestamp = GST_CLOCK_TIME_NONE;
2155     duration = GST_CLOCK_TIME_NONE;
2156   }
2157   if (wav->discont) {
2158     GST_DEBUG_OBJECT (wav, "marking DISCONT");
2159     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
2160     wav->discont = FALSE;
2161   } else {
2162     GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
2163   }
2164 
2165   GST_BUFFER_TIMESTAMP (buf) = timestamp;
2166   GST_BUFFER_DURATION (buf) = duration;
2167 
2168   GST_LOG_OBJECT (wav,
2169       "Got buffer. timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT
2170       ", size:%" G_GSIZE_FORMAT, GST_TIME_ARGS (timestamp),
2171       GST_TIME_ARGS (duration), gst_buffer_get_size (buf));
2172 
2173   if ((res = gst_pad_push (wav->srcpad, buf)) != GST_FLOW_OK)
2174     goto push_error;
2175 
2176   if (obtained < wav->dataleft) {
2177     wav->offset += obtained;
2178     wav->dataleft -= obtained;
2179   } else {
2180     wav->offset += wav->dataleft;
2181     wav->dataleft = 0;
2182   }
2183 
2184   /* Iterate until need more data, so adapter size won't grow */
2185   if (wav->streaming) {
2186     GST_LOG_OBJECT (wav,
2187         "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT, wav->offset,
2188         wav->end_offset);
2189     goto iterate_adapter;
2190   }
2191   return res;
2192 
2193   /* ERROR */
2194 found_eos:
2195   {
2196     GST_DEBUG_OBJECT (wav, "found EOS");
2197     return GST_FLOW_EOS;
2198   }
2199 pull_error:
2200   {
2201     /* check if we got EOS */
2202     if (res == GST_FLOW_EOS)
2203       goto found_eos;
2204 
2205     GST_WARNING_OBJECT (wav,
2206         "Error getting %" G_GINT64_FORMAT " bytes from the "
2207         "sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, wav->dataleft);
2208     return res;
2209   }
2210 push_error:
2211   {
2212     GST_INFO_OBJECT (wav,
2213         "Error pushing on srcpad %s:%s, reason %s, is linked? = %d",
2214         GST_DEBUG_PAD_NAME (wav->srcpad), gst_flow_get_name (res),
2215         gst_pad_is_linked (wav->srcpad));
2216     return res;
2217   }
2218 }
2219 
2220 static void
gst_wavparse_loop(GstPad * pad)2221 gst_wavparse_loop (GstPad * pad)
2222 {
2223   GstFlowReturn ret;
2224   GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad));
2225   GstEvent *event;
2226   gchar *stream_id;
2227 
2228   GST_LOG_OBJECT (wav, "process data");
2229 
2230   switch (wav->state) {
2231     case GST_WAVPARSE_START:
2232       GST_INFO_OBJECT (wav, "GST_WAVPARSE_START");
2233       if ((ret = gst_wavparse_stream_init (wav)) != GST_FLOW_OK)
2234         goto pause;
2235 
2236       stream_id =
2237           gst_pad_create_stream_id (wav->srcpad, GST_ELEMENT_CAST (wav), NULL);
2238       event = gst_event_new_stream_start (stream_id);
2239       gst_event_set_group_id (event, gst_util_group_id_next ());
2240       gst_pad_push_event (wav->srcpad, event);
2241       g_free (stream_id);
2242 
2243       wav->state = GST_WAVPARSE_HEADER;
2244       /* fall-through */
2245 
2246     case GST_WAVPARSE_HEADER:
2247       GST_INFO_OBJECT (wav, "GST_WAVPARSE_HEADER");
2248       if ((ret = gst_wavparse_stream_headers (wav)) != GST_FLOW_OK)
2249         goto pause;
2250 
2251       wav->state = GST_WAVPARSE_DATA;
2252       GST_INFO_OBJECT (wav, "GST_WAVPARSE_DATA");
2253       /* fall-through */
2254 
2255     case GST_WAVPARSE_DATA:
2256       if ((ret = gst_wavparse_stream_data (wav, FALSE)) != GST_FLOW_OK)
2257         goto pause;
2258       break;
2259     default:
2260       g_assert_not_reached ();
2261   }
2262   return;
2263 
2264   /* ERRORS */
2265 pause:
2266   {
2267     const gchar *reason = gst_flow_get_name (ret);
2268 
2269     GST_DEBUG_OBJECT (wav, "pausing task, reason %s", reason);
2270     gst_pad_pause_task (pad);
2271 
2272     if (ret == GST_FLOW_EOS) {
2273       /* handle end-of-stream/segment */
2274       /* so align our position with the end of it, if there is one
2275        * this ensures a subsequent will arrive at correct base/acc time */
2276       if (wav->segment.format == GST_FORMAT_TIME) {
2277         if (wav->segment.rate > 0.0 &&
2278             GST_CLOCK_TIME_IS_VALID (wav->segment.stop))
2279           wav->segment.position = wav->segment.stop;
2280         else if (wav->segment.rate < 0.0)
2281           wav->segment.position = wav->segment.start;
2282       }
2283       if (wav->state == GST_WAVPARSE_START || !wav->caps) {
2284         GST_ELEMENT_ERROR (wav, STREAM, WRONG_TYPE, (NULL),
2285             ("No valid input found before end of stream"));
2286         gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
2287       } else {
2288         /* add pad before we perform EOS */
2289         if (G_UNLIKELY (wav->first)) {
2290           wav->first = FALSE;
2291           gst_wavparse_add_src_pad (wav, NULL);
2292         } else {
2293           /* If we have a pending start segment, send it now. Can happen if a seek
2294            * causes an immediate EOS */
2295           if (G_UNLIKELY (wav->start_segment != NULL)) {
2296             gst_pad_push_event (wav->srcpad, wav->start_segment);
2297             wav->start_segment = NULL;
2298           }
2299         }
2300 
2301         /* perform EOS logic */
2302         if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2303           GstClockTime stop;
2304 
2305           if ((stop = wav->segment.stop) == -1)
2306             stop = wav->segment.duration;
2307 
2308           gst_element_post_message (GST_ELEMENT_CAST (wav),
2309               gst_message_new_segment_done (GST_OBJECT_CAST (wav),
2310                   wav->segment.format, stop));
2311           gst_pad_push_event (wav->srcpad,
2312               gst_event_new_segment_done (wav->segment.format, stop));
2313         } else {
2314           gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
2315         }
2316       }
2317     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
2318       /* for fatal errors we post an error message, post the error
2319        * first so the app knows about the error first. */
2320       GST_ELEMENT_FLOW_ERROR (wav, ret);
2321       gst_pad_push_event (wav->srcpad, gst_event_new_eos ());
2322     }
2323     return;
2324   }
2325 }
2326 
2327 static GstFlowReturn
gst_wavparse_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)2328 gst_wavparse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
2329 {
2330   GstFlowReturn ret;
2331   GstWavParse *wav = GST_WAVPARSE (parent);
2332 
2333   GST_LOG_OBJECT (wav, "adapter_push %" G_GSIZE_FORMAT " bytes",
2334       gst_buffer_get_size (buf));
2335 
2336   gst_adapter_push (wav->adapter, buf);
2337 
2338   switch (wav->state) {
2339     case GST_WAVPARSE_START:
2340       GST_INFO_OBJECT (wav, "GST_WAVPARSE_START");
2341       if ((ret = gst_wavparse_parse_stream_init (wav)) != GST_FLOW_OK)
2342         goto done;
2343 
2344       if (wav->state != GST_WAVPARSE_HEADER)
2345         break;
2346 
2347       /* otherwise fall-through */
2348     case GST_WAVPARSE_HEADER:
2349       GST_INFO_OBJECT (wav, "GST_WAVPARSE_HEADER");
2350       if ((ret = gst_wavparse_stream_headers (wav)) != GST_FLOW_OK)
2351         goto done;
2352 
2353       if (!wav->got_fmt || wav->datastart == 0)
2354         break;
2355 
2356       wav->state = GST_WAVPARSE_DATA;
2357       GST_INFO_OBJECT (wav, "GST_WAVPARSE_DATA");
2358 
2359       /* fall-through */
2360     case GST_WAVPARSE_DATA:
2361       if (buf && GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))
2362         wav->discont = TRUE;
2363       if ((ret = gst_wavparse_stream_data (wav, FALSE)) != GST_FLOW_OK)
2364         goto done;
2365       break;
2366     default:
2367       g_return_val_if_reached (GST_FLOW_ERROR);
2368   }
2369 done:
2370   if (G_UNLIKELY (wav->abort_buffering)) {
2371     wav->abort_buffering = FALSE;
2372     ret = GST_FLOW_ERROR;
2373     /* sort of demux/parse error */
2374     GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL), ("unhandled buffer size"));
2375   }
2376 
2377   return ret;
2378 }
2379 
2380 static GstFlowReturn
gst_wavparse_flush_data(GstWavParse * wav)2381 gst_wavparse_flush_data (GstWavParse * wav)
2382 {
2383   GstFlowReturn ret = GST_FLOW_OK;
2384   guint av;
2385 
2386   if ((av = gst_adapter_available (wav->adapter)) > 0) {
2387     ret = gst_wavparse_stream_data (wav, TRUE);
2388   }
2389 
2390   return ret;
2391 }
2392 
2393 static gboolean
gst_wavparse_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)2394 gst_wavparse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2395 {
2396   GstWavParse *wav = GST_WAVPARSE (parent);
2397   gboolean ret = TRUE;
2398 
2399   GST_LOG_OBJECT (wav, "handling %s event", GST_EVENT_TYPE_NAME (event));
2400 
2401   switch (GST_EVENT_TYPE (event)) {
2402     case GST_EVENT_CAPS:
2403     {
2404       /* discard, we'll come up with proper src caps */
2405       gst_event_unref (event);
2406       break;
2407     }
2408     case GST_EVENT_SEGMENT:
2409     {
2410       gint64 start, stop, offset = 0, end_offset = -1;
2411       GstSegment segment;
2412 
2413       /* some debug output */
2414       gst_event_copy_segment (event, &segment);
2415       GST_DEBUG_OBJECT (wav, "received newsegment %" GST_SEGMENT_FORMAT,
2416           &segment);
2417 
2418       if (wav->state != GST_WAVPARSE_DATA) {
2419         GST_DEBUG_OBJECT (wav, "still starting, eating event");
2420         goto exit;
2421       }
2422 
2423       /* now we are either committed to TIME or BYTE format,
2424        * and we only expect a BYTE segment, e.g. following a seek */
2425       if (segment.format == GST_FORMAT_BYTES) {
2426         /* handle (un)signed issues */
2427         start = segment.start;
2428         stop = segment.stop;
2429         if (start > 0) {
2430           offset = start;
2431           start -= wav->datastart;
2432           start = MAX (start, 0);
2433         }
2434         if (stop > 0) {
2435           end_offset = stop;
2436           stop -= wav->datastart;
2437           stop = MAX (stop, 0);
2438         }
2439         if (wav->segment.format == GST_FORMAT_TIME) {
2440           guint64 bps = wav->bps;
2441 
2442           /* operating in format TIME, so we can convert */
2443           if (!bps && wav->fact)
2444             bps =
2445                 gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
2446           if (bps) {
2447             if (start >= 0)
2448               start =
2449                   gst_util_uint64_scale_ceil (start, GST_SECOND,
2450                   (guint64) wav->bps);
2451             if (stop >= 0)
2452               stop =
2453                   gst_util_uint64_scale_ceil (stop, GST_SECOND,
2454                   (guint64) wav->bps);
2455           }
2456         }
2457       } else {
2458         GST_DEBUG_OBJECT (wav, "unsupported segment format, ignoring");
2459         goto exit;
2460       }
2461 
2462       segment.start = start;
2463       segment.stop = stop;
2464 
2465       /* accept upstream's notion of segment and distribute along */
2466       segment.format = wav->segment.format;
2467       segment.time = segment.position = segment.start;
2468       segment.duration = wav->segment.duration;
2469       segment.base = gst_segment_to_running_time (&wav->segment,
2470           GST_FORMAT_TIME, wav->segment.position);
2471 
2472       gst_segment_copy_into (&segment, &wav->segment);
2473 
2474       /* also store the newsegment event for the streaming thread */
2475       if (wav->start_segment)
2476         gst_event_unref (wav->start_segment);
2477       GST_DEBUG_OBJECT (wav, "Storing newseg %" GST_SEGMENT_FORMAT, &segment);
2478       wav->start_segment = gst_event_new_segment (&segment);
2479 
2480       /* stream leftover data in current segment */
2481       gst_wavparse_flush_data (wav);
2482       /* and set up streaming thread for next one */
2483       wav->offset = offset;
2484       wav->end_offset = end_offset;
2485 
2486       if (wav->datasize > 0 && (wav->end_offset == -1
2487               || wav->end_offset > wav->datastart + wav->datasize))
2488         wav->end_offset = wav->datastart + wav->datasize;
2489 
2490       if (wav->end_offset != -1) {
2491         wav->dataleft = wav->end_offset - wav->offset;
2492       } else {
2493         /* infinity; upstream will EOS when done */
2494         wav->dataleft = G_MAXUINT64;
2495       }
2496     exit:
2497       gst_event_unref (event);
2498       break;
2499     }
2500     case GST_EVENT_EOS:
2501       if (wav->state == GST_WAVPARSE_START || !wav->caps) {
2502         GST_ELEMENT_ERROR (wav, STREAM, WRONG_TYPE, (NULL),
2503             ("No valid input found before end of stream"));
2504       } else {
2505         /* add pad if needed so EOS is seen downstream */
2506         if (G_UNLIKELY (wav->first)) {
2507           wav->first = FALSE;
2508           gst_wavparse_add_src_pad (wav, NULL);
2509         }
2510 
2511         /* stream leftover data in current segment */
2512         gst_wavparse_flush_data (wav);
2513       }
2514 
2515       /* fall-through */
2516     case GST_EVENT_FLUSH_STOP:
2517     {
2518       GstClockTime dur;
2519 
2520       if (wav->adapter)
2521         gst_adapter_clear (wav->adapter);
2522       wav->discont = TRUE;
2523       dur = wav->segment.duration;
2524       gst_segment_init (&wav->segment, wav->segment.format);
2525       wav->segment.duration = dur;
2526       /* fall-through */
2527     }
2528     default:
2529       ret = gst_pad_event_default (wav->sinkpad, parent, event);
2530       break;
2531   }
2532 
2533   return ret;
2534 }
2535 
2536 #if 0
2537 /* convert and query stuff */
2538 static const GstFormat *
2539 gst_wavparse_get_formats (GstPad * pad)
2540 {
2541   static const GstFormat formats[] = {
2542     GST_FORMAT_TIME,
2543     GST_FORMAT_BYTES,
2544     GST_FORMAT_DEFAULT,         /* a "frame", ie a set of samples per Hz */
2545     0
2546   };
2547 
2548   return formats;
2549 }
2550 #endif
2551 
2552 static gboolean
gst_wavparse_pad_convert(GstPad * pad,GstFormat src_format,gint64 src_value,GstFormat * dest_format,gint64 * dest_value)2553 gst_wavparse_pad_convert (GstPad * pad,
2554     GstFormat src_format, gint64 src_value,
2555     GstFormat * dest_format, gint64 * dest_value)
2556 {
2557   GstWavParse *wavparse;
2558   gboolean res = TRUE;
2559 
2560   wavparse = GST_WAVPARSE (GST_PAD_PARENT (pad));
2561 
2562   if (*dest_format == src_format) {
2563     *dest_value = src_value;
2564     return TRUE;
2565   }
2566 
2567   if ((wavparse->bps == 0) && !wavparse->fact)
2568     goto no_bps_fact;
2569 
2570   GST_INFO_OBJECT (wavparse, "converting value from %s to %s",
2571       gst_format_get_name (src_format), gst_format_get_name (*dest_format));
2572 
2573   switch (src_format) {
2574     case GST_FORMAT_BYTES:
2575       switch (*dest_format) {
2576         case GST_FORMAT_DEFAULT:
2577           *dest_value = src_value / wavparse->bytes_per_sample;
2578           /* make sure we end up on a sample boundary */
2579           *dest_value -= *dest_value % wavparse->bytes_per_sample;
2580           break;
2581         case GST_FORMAT_TIME:
2582           /* src_value + datastart = offset */
2583           GST_INFO_OBJECT (wavparse,
2584               "src=%" G_GINT64_FORMAT ", offset=%" G_GINT64_FORMAT, src_value,
2585               wavparse->offset);
2586           if (wavparse->bps > 0)
2587             *dest_value = gst_util_uint64_scale_ceil (src_value, GST_SECOND,
2588                 (guint64) wavparse->bps);
2589           else if (wavparse->fact) {
2590             guint64 bps = gst_util_uint64_scale_int_ceil (wavparse->datasize,
2591                 wavparse->rate, wavparse->fact);
2592 
2593             *dest_value =
2594                 gst_util_uint64_scale_int_ceil (src_value, GST_SECOND, bps);
2595           } else {
2596             res = FALSE;
2597           }
2598           break;
2599         default:
2600           res = FALSE;
2601           goto done;
2602       }
2603       break;
2604 
2605     case GST_FORMAT_DEFAULT:
2606       switch (*dest_format) {
2607         case GST_FORMAT_BYTES:
2608           *dest_value = src_value * wavparse->bytes_per_sample;
2609           break;
2610         case GST_FORMAT_TIME:
2611           *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
2612               (guint64) wavparse->rate);
2613           break;
2614         default:
2615           res = FALSE;
2616           goto done;
2617       }
2618       break;
2619 
2620     case GST_FORMAT_TIME:
2621       switch (*dest_format) {
2622         case GST_FORMAT_BYTES:
2623           if (wavparse->bps > 0)
2624             *dest_value = gst_util_uint64_scale (src_value,
2625                 (guint64) wavparse->bps, GST_SECOND);
2626           else {
2627             guint64 bps = gst_util_uint64_scale_int (wavparse->datasize,
2628                 wavparse->rate, wavparse->fact);
2629 
2630             *dest_value = gst_util_uint64_scale (src_value, bps, GST_SECOND);
2631           }
2632           /* make sure we end up on a sample boundary */
2633           *dest_value -= *dest_value % wavparse->blockalign;
2634           break;
2635         case GST_FORMAT_DEFAULT:
2636           *dest_value = gst_util_uint64_scale (src_value,
2637               (guint64) wavparse->rate, GST_SECOND);
2638           break;
2639         default:
2640           res = FALSE;
2641           goto done;
2642       }
2643       break;
2644 
2645     default:
2646       res = FALSE;
2647       goto done;
2648   }
2649 
2650 done:
2651   return res;
2652 
2653   /* ERRORS */
2654 no_bps_fact:
2655   {
2656     GST_DEBUG_OBJECT (wavparse, "bps 0 or no fact chunk, cannot convert");
2657     res = FALSE;
2658     goto done;
2659   }
2660 }
2661 
2662 /* handle queries for location and length in requested format */
2663 static gboolean
gst_wavparse_pad_query(GstPad * pad,GstObject * parent,GstQuery * query)2664 gst_wavparse_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
2665 {
2666   gboolean res = TRUE;
2667   GstWavParse *wav = GST_WAVPARSE (parent);
2668 
2669   GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));
2670 
2671   if (wav->state != GST_WAVPARSE_DATA) {
2672     return gst_pad_query_default (pad, parent, query);
2673   }
2674 
2675   switch (GST_QUERY_TYPE (query)) {
2676     case GST_QUERY_POSITION:
2677     {
2678       gint64 curb;
2679       gint64 cur;
2680       GstFormat format;
2681 
2682       /* this is not very precise, as we have pushed severla buffer upstream for prerolling */
2683       curb = wav->offset - wav->datastart;
2684       gst_query_parse_position (query, &format, NULL);
2685       GST_INFO_OBJECT (wav, "pos query at %" G_GINT64_FORMAT, curb);
2686 
2687       switch (format) {
2688         case GST_FORMAT_BYTES:
2689           format = GST_FORMAT_BYTES;
2690           cur = curb;
2691           break;
2692         default:
2693           res = gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb,
2694               &format, &cur);
2695           break;
2696       }
2697       if (res)
2698         gst_query_set_position (query, format, cur);
2699       break;
2700     }
2701     case GST_QUERY_DURATION:
2702     {
2703       gint64 duration = 0;
2704       GstFormat format;
2705 
2706       if (wav->ignore_length) {
2707         res = FALSE;
2708         break;
2709       }
2710 
2711       gst_query_parse_duration (query, &format, NULL);
2712 
2713       switch (format) {
2714         case GST_FORMAT_BYTES:{
2715           format = GST_FORMAT_BYTES;
2716           duration = wav->datasize;
2717           break;
2718         }
2719         case GST_FORMAT_TIME:
2720           if ((res = gst_wavparse_calculate_duration (wav))) {
2721             duration = wav->duration;
2722           }
2723           break;
2724         default:
2725           res = FALSE;
2726           break;
2727       }
2728       if (res)
2729         gst_query_set_duration (query, format, duration);
2730       break;
2731     }
2732     case GST_QUERY_CONVERT:
2733     {
2734       gint64 srcvalue, dstvalue;
2735       GstFormat srcformat, dstformat;
2736 
2737       gst_query_parse_convert (query, &srcformat, &srcvalue,
2738           &dstformat, &dstvalue);
2739       res = gst_wavparse_pad_convert (pad, srcformat, srcvalue,
2740           &dstformat, &dstvalue);
2741       if (res)
2742         gst_query_set_convert (query, srcformat, srcvalue, dstformat, dstvalue);
2743       break;
2744     }
2745     case GST_QUERY_SEEKING:{
2746       GstFormat fmt;
2747       gboolean seekable = FALSE;
2748 
2749       gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
2750       if (fmt == wav->segment.format) {
2751         if (wav->streaming) {
2752           GstQuery *q;
2753 
2754           q = gst_query_new_seeking (GST_FORMAT_BYTES);
2755           if ((res = gst_pad_peer_query (wav->sinkpad, q))) {
2756             gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
2757             GST_LOG_OBJECT (wav, "upstream BYTE seekable %d", seekable);
2758           }
2759           gst_query_unref (q);
2760         } else {
2761           GST_LOG_OBJECT (wav, "looping => seekable");
2762           seekable = TRUE;
2763           res = TRUE;
2764         }
2765       } else if (fmt == GST_FORMAT_TIME) {
2766         res = TRUE;
2767       }
2768       if (res) {
2769         gst_query_set_seeking (query, fmt, seekable, 0, wav->segment.duration);
2770       }
2771       break;
2772     }
2773     case GST_QUERY_SEGMENT:
2774     {
2775       GstFormat format;
2776       gint64 start, stop;
2777 
2778       format = wav->segment.format;
2779 
2780       start =
2781           gst_segment_to_stream_time (&wav->segment, format,
2782           wav->segment.start);
2783       if ((stop = wav->segment.stop) == -1)
2784         stop = wav->segment.duration;
2785       else
2786         stop = gst_segment_to_stream_time (&wav->segment, format, stop);
2787 
2788       gst_query_set_segment (query, wav->segment.rate, format, start, stop);
2789       res = TRUE;
2790       break;
2791     }
2792     default:
2793       res = gst_pad_query_default (pad, parent, query);
2794       break;
2795   }
2796   return res;
2797 }
2798 
2799 static gboolean
gst_wavparse_srcpad_event(GstPad * pad,GstObject * parent,GstEvent * event)2800 gst_wavparse_srcpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
2801 {
2802   GstWavParse *wavparse = GST_WAVPARSE (parent);
2803   gboolean res = FALSE;
2804 
2805   GST_DEBUG_OBJECT (wavparse, "%s event", GST_EVENT_TYPE_NAME (event));
2806 
2807   switch (GST_EVENT_TYPE (event)) {
2808     case GST_EVENT_SEEK:
2809       /* can only handle events when we are in the data state */
2810       if (wavparse->state == GST_WAVPARSE_DATA) {
2811         res = gst_wavparse_perform_seek (wavparse, event);
2812       }
2813       gst_event_unref (event);
2814       break;
2815 
2816     case GST_EVENT_TOC_SELECT:
2817     {
2818       char *uid = NULL;
2819       GstTocEntry *entry = NULL;
2820       GstEvent *seek_event;
2821       gint64 start_pos;
2822 
2823       if (!wavparse->toc) {
2824         GST_DEBUG_OBJECT (wavparse, "no TOC to select");
2825         return FALSE;
2826       } else {
2827         gst_event_parse_toc_select (event, &uid);
2828         if (uid != NULL) {
2829           GST_OBJECT_LOCK (wavparse);
2830           entry = gst_toc_find_entry (wavparse->toc, uid);
2831           if (entry == NULL) {
2832             GST_OBJECT_UNLOCK (wavparse);
2833             GST_WARNING_OBJECT (wavparse, "no TOC entry with given UID: %s",
2834                 uid);
2835             res = FALSE;
2836           } else {
2837             gst_toc_entry_get_start_stop_times (entry, &start_pos, NULL);
2838             GST_OBJECT_UNLOCK (wavparse);
2839             seek_event = gst_event_new_seek (1.0,
2840                 GST_FORMAT_TIME,
2841                 GST_SEEK_FLAG_FLUSH,
2842                 GST_SEEK_TYPE_SET, start_pos, GST_SEEK_TYPE_SET, -1);
2843             res = gst_wavparse_perform_seek (wavparse, seek_event);
2844             gst_event_unref (seek_event);
2845           }
2846           g_free (uid);
2847         } else {
2848           GST_WARNING_OBJECT (wavparse, "received empty TOC select event");
2849           res = FALSE;
2850         }
2851       }
2852       gst_event_unref (event);
2853       break;
2854     }
2855 
2856     default:
2857       res = gst_pad_push_event (wavparse->sinkpad, event);
2858       break;
2859   }
2860   return res;
2861 }
2862 
2863 static gboolean
gst_wavparse_sink_activate(GstPad * sinkpad,GstObject * parent)2864 gst_wavparse_sink_activate (GstPad * sinkpad, GstObject * parent)
2865 {
2866   GstWavParse *wav = GST_WAVPARSE (parent);
2867   GstQuery *query;
2868   gboolean pull_mode;
2869 
2870   if (wav->adapter) {
2871     gst_adapter_clear (wav->adapter);
2872     g_object_unref (wav->adapter);
2873     wav->adapter = NULL;
2874   }
2875 
2876   query = gst_query_new_scheduling ();
2877 
2878   if (!gst_pad_peer_query (sinkpad, query)) {
2879     gst_query_unref (query);
2880     goto activate_push;
2881   }
2882 
2883   pull_mode = gst_query_has_scheduling_mode_with_flags (query,
2884       GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
2885   gst_query_unref (query);
2886 
2887   if (!pull_mode)
2888     goto activate_push;
2889 
2890   GST_DEBUG_OBJECT (sinkpad, "activating pull");
2891   wav->streaming = FALSE;
2892   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
2893 
2894 activate_push:
2895   {
2896     GST_DEBUG_OBJECT (sinkpad, "activating push");
2897     wav->streaming = TRUE;
2898     wav->adapter = gst_adapter_new ();
2899     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
2900   }
2901 }
2902 
2903 
2904 static gboolean
gst_wavparse_sink_activate_mode(GstPad * sinkpad,GstObject * parent,GstPadMode mode,gboolean active)2905 gst_wavparse_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
2906     GstPadMode mode, gboolean active)
2907 {
2908   gboolean res;
2909 
2910   switch (mode) {
2911     case GST_PAD_MODE_PUSH:
2912       res = TRUE;
2913       break;
2914     case GST_PAD_MODE_PULL:
2915       if (active) {
2916         /* if we have a scheduler we can start the task */
2917         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_wavparse_loop,
2918             sinkpad, NULL);
2919       } else {
2920         res = gst_pad_stop_task (sinkpad);
2921       }
2922       break;
2923     default:
2924       res = FALSE;
2925       break;
2926   }
2927   return res;
2928 }
2929 
2930 static GstStateChangeReturn
gst_wavparse_change_state(GstElement * element,GstStateChange transition)2931 gst_wavparse_change_state (GstElement * element, GstStateChange transition)
2932 {
2933   GstStateChangeReturn ret;
2934   GstWavParse *wav = GST_WAVPARSE (element);
2935 
2936   switch (transition) {
2937     case GST_STATE_CHANGE_NULL_TO_READY:
2938       break;
2939     case GST_STATE_CHANGE_READY_TO_PAUSED:
2940       break;
2941     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2942       break;
2943     default:
2944       break;
2945   }
2946 
2947   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2948 
2949   switch (transition) {
2950     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2951       break;
2952     case GST_STATE_CHANGE_PAUSED_TO_READY:
2953       gst_wavparse_reset (wav);
2954       break;
2955     case GST_STATE_CHANGE_READY_TO_NULL:
2956       break;
2957     default:
2958       break;
2959   }
2960   return ret;
2961 }
2962 
2963 static void
gst_wavparse_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2964 gst_wavparse_set_property (GObject * object, guint prop_id,
2965     const GValue * value, GParamSpec * pspec)
2966 {
2967   GstWavParse *self;
2968 
2969   g_return_if_fail (GST_IS_WAVPARSE (object));
2970   self = GST_WAVPARSE (object);
2971 
2972   switch (prop_id) {
2973     case PROP_IGNORE_LENGTH:
2974       self->ignore_length = g_value_get_boolean (value);
2975       break;
2976     default:
2977       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
2978   }
2979 
2980 }
2981 
2982 static void
gst_wavparse_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2983 gst_wavparse_get_property (GObject * object, guint prop_id,
2984     GValue * value, GParamSpec * pspec)
2985 {
2986   GstWavParse *self;
2987 
2988   g_return_if_fail (GST_IS_WAVPARSE (object));
2989   self = GST_WAVPARSE (object);
2990 
2991   switch (prop_id) {
2992     case PROP_IGNORE_LENGTH:
2993       g_value_set_boolean (value, self->ignore_length);
2994       break;
2995     default:
2996       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
2997   }
2998 }
2999 
3000 static gboolean
plugin_init(GstPlugin * plugin)3001 plugin_init (GstPlugin * plugin)
3002 {
3003   return GST_ELEMENT_REGISTER (wavparse, plugin);
3004 }
3005 
3006 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3007     GST_VERSION_MINOR,
3008     wavparse,
3009     "Parse a .wav file into raw audio",
3010     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
3011