889d65147886fba5e4dd6a7a2a63a834715905b9
[platform/upstream/gst-plugins-good.git] / gst / wavparse / gstwavparse.c
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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <string.h>
26
27 #include <gstwavparse.h>
28
29 static void             gst_wavparse_base_init          (gpointer g_class);
30 static void             gst_wavparse_class_init         (GstWavParseClass *klass);
31 static void             gst_wavparse_init               (GstWavParse *wavparse);
32
33 static GstElementStateReturn
34                         gst_wavparse_change_state       (GstElement *element);
35
36 static const GstFormat* gst_wavparse_get_formats        (GstPad *pad);
37 static const GstQueryType *
38                         gst_wavparse_get_query_types    (GstPad *pad);
39 static gboolean         gst_wavparse_pad_query          (GstPad *pad, 
40                                                          GstQueryType type,
41                                                          GstFormat *format, 
42                                                          gint64 *value);
43 static gboolean         gst_wavparse_pad_convert        (GstPad *pad,
44                                                          GstFormat src_format,
45                                                          gint64 src_value,
46                                                          GstFormat *dest_format,
47                                                          gint64 *dest_value);
48
49 static void             gst_wavparse_loop               (GstElement *element);
50 static const GstEventMask*
51                         gst_wavparse_get_event_masks    (GstPad *pad);
52 static gboolean         gst_wavparse_srcpad_event       (GstPad *pad, GstEvent *event);
53 static void             gst_wavparse_get_property       (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
54
55 /* elementfactory information */
56 static GstElementDetails gst_wavparse_details = GST_ELEMENT_DETAILS (
57   ".wav demuxer",
58   "Codec/Demuxer",
59   "Parse a .wav file into raw audio",
60   "Erik Walthinsen <omega@cse.ogi.edu>"
61 );
62
63 static GstStaticPadTemplate sink_template_factory =
64 GST_STATIC_PAD_TEMPLATE (
65   "wavparse_sink",
66   GST_PAD_SINK,
67   GST_PAD_ALWAYS,
68   GST_STATIC_CAPS ("audio/x-wav")
69 );
70
71 static GstStaticPadTemplate src_template_factory =
72 GST_STATIC_PAD_TEMPLATE (
73   "wavparse_src",
74   GST_PAD_SRC,
75   GST_PAD_ALWAYS,
76   GST_STATIC_CAPS (
77     "audio/x-raw-int, "
78       "endianness = (int) little_endian, "
79       "signed = (boolean) { true, false }, "
80       "width = (int) { 8, 16 }, "
81       "depth = (int) { 8, 16 }, "
82       "rate = (int) [ 8000, 48000 ], "
83       "channels = (int) [ 1, 2 ]; "
84     "audio/mpeg, "
85       "mpegversion = (int) 1, "
86       "layer = (int) [ 1, 3 ], "
87       "rate = (int) [ 8000, 48000 ], "
88       "channels = (int) [ 1, 2 ]; "
89     "audio/x-alaw, "
90       "rate = (int) [ 8000, 48000 ], "
91       "channels = (int) [ 1, 2 ]; "
92     "audio/x-mulaw, "
93       "rate = (int) [ 8000, 48000 ], "
94       "channels = (int) [ 1, 2 ]"
95   )
96 );
97
98 /* WavParse signals and args */
99 enum {
100   /* FILL ME */
101   LAST_SIGNAL
102 };
103
104 enum {
105   PROP_0,
106 };
107
108 static GstElementClass *parent_class = NULL;
109 /*static guint gst_wavparse_signals[LAST_SIGNAL] = { 0 }; */
110
111 GType
112 gst_wavparse_get_type (void) 
113 {
114   static GType wavparse_type = 0;
115
116   if (!wavparse_type) {
117     static const GTypeInfo wavparse_info = {
118       sizeof(GstWavParseClass),
119       gst_wavparse_base_init,
120       NULL,
121       (GClassInitFunc) gst_wavparse_class_init,
122       NULL,
123       NULL,
124       sizeof(GstWavParse),
125       0,
126       (GInstanceInitFunc) gst_wavparse_init,
127     };
128     wavparse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavParse", &wavparse_info, 0);
129   }
130   return wavparse_type;
131 }
132
133
134 static void
135 gst_wavparse_base_init (gpointer g_class) 
136 {
137   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
138   
139   gst_element_class_set_details (element_class, &gst_wavparse_details);
140
141   /* register src pads */
142   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_template_factory));
143   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_template_factory));
144 }
145 static void
146 gst_wavparse_class_init (GstWavParseClass *klass) 
147 {
148   GstElementClass *gstelement_class;
149   GObjectClass *object_class;
150   
151   gstelement_class = (GstElementClass*) klass;
152   object_class = (GObjectClass *) klass;
153   
154   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
155
156   object_class->get_property = gst_wavparse_get_property;
157   gstelement_class->change_state = gst_wavparse_change_state;
158 }
159
160 static void 
161 gst_wavparse_init (GstWavParse *wavparse) 
162 {
163   /* sink */
164   wavparse->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_template_factory), "sink");
165   gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->sinkpad);
166
167   gst_pad_set_formats_function (wavparse->sinkpad, gst_wavparse_get_formats);
168   gst_pad_set_convert_function (wavparse->sinkpad, gst_wavparse_pad_convert);
169   gst_pad_set_query_type_function (wavparse->sinkpad, 
170                                    gst_wavparse_get_query_types);
171   gst_pad_set_query_function (wavparse->sinkpad, gst_wavparse_pad_query);
172
173   /* source */
174   wavparse->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&src_template_factory), "src");
175   gst_pad_use_explicit_caps (wavparse->srcpad);
176   gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->srcpad);
177   gst_pad_set_formats_function (wavparse->srcpad, gst_wavparse_get_formats);
178   gst_pad_set_convert_function (wavparse->srcpad, gst_wavparse_pad_convert);
179   gst_pad_set_query_type_function (wavparse->srcpad,
180                                    gst_wavparse_get_query_types);
181   gst_pad_set_query_function (wavparse->srcpad, gst_wavparse_pad_query);
182   gst_pad_set_event_function (wavparse->srcpad, gst_wavparse_srcpad_event);
183   gst_pad_set_event_mask_function (wavparse->srcpad, gst_wavparse_get_event_masks);
184
185   gst_element_set_loop_function (GST_ELEMENT (wavparse), gst_wavparse_loop);
186
187   wavparse->state = GST_WAVPARSE_UNKNOWN;
188   wavparse->bps = 0;
189   wavparse->seek_pending = FALSE;
190   wavparse->seek_offset = 0;
191 }
192
193 static void
194 gst_wavparse_get_property (GObject *object,
195                            guint prop_id,
196                            GValue *value,
197                            GParamSpec *pspec)
198 {
199   GstWavParse *wavparse;
200
201   wavparse = GST_WAVPARSE (object);
202
203   switch (prop_id) {
204   default:
205     break;
206   }
207 }
208
209 #if 0
210 static void
211 gst_wavparse_parse_adtl (GstWavParse *wavparse,
212                          int len)
213 {
214   guint32 got_bytes;
215   GstByteStream *bs = wavparse->bs;
216   gst_riff_chunk *temp_chunk, chunk;
217   guint8 *tempdata;
218   struct _gst_riff_labl labl, *temp_labl;
219   struct _gst_riff_ltxt ltxt, *temp_ltxt;
220   struct _gst_riff_note note, *temp_note;
221   char *label_name;
222   GstProps *props;
223   GstPropsEntry *entry;
224   GstCaps *new_caps;
225   GList *caps = NULL;
226
227   props = wavparse->metadata->properties;
228   
229   while (len > 0) {
230     got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk));
231     if (got_bytes != sizeof (gst_riff_chunk)) {
232       return;
233     }
234     temp_chunk = (gst_riff_chunk *) tempdata;
235     
236     chunk.id = GUINT32_FROM_LE (temp_chunk->id);
237     chunk.size = GUINT32_FROM_LE (temp_chunk->size);
238
239     if (chunk.size == 0) {
240       gst_bytestream_flush (bs, sizeof (gst_riff_chunk));
241       len -= sizeof (gst_riff_chunk);
242       continue;
243     }
244     
245     switch  (chunk.id) {
246     case GST_RIFF_adtl_labl:
247       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (struct _gst_riff_labl));
248       if (got_bytes != sizeof (struct _gst_riff_labl)) {
249         return;
250       }
251       
252       temp_labl = (struct _gst_riff_labl *) tempdata;
253       labl.id = GUINT32_FROM_LE (temp_labl->id);
254       labl.size = GUINT32_FROM_LE (temp_labl->size);
255       labl.identifier = GUINT32_FROM_LE (temp_labl->identifier);
256
257       gst_bytestream_flush (bs, sizeof (struct _gst_riff_labl));
258       len -= sizeof (struct _gst_riff_labl);
259       
260       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, labl.size - 4);
261       if (got_bytes != labl.size - 4) {
262         return;
263       }
264
265       label_name = (char *) tempdata;
266       
267       gst_bytestream_flush (bs, ((labl.size - 4) + 1) & ~1);
268       len -= (( (labl.size - 4) + 1) & ~1);
269
270       new_caps = gst_caps_new ("label",
271                                                                                                                          "application/x-gst-metadata",
272                                                                                                                          gst_props_new (
273                                                                                                                                  "identifier", G_TYPE_INT (labl.identifier),
274                                                                                                                                  "name", G_TYPE_STRING (label_name),
275                                                                                                                                  NULL));
276       
277       if (gst_props_get (props, "labels", &caps, NULL)) {
278                                 caps = g_list_append (caps, new_caps);
279       } else {
280                                 caps = g_list_append (NULL, new_caps);
281                                 
282                                 entry = gst_props_entry_new ("labels", GST_PROPS_GLIST (caps));
283                                 gst_props_add_entry (props, entry);
284       }
285       
286       break;
287       
288     case GST_RIFF_adtl_ltxt:
289       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (struct _gst_riff_ltxt));
290       if (got_bytes != sizeof (struct _gst_riff_ltxt)) {
291         return;
292       }
293       
294       temp_ltxt = (struct _gst_riff_ltxt *) tempdata;
295       ltxt.id = GUINT32_FROM_LE (temp_ltxt->id);
296       ltxt.size = GUINT32_FROM_LE (temp_ltxt->size);
297       ltxt.identifier = GUINT32_FROM_LE (temp_ltxt->identifier);
298       ltxt.length = GUINT32_FROM_LE (temp_ltxt->length);
299       ltxt.purpose = GUINT32_FROM_LE (temp_ltxt->purpose);
300       ltxt.country = GUINT16_FROM_LE (temp_ltxt->country);
301       ltxt.language = GUINT16_FROM_LE (temp_ltxt->language);
302       ltxt.dialect = GUINT16_FROM_LE (temp_ltxt->dialect);
303       ltxt.codepage = GUINT16_FROM_LE (temp_ltxt->codepage);
304
305       gst_bytestream_flush (bs, sizeof (struct _gst_riff_ltxt));
306       len -= sizeof (struct _gst_riff_ltxt);
307
308                         if (ltxt.size - 20 > 0) {
309                                 got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, ltxt.size - 20);
310                                 if (got_bytes != ltxt.size - 20) {
311                                         return;
312                                 }
313                                 
314                                 gst_bytestream_flush (bs, ((ltxt.size - 20) + 1) & ~1);
315                                 len -= (( (ltxt.size - 20) + 1) & ~1);
316
317                                 label_name = (char *) tempdata;
318                         } else {
319                                 label_name = "";
320                         }
321                         
322       new_caps = gst_caps_new ("ltxt",
323                                                                                                                          "application/x-gst-metadata",
324                                                                                                                          gst_props_new (
325                                                                                                                                  "identifier", G_TYPE_INT (ltxt.identifier),
326                                                                                                                                  "name", G_TYPE_STRING (label_name),
327                                                                                                                                  "length", G_TYPE_INT (ltxt.length),
328                                                                                                                                  NULL));
329                         
330       if (gst_props_get (props, "ltxts", &caps, NULL)) {
331                                 caps = g_list_append (caps, new_caps);
332       } else {
333                                 caps = g_list_append (NULL, new_caps);
334                                 
335                                 entry = gst_props_entry_new ("ltxts", GST_PROPS_GLIST (caps));
336                                 gst_props_add_entry (props, entry);
337       }
338       
339       break;
340                         
341     case GST_RIFF_adtl_note:
342       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (struct _gst_riff_note));
343       if (got_bytes != sizeof (struct _gst_riff_note)) {
344                                 return;
345       }
346       
347       temp_note = (struct _gst_riff_note *) tempdata;
348       note.id = GUINT32_FROM_LE (temp_note->id);
349       note.size = GUINT32_FROM_LE (temp_note->size);
350       note.identifier = GUINT32_FROM_LE (temp_note->identifier);
351       
352       gst_bytestream_flush (bs, sizeof (struct _gst_riff_note));
353       len -= sizeof (struct _gst_riff_note);
354                         
355       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, note.size - 4);
356       if (got_bytes != note.size - 4) {
357                                 return;
358       }
359                         
360       gst_bytestream_flush (bs, ((note.size - 4) + 1) & ~1);
361       len -= (( (note.size - 4) + 1) & ~1);
362                         
363       label_name = (char *) tempdata;
364       
365       new_caps = gst_caps_new ("note",
366                                                                                                                          "application/x-gst-metadata",
367                                                                                                                          gst_props_new (
368                                                                                                                                  "identifier", G_TYPE_INT (note.identifier),
369                                                                                                                                  "name", G_TYPE_STRING (label_name),
370                                                                                                                                  NULL));
371       
372       if (gst_props_get (props, "notes", &caps, NULL)) {
373                                 caps = g_list_append (caps, new_caps);
374       } else {
375                                 caps = g_list_append (NULL, new_caps);
376                                 
377                                 entry = gst_props_entry_new ("notes", GST_PROPS_GLIST (caps));
378                                 gst_props_add_entry (props, entry);
379       }
380                         
381       break;      
382       
383     default:
384       g_print ("Unknown chunk: " GST_FOURCC_FORMAT "\n", GST_FOURCC_ARGS(chunk.id));
385       return;
386     }
387   }
388
389   g_object_notify (G_OBJECT (wavparse), "metadata");
390 }
391 #endif
392
393 #if 0
394 static void
395 gst_wavparse_parse_info (GstWavParse *wavparse,
396                                                                                                  int len)
397 {
398   gst_riff_chunk *temp_chunk, chunk;
399   GstByteStream *bs = wavparse->bs;
400   guint8 *tempdata;
401   guint32 got_bytes;
402   char *name, *type;
403   
404   while (len > 0) {
405     got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk));
406     temp_chunk = (gst_riff_chunk *) tempdata;
407     
408     chunk.id = GUINT32_FROM_LE (temp_chunk->id);
409     chunk.size = GUINT32_FROM_LE (temp_chunk->size);
410
411     gst_bytestream_flush (bs, sizeof (gst_riff_chunk));
412     if (got_bytes != sizeof (gst_riff_chunk)) {
413       return;
414     }
415                                                  
416     /* move our pointer on past the header */
417     len -= sizeof (gst_riff_chunk);
418     
419     if (chunk.size == 0) {
420       continue;
421     }
422     
423     got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, chunk.size);
424     name = (char *) tempdata;
425     if (got_bytes != chunk.size) {
426       return;
427     }
428     
429     /* move our pointer on past the data ... on an even boundary */
430                 gst_bytestream_flush (bs, (chunk.size + 1) & ~1);
431     len -= ((chunk.size + 1) & ~1);
432
433     /* We now have an info string in 'name' of type chunk.id
434        - find type */
435     switch (chunk.id) {
436     case GST_RIFF_INFO_IARL:
437       type = "Location";
438       break;
439       
440     case GST_RIFF_INFO_IART:
441       type = "Artist";
442       break;
443       
444     case GST_RIFF_INFO_ICMS:
445       type = "Commissioner";
446       break;
447       
448     case GST_RIFF_INFO_ICMT:
449       type = "Comment";
450       break;
451       
452     case GST_RIFF_INFO_ICOP:
453       type = "Copyright";
454       break;
455       
456     case GST_RIFF_INFO_ICRD:
457       type = "Creation Date";
458       break;
459       
460     case GST_RIFF_INFO_IENG:
461       type = "Engineer";
462       break;
463       
464     case GST_RIFF_INFO_IGNR:
465       type = "Genre";
466       break;
467       
468     case GST_RIFF_INFO_IKEY:
469       type = "Keywords";
470       break;
471       
472     case GST_RIFF_INFO_INAM:
473       type = "Title"; /* name */
474       break;
475       
476     case GST_RIFF_INFO_IPRD:
477       type = "Product";
478       break;
479       
480     case GST_RIFF_INFO_ISBJ:
481       type = "Subject";
482       break;
483       
484     case GST_RIFF_INFO_ISFT:
485       type = "Software";
486       break;
487       
488     case GST_RIFF_INFO_ITCH:
489       type = "Technician";
490       break;
491       
492     default:
493       g_print ("Unknown: %4.4s\n", (char *) &chunk.id);
494       type = NULL;
495       break;
496     }
497
498     if (type) {
499       GstPropsEntry *entry;
500
501       entry = gst_props_entry_new (type, G_TYPE_STRING (name));
502       gst_props_add_entry (wavparse->metadata->properties, entry);
503     }
504   }
505
506   g_object_notify (G_OBJECT (wavparse), "metadata");
507 }
508 #endif
509
510 #if 0
511 static void
512 gst_wavparse_parse_cues (GstWavParse *wavparse,
513                          int len)
514 {
515   guint32 got_bytes;
516   GstByteStream *bs = wavparse->bs;
517   struct _gst_riff_cue *temp_cue, cue;
518   struct _gst_riff_cuepoints *points;
519   guint8 *tempdata;
520   int i;
521   GList *cues = NULL;
522   GstPropsEntry *entry;
523
524   while (len > 0) {
525     int required;
526     
527     got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (struct _gst_riff_cue));
528     temp_cue = (struct _gst_riff_cue *) tempdata;
529
530     /* fixup for our big endian friends */
531     cue.id = GUINT32_FROM_LE (temp_cue->id);
532     cue.size = GUINT32_FROM_LE (temp_cue->size);
533     cue.cuepoints = GUINT32_FROM_LE (temp_cue->cuepoints);
534
535     gst_bytestream_flush (bs, sizeof (struct _gst_riff_cue));
536     if (got_bytes != sizeof (struct _gst_riff_cue)) {
537       return;
538     }
539
540     len -= sizeof (struct _gst_riff_cue);
541
542     /* -4 because cue.size contains the cuepoints size
543        and we've already flushed that out of the system */
544     required = cue.size - 4;
545     got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, required);
546     gst_bytestream_flush (bs, ((required) + 1) & ~1);
547     if (got_bytes != required) {
548       return;
549     }
550
551     len -= ( ((cue.size - 4) + 1) & ~1);
552
553     /* now we have an array of struct _gst_riff_cuepoints in tempdata */
554     points = (struct _gst_riff_cuepoints *) tempdata;
555     
556     for (i = 0; i < cue.cuepoints; i++) {
557       GstCaps *caps;
558
559       caps = gst_caps_new ("cues",
560                                                                                                          "application/x-gst-metadata",
561                                                                                                          gst_props_new (
562                                                                                                                  "identifier", G_TYPE_INT (points[i].identifier),
563                                                                                                                  "position", G_TYPE_INT (points[i].offset),
564                                                                                                                  NULL));
565       cues = g_list_append (cues, caps);
566     }
567     
568     entry = gst_props_entry_new ("cues", GST_PROPS_GLIST (cues));
569     gst_props_add_entry (wavparse->metadata->properties, entry);
570   }
571   
572   g_object_notify (G_OBJECT (wavparse), "metadata");
573 }
574 #endif
575
576 static void
577 gst_wavparse_parse_fmt (GstWavParse *wavparse, guint size)
578 {
579   GstWavParseFormat *format;
580   GstCaps *caps = NULL;
581   guint8 *fmtdata;
582   GstByteStream *bs = wavparse->bs;
583   guint32 got_bytes;
584
585   got_bytes = gst_bytestream_peek_bytes (bs, &fmtdata, sizeof (GstWavParseFormat));
586   format = (GstWavParseFormat *) fmtdata;
587
588   if (got_bytes == sizeof (GstWavParseFormat)) {
589     gst_bytestream_flush (bs, size);
590     wavparse->bps = GUINT16_FROM_LE (format->wBlockAlign);
591     wavparse->rate = GUINT32_FROM_LE (format->dwSamplesPerSec);
592     wavparse->channels = GUINT16_FROM_LE (format->wChannels);
593     wavparse->width = GUINT16_FROM_LE (format->wBitsPerSample);
594     wavparse->format = GINT16_FROM_LE (format->wFormatTag);
595
596     /* set the caps on the src pad */
597     /* FIXME: handle all of the other formats as well */
598     switch (wavparse->format) {
599     case GST_RIFF_WAVE_FORMAT_ALAW:
600     case GST_RIFF_WAVE_FORMAT_MULAW: {
601       char *mime = (wavparse->format == GST_RIFF_WAVE_FORMAT_ALAW) ?
602                                 "audio/x-alaw" : "audio/x-mulaw";
603       if (wavparse->width != 8) {
604                                 g_warning ("Ignoring invalid width %d", wavparse->width);
605                                 return;
606       }
607                         
608       caps = gst_caps_new_simple (mime,
609         "rate", G_TYPE_INT, wavparse->rate,
610         "channels", G_TYPE_INT, wavparse->channels,
611         NULL);
612     }
613       break;
614                         
615     case GST_RIFF_WAVE_FORMAT_PCM:
616       caps = gst_caps_new_simple ("audio/x-raw-int",
617         "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
618         "signed", G_TYPE_BOOLEAN, (wavparse->width > 8) ? TRUE : FALSE,
619         "width", G_TYPE_INT, wavparse->width,
620         "depth", G_TYPE_INT, wavparse->width,
621         "rate", G_TYPE_INT, wavparse->rate,
622         "channels", G_TYPE_INT, wavparse->channels,
623         NULL);
624       break;
625                         
626     case GST_RIFF_WAVE_FORMAT_MPEGL12:
627     case GST_RIFF_WAVE_FORMAT_MPEGL3: {
628       int layer = (wavparse->format == GST_RIFF_WAVE_FORMAT_MPEGL12) ? 2 : 3;
629                         
630       caps = gst_caps_new_simple ("audio/mpeg",
631           "mpegversion", G_TYPE_INT, 1,
632           "layer", G_TYPE_INT, layer,
633           "rate", G_TYPE_INT, wavparse->rate,
634           "channels", G_TYPE_INT, wavparse->channels,
635           NULL);
636     }
637       break;
638                         
639     default:
640       GST_ELEMENT_ERROR (wavparse, STREAM, NOT_IMPLEMENTED, (NULL), ("format %d not handled", wavparse->format));
641       return;
642     }
643
644     if (caps) {
645       gst_pad_set_explicit_caps (wavparse->srcpad, caps);
646       gst_caps_free (caps);
647     }
648
649     GST_DEBUG ("frequency %d, channels %d", wavparse->rate, wavparse->channels);
650   }
651 }
652
653 static gboolean
654 gst_wavparse_handle_sink_event (GstWavParse *wavparse)
655 {
656   guint32 remaining;
657   GstEvent *event;
658   GstEventType type;
659   gboolean res = TRUE;
660         
661   gst_bytestream_get_status (wavparse->bs, &remaining, &event);
662         
663   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
664   GST_DEBUG ("wavparse: event %p %d", event, type);
665         
666   switch (type) {
667   case GST_EVENT_EOS:
668     gst_bytestream_flush (wavparse->bs, remaining);
669     gst_pad_event_default (wavparse->sinkpad, event);
670     res = FALSE;
671     goto done;
672
673   case GST_EVENT_FLUSH:
674     g_warning ("Wavparse: Flush event");
675     break;
676
677   default:
678     g_warning ("Wavparse: Unhandled event %d", type);
679     break;
680   }
681
682   gst_event_unref (event);
683
684  done:
685   return res;
686 }
687     
688 static void
689 gst_wavparse_loop (GstElement *element)
690 {
691   GstWavParse *wavparse;
692   gst_riff_riff chunk;
693   guint32 flush = 0;
694   guint32 got_bytes;
695   GstByteStream *bs;
696
697   wavparse = GST_WAVPARSE (element);
698
699   bs = wavparse->bs;
700
701   if (wavparse->seek_pending) {
702     GST_DEBUG ("wavparse: seek pending to %" G_GINT64_FORMAT " %08llx",
703                wavparse->seek_offset,
704                (unsigned long long) wavparse->seek_offset);
705     
706     if (!gst_bytestream_seek (bs, wavparse->seek_offset, GST_SEEK_METHOD_SET)) {
707       GST_INFO ("wavparse: Could not seek");
708     }
709                 
710     wavparse->seek_pending = FALSE;
711   }
712         
713   if (wavparse->state == GST_WAVPARSE_DATA) {
714     GstBuffer *buf;
715     int desired;
716     
717     /* This seems to want the whole chunk,
718        Will this screw up streaming?
719        Does anyone care about streaming wavs?
720        FIXME: Should we have a decent buffer size? */
721                 
722 #define MAX_BUFFER_SIZE 1024
723
724     if (wavparse->dataleft > 0) {
725       desired = MIN (wavparse->dataleft, MAX_BUFFER_SIZE);
726       got_bytes = gst_bytestream_peek (bs, &buf, desired);
727
728       if (got_bytes != desired) {
729         /* EOS? */
730         GstEvent *event;
731         guint32 remaining;
732         gst_bytestream_get_status (bs, &remaining, &event);
733         if (event && GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
734           gst_pad_event_default (wavparse->sinkpad, event);
735         } else {
736           GST_ELEMENT_ERROR (element, RESOURCE, READ, (NULL), (NULL));
737         }
738         return;
739       }
740
741       wavparse->dataleft -= got_bytes;
742       wavparse->byteoffset += got_bytes;
743
744       gst_bytestream_flush (bs, got_bytes);
745                         
746       gst_pad_push (wavparse->srcpad, GST_DATA (buf));
747       return;
748     } else {
749       wavparse->state = GST_WAVPARSE_OTHER;
750     }
751   }
752
753   do {
754     gst_riff_riff *temp_chunk;
755     guint8 *tempdata;
756     guint32 skipsize;
757                 
758     /* read first two dwords to get chunktype and size */
759     while (TRUE) {
760       got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk));
761       temp_chunk = (gst_riff_riff *) tempdata;
762
763       if (got_bytes < sizeof (gst_riff_chunk)) {
764                                 if (!gst_wavparse_handle_sink_event (wavparse)) {
765                                         return;
766                                 }
767       } else {
768                                 break;
769       }
770     }
771                 
772     chunk.id = GUINT32_FROM_LE (temp_chunk->id);
773     chunk.size = GUINT32_FROM_LE (temp_chunk->size);
774
775     switch (chunk.id) {
776     case GST_RIFF_TAG_RIFF:
777     case GST_RIFF_TAG_LIST:
778       /* Read complete list chunk */
779       while (TRUE) {
780         got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_list));
781         temp_chunk = (gst_riff_riff *) tempdata;
782         if (got_bytes < sizeof (gst_riff_list)) {
783           if (!gst_wavparse_handle_sink_event (wavparse)) {
784             return;
785           }
786         } else {
787           break;
788         }
789       }
790                         
791       chunk.type = GUINT32_FROM_LE (temp_chunk->type);
792       skipsize = sizeof (gst_riff_list);
793       break;
794
795     case GST_RIFF_TAG_cue:
796       skipsize = 0;
797       break;
798       
799     default:
800       skipsize = sizeof (gst_riff_chunk);
801       break;
802     }
803     gst_bytestream_flush (bs, skipsize);
804   } while (FALSE);
805
806   /* need to flush an even number of bytes at the end */
807   flush = (chunk.size + 1) & ~1;
808
809   switch (wavparse->state) {
810   case GST_WAVPARSE_START:
811     if (chunk.id != GST_RIFF_TAG_RIFF &&
812                                 chunk.type != GST_RIFF_RIFF_WAVE) {
813       GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
814                            ("chunk.id %08x chunk.type %08x", chunk.id, chunk.type));
815       return;
816     }
817                 
818     wavparse->state = GST_WAVPARSE_OTHER;
819     /* We are not going to flush lists */
820     flush = 0;
821     break;
822
823   case GST_WAVPARSE_OTHER:
824     GST_DEBUG ("riff tag: %4.4s %08x", (char *) &chunk.id, chunk.size);
825                 
826     switch (chunk.id) {
827     case GST_RIFF_TAG_data:
828       wavparse->state = GST_WAVPARSE_DATA;
829       wavparse->dataleft = chunk.size;
830       wavparse->byteoffset = 0;
831       flush = 0;
832       break;
833       
834     case GST_RIFF_TAG_fmt:
835       gst_wavparse_parse_fmt (wavparse, chunk.size);
836       flush = 0;
837       break;
838
839     case GST_RIFF_TAG_cue:
840       //gst_wavparse_parse_cues (wavparse, chunk.size);
841       break;
842       
843     case GST_RIFF_TAG_LIST:
844       GST_DEBUG ("list type: %4.4s", (char *) &chunk.type);
845       switch (chunk.type) {
846       case GST_RIFF_LIST_INFO:
847         //gst_wavparse_parse_info (wavparse, chunk.size - 4);
848         //flush = 0;
849
850         break;
851                                 
852       case GST_RIFF_LIST_adtl:
853         //gst_wavparse_parse_adtl (wavparse, chunk.size - 4);
854         //flush = 0;
855         break;
856
857       default:
858         //flush = 0;
859         break;
860       }
861       
862     default:
863       GST_DEBUG ("  *****  unknown chunkid %08x", chunk.id);
864       //flush = 0;
865       break;      
866     }
867     break;
868                 
869   case GST_WAVPARSE_DATA:
870                 /* Should have been handled up there ^^^^ */
871                 flush = 0;
872     break;
873                 
874   default:
875     /* Unknown */
876     g_warning ("Unknown state %d\n", wavparse->state);
877     //GST_DEBUG ("  *****  unknown chunkid %08x", chunk.id);
878     break;
879   }
880
881   if (flush > 0) {
882     gboolean res;
883
884     res = gst_bytestream_flush (bs, flush);
885     if (!res) {
886       guint32 remaining;
887       GstEvent *event;
888
889       gst_bytestream_get_status (bs, &remaining, &event);
890       gst_event_unref (event);
891     } 
892   }
893 }
894
895 /* convert and query stuff */
896 static const GstFormat *
897 gst_wavparse_get_formats (GstPad *pad)
898 {
899   static GstFormat formats[] = {
900     GST_FORMAT_TIME,
901     GST_FORMAT_BYTES,
902     GST_FORMAT_DEFAULT, /* a "frame", ie a set of samples per Hz */
903     0,
904     0
905   };
906   return formats;
907 }
908
909 static gboolean
910 gst_wavparse_pad_convert (GstPad *pad,
911                           GstFormat src_format, gint64 src_value,
912                           GstFormat *dest_format, gint64 *dest_value)
913 {
914   gint bytes_per_sample;
915   glong byterate;
916   GstWavParse *wavparse;
917   const GstFormat *formats;
918   gboolean src_format_ok = FALSE;
919   gboolean dest_format_ok = FALSE;
920
921   wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
922   
923   bytes_per_sample = wavparse->channels * wavparse->width / 8;
924   if (bytes_per_sample == 0) {
925     GST_DEBUG ("bytes_per_sample 0, probably an mp3 - channels %d, width %d",
926                wavparse->channels, wavparse->width);
927     return FALSE;
928   }
929   byterate = (glong) (bytes_per_sample * wavparse->rate);
930   if (byterate == 0) {
931     g_warning ("byterate is 0, internal error\n");
932     return FALSE;
933   }
934   GST_DEBUG ("bytes per sample: %d", bytes_per_sample);
935   /* check if both src_format and sink_format are in the supported formats */
936   formats = gst_pad_get_formats (pad);
937
938   while (formats && *formats) {
939     if (src_format == *formats) { src_format_ok = TRUE; }
940     if (*dest_format == *formats) { dest_format_ok = TRUE; }
941     formats++;
942   }
943   if (!src_format_ok || !dest_format_ok) {
944     GST_DEBUG ("src or dest format not supported");
945     return FALSE;
946   }
947
948
949   switch (src_format) {
950     case GST_FORMAT_BYTES:
951       if (*dest_format == GST_FORMAT_DEFAULT)
952         *dest_value = src_value / bytes_per_sample;
953       else if (*dest_format == GST_FORMAT_TIME)
954         *dest_value = src_value * GST_SECOND / byterate;
955       else {
956         GST_DEBUG ("can't convert from bytes to other than units/time");
957          return FALSE;
958       }
959
960       break;
961     case GST_FORMAT_DEFAULT:
962       if (*dest_format == GST_FORMAT_BYTES)
963         *dest_value = src_value * bytes_per_sample;
964       else if (*dest_format == GST_FORMAT_TIME)
965         *dest_value = src_value * GST_SECOND / wavparse->rate;
966       else {
967         GST_DEBUG ("can't convert from units to other than bytes/time");
968          return FALSE;
969       }
970       break;
971     case GST_FORMAT_TIME:
972       if (*dest_format == GST_FORMAT_BYTES)
973         *dest_value = src_value * byterate / GST_SECOND;
974       else if (*dest_format == GST_FORMAT_DEFAULT)
975         *dest_value = src_value * wavparse->rate / GST_SECOND;
976       else {
977         GST_DEBUG ("can't convert from time to other than bytes/units");
978          return FALSE;
979       }
980
981       *dest_value = *dest_value & ~(bytes_per_sample - 1);
982       break;
983     default:
984       g_warning ("unhandled format for wavparse\n");
985       return FALSE;
986   }
987   return TRUE;
988 }
989       
990 static const GstQueryType *
991 gst_wavparse_get_query_types (GstPad *pad)
992 {
993   static const GstQueryType types[] = {
994     GST_QUERY_TOTAL,
995     GST_QUERY_POSITION,
996     0
997   };
998   return types;
999 }
1000
1001 /* handle queries for location and length in requested format */
1002 static gboolean
1003 gst_wavparse_pad_query (GstPad *pad, GstQueryType type,
1004                         GstFormat *format, gint64 *value)
1005 {
1006   GstFormat peer_format = GST_FORMAT_BYTES;
1007   gint64 peer_value;
1008   GstWavParse *wavparse;
1009
1010   /* probe sink's peer pad, convert value, and that's it :) */
1011   /* FIXME: ideally we'd loop over possible formats of peer instead
1012    * of only using BYTE */
1013   
1014   /* only support byte, time and unit queries */
1015   wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
1016   if (!gst_pad_query (GST_PAD_PEER (wavparse->sinkpad), type, 
1017                       &peer_format, &peer_value)) {
1018     GST_DEBUG ("Could not query sink pad's peer");
1019     return FALSE;
1020   }
1021   if (!gst_pad_convert (wavparse->sinkpad, peer_format, peer_value,
1022                         format, value)) {
1023     GST_DEBUG ("Could not convert sink pad's peer");
1024     return FALSE;
1025   }
1026   GST_DEBUG ("pad_query done, value %" G_GINT64_FORMAT "\n", *value);
1027   return TRUE;
1028 }
1029
1030 static const GstEventMask*
1031 gst_wavparse_get_event_masks (GstPad *pad)
1032
1033   static const GstEventMask gst_wavparse_src_event_masks[] = {
1034     { GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
1035                       GST_SEEK_FLAG_FLUSH },
1036     { 0, }
1037   };
1038   return gst_wavparse_src_event_masks;
1039 }   
1040
1041 static gboolean
1042 gst_wavparse_srcpad_event (GstPad *pad, GstEvent *event)
1043 {
1044 #if 0
1045   GstWavParse *wavparse = GST_WAVPARSE (GST_PAD_PARENT (pad));
1046   gboolean res = FALSE;
1047
1048   GST_DEBUG ("event %d", GST_EVENT_TYPE (event));
1049
1050   switch (GST_EVENT_TYPE (event)) {
1051     case GST_EVENT_SEEK:
1052     {
1053       gint64 byteoffset;
1054       GstFormat format;
1055
1056       /* we can only seek when in the DATA state */
1057       if (wavparse->state != GST_WAVPARSE_DATA) {
1058         return FALSE;
1059       }
1060
1061       format = GST_FORMAT_BYTES;
1062       
1063       /* bring format to bytes for the peer element, 
1064        * FIXME be smarter here */
1065       res = gst_pad_convert (pad, 
1066                              GST_EVENT_SEEK_FORMAT (event),
1067                              GST_EVENT_SEEK_OFFSET (event),
1068                              &format,
1069                              &byteoffset);
1070       
1071       if (res) {
1072         /* ok, seek worked, update our state */
1073         wavparse->seek_offset = byteoffset;
1074         wavparse->seek_pending = TRUE;
1075         wavparse->need_discont = TRUE;
1076       }
1077       break;
1078     }
1079     default:
1080       break;
1081   }
1082
1083   gst_event_unref (event);
1084   return res;
1085 #else
1086   return FALSE;
1087 #endif
1088 }
1089
1090 static GstElementStateReturn
1091 gst_wavparse_change_state (GstElement *element)
1092 {
1093   GstWavParse *wavparse = GST_WAVPARSE (element);
1094
1095   switch (GST_STATE_TRANSITION (element)) {
1096     case GST_STATE_NULL_TO_READY:
1097       break;
1098     case GST_STATE_READY_TO_PAUSED:
1099       wavparse->bs = gst_bytestream_new (wavparse->sinkpad);
1100       wavparse->state = GST_WAVPARSE_START;
1101       break;
1102     case GST_STATE_PAUSED_TO_PLAYING:
1103       break;
1104     case GST_STATE_PLAYING_TO_PAUSED:
1105       break;
1106     case GST_STATE_PAUSED_TO_READY:
1107       gst_bytestream_destroy (wavparse->bs);
1108       wavparse->state = GST_WAVPARSE_UNKNOWN;
1109       wavparse->bps = 0;
1110       wavparse->seek_pending = FALSE;
1111       wavparse->seek_offset = 0;
1112       break;
1113     case GST_STATE_READY_TO_NULL:
1114       break;
1115   }
1116
1117   if (GST_ELEMENT_CLASS (parent_class)->change_state)
1118     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1119
1120   return GST_STATE_SUCCESS;
1121 }
1122
1123 static gboolean
1124 plugin_init (GstPlugin *plugin)
1125 {
1126   if (!gst_library_load ("gstbytestream")) {
1127     return FALSE;
1128   }
1129
1130   return gst_element_register (plugin, "wavparse", GST_RANK_SECONDARY, GST_TYPE_WAVPARSE);
1131 }
1132
1133 GST_PLUGIN_DEFINE (
1134   GST_VERSION_MAJOR,
1135   GST_VERSION_MINOR,
1136   "wavparse",
1137   "Parse a .wav file into raw audio",
1138   plugin_init,
1139   VERSION,
1140   GST_LICENSE,
1141   GST_PACKAGE,
1142   GST_ORIGIN
1143 )