wavenc: Raise rank of muxer to PRIMARY
[platform/upstream/gst-plugins-good.git] / gst / wavenc / gstwavenc.c
1 /* -*- mOde: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* GStreamer .wav encoder
3  * Copyright (C) <2002> Iain Holmes <iain@prettypeople.org>
4  * Copyright (C) <2006> Tim-Philipp Müller <tim centricular net>
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  * 
21  */
22 /**
23  * SECTION:element-wavenc
24  *
25  * Format a audio stream into the wav format.
26  *
27  */
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33 #include "gstwavenc.h"
34 #include "riff.h"
35
36 GST_DEBUG_CATEGORY_STATIC (wavenc_debug);
37 #define GST_CAT_DEFAULT wavenc_debug
38
39 struct riff_struct
40 {
41   guint8 id[4];                 /* RIFF */
42   guint32 len;
43   guint8 wav_id[4];             /* WAVE */
44 };
45
46 struct chunk_struct
47 {
48   guint8 id[4];
49   guint32 len;
50 };
51
52 struct common_struct
53 {
54   guint16 wFormatTag;
55   guint16 wChannels;
56   guint32 dwSamplesPerSec;
57   guint32 dwAvgBytesPerSec;
58   guint16 wBlockAlign;
59   guint16 wBitsPerSample;       /* Only for PCM */
60 };
61
62 struct wave_header
63 {
64   struct riff_struct riff;
65   struct chunk_struct format;
66   struct common_struct common;
67   struct chunk_struct data;
68 };
69
70 static const GstElementDetails gst_wavenc_details =
71 GST_ELEMENT_DETAILS ("WAV audio muxer",
72     "Codec/Muxer/Audio",
73     "Encode raw audio into WAV",
74     "Iain Holmes <iain@prettypeople.org>");
75
76 /* FIXME: mono doesn't produce correct files it seems, at least mplayer xruns */
77 /* Max. of two channels, more channels need WAVFORMATEX with
78  * channel layout, which we do not support yet */
79 #define SINK_CAPS \
80     "audio/x-raw-int, "                  \
81     "rate = (int) [ 1, MAX ], "          \
82     "channels = (int) [ 1, 2 ], "        \
83     "endianness = (int) LITTLE_ENDIAN, " \
84     "width = (int) 32, "                 \
85     "depth = (int) 32, "                 \
86     "signed = (boolean) true"            \
87     "; "                                 \
88     "audio/x-raw-int, "                  \
89     "rate = (int) [ 1, MAX ], "          \
90     "channels = (int) [ 1, 2 ], "        \
91     "endianness = (int) LITTLE_ENDIAN, " \
92     "width = (int) 24, "                 \
93     "depth = (int) 24, "                 \
94     "signed = (boolean) true"            \
95     "; "                                 \
96     "audio/x-raw-int, "                  \
97     "rate = (int) [ 1, MAX ], "          \
98     "channels = (int) [ 1, 2 ], "        \
99     "endianness = (int) LITTLE_ENDIAN, " \
100     "width = (int) 16, "                 \
101     "depth = (int) 16, "                 \
102     "signed = (boolean) true"            \
103     "; "                                 \
104     "audio/x-raw-int, "                  \
105     "rate = (int) [ 1, MAX ], "          \
106     "channels = (int) [ 1, 2 ], "        \
107     "width = (int) 8, "                  \
108     "depth = (int) 8, "                  \
109     "signed = (boolean) false"           \
110     "; "                                 \
111     "audio/x-raw-float, "                \
112     "rate = (int) [ 1, MAX ], "          \
113     "channels = (int) [ 1, 2 ], "        \
114     "endianness = (int) LITTLE_ENDIAN, " \
115     "width = (int) { 32, 64 }; "         \
116     "audio/x-alaw, "                     \
117     "rate = (int) [ 8000, 192000 ], "    \
118     "channels = (int) [ 1, 2 ], "        \
119     "width = (int) 8, "                  \
120     "depth = (int) 8, "                  \
121     "signed = (boolean) false; "         \
122     "audio/x-mulaw, "                    \
123     "rate = (int) [ 8000, 192000 ], "    \
124     "channels = (int) [ 1, 2 ], "        \
125     "width = (int) 8, "                  \
126     "depth = (int) 8, "                  \
127     "signed = (boolean) false"
128
129
130 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
131     GST_PAD_SINK,
132     GST_PAD_ALWAYS,
133     GST_STATIC_CAPS (SINK_CAPS)
134     );
135
136 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
137     GST_PAD_SRC,
138     GST_PAD_ALWAYS,
139     GST_STATIC_CAPS ("audio/x-wav")
140     );
141
142 GST_BOILERPLATE (GstWavEnc, gst_wavenc, GstElement, GST_TYPE_ELEMENT);
143
144 static GstFlowReturn gst_wavenc_chain (GstPad * pad, GstBuffer * buf);
145 static gboolean gst_wavenc_event (GstPad * pad, GstEvent * event);
146 static GstStateChangeReturn gst_wavenc_change_state (GstElement * element,
147     GstStateChange transition);
148 static gboolean gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps);
149
150 static void
151 gst_wavenc_base_init (gpointer g_class)
152 {
153   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
154
155   gst_element_class_set_details (element_class, &gst_wavenc_details);
156
157   gst_element_class_add_pad_template (element_class,
158       gst_static_pad_template_get (&src_factory));
159   gst_element_class_add_pad_template (element_class,
160       gst_static_pad_template_get (&sink_factory));
161
162   GST_DEBUG_CATEGORY_INIT (wavenc_debug, "wavenc", 0, "WAV encoder element");
163 }
164
165 static void
166 gst_wavenc_class_init (GstWavEncClass * klass)
167 {
168   GstElementClass *element_class;
169
170   element_class = (GstElementClass *) klass;
171
172   element_class->change_state = GST_DEBUG_FUNCPTR (gst_wavenc_change_state);
173 }
174
175 static void
176 gst_wavenc_init (GstWavEnc * wavenc, GstWavEncClass * klass)
177 {
178   wavenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
179   gst_pad_set_chain_function (wavenc->sinkpad,
180       GST_DEBUG_FUNCPTR (gst_wavenc_chain));
181   gst_pad_set_event_function (wavenc->sinkpad,
182       GST_DEBUG_FUNCPTR (gst_wavenc_event));
183   gst_pad_set_setcaps_function (wavenc->sinkpad,
184       GST_DEBUG_FUNCPTR (gst_wavenc_sink_setcaps));
185   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);
186
187   wavenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
188   gst_pad_use_fixed_caps (wavenc->srcpad);
189   gst_pad_set_caps (wavenc->srcpad,
190       gst_static_pad_template_get_caps (&src_factory));
191   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
192 }
193
194 #define WAV_HEADER_LEN 44
195
196 static GstBuffer *
197 gst_wavenc_create_header_buf (GstWavEnc * wavenc, guint audio_data_size)
198 {
199   struct wave_header wave;
200   GstBuffer *buf;
201   guint8 *header;
202
203   buf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
204   header = GST_BUFFER_DATA (buf);
205   memset (header, 0, WAV_HEADER_LEN);
206
207   wave.common.wChannels = wavenc->channels;
208   wave.common.wBitsPerSample = wavenc->width;
209   wave.common.dwSamplesPerSec = wavenc->rate;
210
211   /* Fill out our wav-header with some information */
212   memcpy (wave.riff.id, "RIFF", 4);
213   wave.riff.len = audio_data_size + WAV_HEADER_LEN - 8;
214   memcpy (wave.riff.wav_id, "WAVE", 4);
215
216   memcpy (wave.format.id, "fmt ", 4);
217   wave.format.len = 16;
218
219   wave.common.wFormatTag = wavenc->format;
220   wave.common.wBlockAlign = (wavenc->width / 8) * wave.common.wChannels;
221   wave.common.dwAvgBytesPerSec =
222       wave.common.wBlockAlign * wave.common.dwSamplesPerSec;
223
224   memcpy (wave.data.id, "data", 4);
225   wave.data.len = audio_data_size;
226
227   memcpy (header, (char *) wave.riff.id, 4);
228   GST_WRITE_UINT32_LE (header + 4, wave.riff.len);
229   memcpy (header + 8, (char *) wave.riff.wav_id, 4);
230   memcpy (header + 12, (char *) wave.format.id, 4);
231   GST_WRITE_UINT32_LE (header + 16, wave.format.len);
232   GST_WRITE_UINT16_LE (header + 20, wave.common.wFormatTag);
233   GST_WRITE_UINT16_LE (header + 22, wave.common.wChannels);
234   GST_WRITE_UINT32_LE (header + 24, wave.common.dwSamplesPerSec);
235   GST_WRITE_UINT32_LE (header + 28, wave.common.dwAvgBytesPerSec);
236   GST_WRITE_UINT16_LE (header + 32, wave.common.wBlockAlign);
237   GST_WRITE_UINT16_LE (header + 34, wave.common.wBitsPerSample);
238   memcpy (header + 36, (char *) wave.data.id, 4);
239   GST_WRITE_UINT32_LE (header + 40, wave.data.len);
240
241   gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad));
242
243   return buf;
244 }
245
246 static GstFlowReturn
247 gst_wavenc_push_header (GstWavEnc * wavenc, guint audio_data_size)
248 {
249   GstFlowReturn ret;
250   GstBuffer *outbuf;
251
252   /* seek to beginning of file */
253   gst_pad_push_event (wavenc->srcpad,
254       gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
255           0, GST_CLOCK_TIME_NONE, 0));
256
257   GST_DEBUG_OBJECT (wavenc, "writing header with datasize=%u", audio_data_size);
258
259   outbuf = gst_wavenc_create_header_buf (wavenc, audio_data_size);
260   GST_BUFFER_OFFSET (outbuf) = 0;
261
262   ret = gst_pad_push (wavenc->srcpad, outbuf);
263
264   if (ret != GST_FLOW_OK) {
265     GST_WARNING_OBJECT (wavenc, "push header failed: flow = %s",
266         gst_flow_get_name (ret));
267   }
268
269   return ret;
270 }
271
272 static gboolean
273 gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps)
274 {
275   GstWavEnc *wavenc;
276   GstStructure *structure;
277   const gchar *name;
278   gint chans, rate, width;
279
280   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
281
282   if (wavenc->sent_header) {
283     GST_WARNING_OBJECT (wavenc, "cannot change format in middle of stream");
284     goto fail;
285   }
286
287   GST_DEBUG_OBJECT (wavenc, "got caps: %" GST_PTR_FORMAT, caps);
288
289   structure = gst_caps_get_structure (caps, 0);
290   name = gst_structure_get_name (structure);
291
292   if (!gst_structure_get_int (structure, "channels", &chans) ||
293       !gst_structure_get_int (structure, "rate", &rate)) {
294     GST_WARNING_OBJECT (wavenc, "caps incomplete");
295     goto fail;
296   }
297
298   if (strcmp (name, "audio/x-raw-int") == 0) {
299     if (!gst_structure_get_int (structure, "width", &width)) {
300       GST_WARNING_OBJECT (wavenc, "caps incomplete");
301       goto fail;
302     }
303     wavenc->format = GST_RIFF_WAVE_FORMAT_PCM;
304     wavenc->width = width;
305   } else if (strcmp (name, "audio/x-raw-float") == 0) {
306     if (!gst_structure_get_int (structure, "width", &width)) {
307       GST_WARNING_OBJECT (wavenc, "caps incomplete");
308       goto fail;
309     }
310     wavenc->format = GST_RIFF_WAVE_FORMAT_FLOAT;
311     wavenc->width = width;
312   } else if (strcmp (name, "audio/x-alaw") == 0) {
313     wavenc->format = GST_RIFF_WAVE_FORMAT_ALAW;
314     wavenc->width = 8;
315   } else if (strcmp (name, "audio/x-mulaw") == 0) {
316     wavenc->format = GST_RIFF_WAVE_FORMAT_MULAW;
317     wavenc->width = 8;
318   } else {
319     GST_WARNING_OBJECT (wavenc, "Unsupported format %s", name);
320     goto fail;
321   }
322
323   wavenc->channels = chans;
324   wavenc->rate = rate;
325
326   GST_LOG_OBJECT (wavenc,
327       "accepted caps: format=0x%04x chans=%u width=%u rate=%u",
328       wavenc->format, wavenc->channels, wavenc->width, wavenc->rate);
329
330   gst_object_unref (wavenc);
331   return TRUE;
332
333 fail:
334   gst_object_unref (wavenc);
335   return FALSE;
336 }
337
338 #if 0
339 static struct _maps
340 {
341   const guint32 id;
342   const gchar *name;
343 } maps[] = {
344   {
345   GST_RIFF_INFO_IARL, "Location"}, {
346   GST_RIFF_INFO_IART, "Artist"}, {
347   GST_RIFF_INFO_ICMS, "Commissioner"}, {
348   GST_RIFF_INFO_ICMT, "Comment"}, {
349   GST_RIFF_INFO_ICOP, "Copyright"}, {
350   GST_RIFF_INFO_ICRD, "Creation Date"}, {
351   GST_RIFF_INFO_IENG, "Engineer"}, {
352   GST_RIFF_INFO_IGNR, "Genre"}, {
353   GST_RIFF_INFO_IKEY, "Keywords"}, {
354   GST_RIFF_INFO_INAM, "Title"}, {
355   GST_RIFF_INFO_IPRD, "Product"}, {
356   GST_RIFF_INFO_ISBJ, "Subject"}, {
357   GST_RIFF_INFO_ISFT, "Software"}, {
358   GST_RIFF_INFO_ITCH, "Technician"}
359 };
360
361 static guint32
362 get_id_from_name (const char *name)
363 {
364   int i;
365
366   for (i = 0; i < G_N_ELEMENTS (maps); i++) {
367     if (strcasecmp (maps[i].name, name) == 0) {
368       return maps[i].id;
369     }
370   }
371
372   return 0;
373 }
374
375 static void
376 write_metadata (GstWavEnc * wavenc)
377 {
378   GString *info_str;
379   GList *props;
380   int total = 4;
381   gboolean need_to_write = FALSE;
382
383   info_str = g_string_new ("LIST    INFO");
384
385   for (props = wavenc->metadata->properties->properties; props;
386       props = props->next) {
387     GstPropsEntry *entry = props->data;
388     const char *name;
389     guint32 id;
390
391     name = gst_props_entry_get_name (entry);
392     id = get_id_from_name (name);
393     if (id != 0) {
394       const char *text;
395       char *tmp;
396       int len, req, i;
397
398       need_to_write = TRUE;     /* We've got at least one entry */
399
400       gst_props_entry_get_string (entry, &text);
401       len = strlen (text) + 1;  /* The length in the file includes the \0 */
402
403       tmp = g_strdup_printf ("%" GST_FOURCC_FORMAT "%d%s", GST_FOURCC_ARGS (id),
404           GUINT32_TO_LE (len), text);
405       g_string_append (info_str, tmp);
406       g_free (tmp);
407
408       /* Check that we end on an even boundary */
409       req = ((len + 8) + 1) & ~1;
410       for (i = 0; i < req - len; i++) {
411         g_string_append_printf (info_str, "%c", 0);
412       }
413
414       total += req;
415     }
416   }
417
418   if (need_to_write) {
419     GstBuffer *buf;
420
421     /* Now we've got all the strings together, we can write our length in */
422     info_str->str[4] = GUINT32_TO_LE (total);
423
424     buf = gst_buffer_new ();
425     gst_buffer_set_data (buf, info_str->str, info_str->len);
426
427     gst_pad_push (wavenc->srcpad, GST_DATA (buf));
428     g_string_free (info_str, FALSE);
429   }
430 }
431
432 static void
433 write_cues (GstWavEnc * wavenc)
434 {
435   GString *cue_string, *point_string;
436   GstBuffer *buf;
437   GList *cue_list, *c;
438   int num_cues, total = 4;
439
440   if (gst_props_get (wavenc->metadata->properties,
441           "cues", &cue_list, NULL) == FALSE) {
442     /* No cues, move along please, nothing to see here */
443     return;
444   }
445
446   /* Space for 'cue ', chunk size and number of cuepoints */
447   cue_string = g_string_new ("cue         ");
448 #define CUEPOINT_SIZE 24
449   point_string = g_string_sized_new (CUEPOINT_SIZE);
450
451   for (c = cue_list, num_cues = 0; c; c = c->next, num_cues++) {
452     GstCaps *cue_caps = c->data;
453     guint32 pos;
454
455     gst_props_get (cue_caps->properties, "position", &pos, NULL);
456
457     point_string->str[0] = GUINT32_TO_LE (num_cues + 1);
458     point_string->str[4] = GUINT32_TO_LE (0);
459     /* Fixme: There is probably a macro for this */
460     point_string->str[8] = 'd';
461     point_string->str[9] = 'a';
462     point_string->str[10] = 't';
463     point_string->str[11] = 'a';
464     point_string->str[12] = GUINT32_TO_LE (0);
465     point_string->str[16] = GUINT32_TO_LE (0);
466     point_string->str[20] = GUINT32_TO_LE (pos);
467
468     total += CUEPOINT_SIZE;
469   }
470
471   /* Set the length and chunk size */
472   cue_string->str[4] = GUINT32_TO_LE (total);
473   cue_string->str[8] = GUINT32_TO_LE (num_cues);
474   /* Stick the cue points on the end */
475   g_string_append (cue_string, point_string->str);
476   g_string_free (point_string, TRUE);
477
478   buf = gst_buffer_new ();
479   gst_buffer_set_data (buf, cue_string->str, cue_string->len);
480
481   gst_pad_push (wavenc->srcpad, GST_DATA (buf));
482   g_string_free (cue_string, FALSE);
483 }
484
485 static void
486 write_labels (GstWavEnc * wavenc)
487 {
488   GstBuffer *buf;
489   GString *info_str;
490   int total = 4;
491   GList *caps;
492
493   info_str = g_string_new ("LIST    adtl");
494   if (gst_props_get (wavenc->metadata->properties, "ltxts", &caps, NULL)) {
495     GList *p;
496     int i;
497
498     for (p = caps, i = 1; p; p = p->next, i++) {
499       GstCaps *ltxt_caps = p->data;
500       GString *ltxt;
501       char *label = NULL;
502       int len, req, j;
503
504       gst_props_get (ltxt_caps->properties, "name", &label, NULL);
505       len = strlen (label);
506
507 #define LTXT_SIZE 28
508       ltxt = g_string_new ("ltxt                        ");
509       ltxt->str[8] = GUINT32_TO_LE (i); /* Identifier */
510       ltxt->str[12] = GUINT32_TO_LE (0);        /* Sample Length */
511       ltxt->str[16] = GUINT32_TO_LE (0);        /* FIXME: Don't save the purpose yet */
512       ltxt->str[20] = GUINT16_TO_LE (0);        /* Country */
513       ltxt->str[22] = GUINT16_TO_LE (0);        /* Language */
514       ltxt->str[24] = GUINT16_TO_LE (0);        /* Dialect */
515       ltxt->str[26] = GUINT16_TO_LE (0);        /* Code Page */
516       g_string_append (ltxt, label);
517       g_free (label);
518
519       len += LTXT_SIZE;
520
521       ltxt->str[4] = GUINT32_TO_LE (len);
522
523       /* Check that we end on an even boundary */
524       req = ((len + 8) + 1) & ~1;
525       for (j = 0; j < req - len; j++) {
526         g_string_append_printf (ltxt, "%c", 0);
527       }
528
529       total += req;
530
531       g_string_append (info_str, ltxt->str);
532       g_string_free (ltxt, TRUE);
533     }
534   }
535
536   if (gst_props_get (wavenc->metadata->properties, "labels", &caps, NULL)) {
537     GList *p;
538     int i;
539
540     for (p = caps, i = 1; p; p = p->next, i++) {
541       GstCaps *labl_caps = p->data;
542       GString *labl;
543       char *label = NULL;
544       int len, req, j;
545
546       gst_props_get (labl_caps->properties, "name", &label, NULL);
547       len = strlen (label);
548
549 #define LABL_SIZE 4
550       labl = g_string_new ("labl        ");
551       labl->str[8] = GUINT32_TO_LE (i);
552       g_string_append (labl, label);
553       g_free (label);
554
555       len += LABL_SIZE;
556
557       labl->str[4] = GUINT32_TO_LE (len);
558
559       /* Check our size */
560       req = ((len + 8) + 1) & ~1;
561       for (j = 0; j < req - len; j++) {
562         g_string_append_printf (labl, "%c", 0);
563       }
564
565       total += req;
566
567       g_string_append (info_str, labl->str);
568       g_string_free (labl, TRUE);
569     }
570   }
571
572   if (gst_props_get (wavenc->metadata->properties, "notes", &caps, NULL)) {
573     GList *p;
574     int i;
575
576     for (p = caps, i = 1; p; p = p->next, i++) {
577       GstCaps *note_caps = p->data;
578       GString *note;
579       char *label = NULL;
580       int len, req, j;
581
582       gst_props_get (note_caps->properties, "name", &label, NULL);
583       len = strlen (label);
584
585 #define NOTE_SIZE 4
586       note = g_string_new ("note        ");
587       note->str[8] = GUINT32_TO_LE (i);
588       g_string_append (note, label);
589       g_free (label);
590
591       len += NOTE_SIZE;
592
593       note->str[4] = GUINT32_TO_LE (len);
594
595       /* Size check */
596       req = ((len + 8) + 1) & ~1;
597       for (j = 0; j < req - len; j++) {
598         g_string_append_printf (note, "%c", 0);
599       }
600
601       total += req;
602
603       g_string_append (info_str, note->str);
604       g_string_free (note, TRUE);
605     }
606   }
607
608   info_str->str[4] = GUINT32_TO_LE (total);
609
610   buf = gst_buffer_new ();
611   gst_buffer_set_data (buf, info_str->str, info_str->len);
612
613   gst_pad_push (wavenc->srcpad, GST_DATA (buf));
614   g_string_free (info_str, FALSE);
615 }
616 #endif
617
618 static gboolean
619 gst_wavenc_event (GstPad * pad, GstEvent * event)
620 {
621   gboolean res = TRUE;
622   GstWavEnc *wavenc;
623
624   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
625
626   switch (GST_EVENT_TYPE (event)) {
627     case GST_EVENT_EOS:{
628       GST_DEBUG_OBJECT (wavenc, "got EOS");
629 #if 0
630       /* Write our metadata if we have any */
631       if (wavenc->metadata) {
632         write_metadata (wavenc);
633         write_cues (wavenc);
634         write_labels (wavenc);
635       }
636 #endif
637       /* write header with correct length values */
638       gst_wavenc_push_header (wavenc, wavenc->length);
639
640       /* and forward the EOS event */
641       res = gst_pad_event_default (pad, event);
642       break;
643     }
644     case GST_EVENT_NEWSEGMENT:
645       /* Just drop it, it's probably in TIME format
646        * anyway. We'll send our own newsegment event */
647       gst_event_unref (event);
648       break;
649     default:
650       res = gst_pad_event_default (pad, event);
651       break;
652   }
653
654   gst_object_unref (wavenc);
655   return res;
656 }
657
658 static GstFlowReturn
659 gst_wavenc_chain (GstPad * pad, GstBuffer * buf)
660 {
661   GstWavEnc *wavenc = GST_WAVENC (GST_PAD_PARENT (pad));
662   GstFlowReturn flow = GST_FLOW_OK;
663
664   g_return_val_if_fail (wavenc->channels > 0, GST_FLOW_WRONG_STATE);
665
666   if (!wavenc->sent_header) {
667     /* use bogus size initially, we'll write the real
668      * header when we get EOS and know the exact length */
669     flow = gst_wavenc_push_header (wavenc, 0x7FFF0000);
670
671     if (flow != GST_FLOW_OK)
672       return flow;
673
674     GST_DEBUG_OBJECT (wavenc, "wrote dummy header");
675     wavenc->sent_header = TRUE;
676   }
677
678   GST_LOG_OBJECT (wavenc, "pushing %u bytes raw audio, ts=%" GST_TIME_FORMAT,
679       GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
680
681   buf = gst_buffer_make_metadata_writable (buf);
682
683   gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad));
684   GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length;
685   GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
686
687   wavenc->length += GST_BUFFER_SIZE (buf);
688
689   flow = gst_pad_push (wavenc->srcpad, buf);
690
691   return flow;
692 }
693
694 static GstStateChangeReturn
695 gst_wavenc_change_state (GstElement * element, GstStateChange transition)
696 {
697   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
698   GstWavEnc *wavenc = GST_WAVENC (element);
699
700   switch (transition) {
701     case GST_STATE_CHANGE_NULL_TO_READY:
702       wavenc->format = 0;
703       wavenc->channels = 0;
704       wavenc->width = 0;
705       wavenc->rate = 0;
706       wavenc->length = 0;
707       wavenc->sent_header = FALSE;
708       break;
709     default:
710       break;
711   }
712
713   ret = parent_class->change_state (element, transition);
714   if (ret != GST_STATE_CHANGE_SUCCESS)
715     return ret;
716
717   return ret;
718 }
719
720 static gboolean
721 plugin_init (GstPlugin * plugin)
722 {
723   return gst_element_register (plugin, "wavenc", GST_RANK_PRIMARY,
724       GST_TYPE_WAVENC);
725 }
726
727 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
728     GST_VERSION_MINOR,
729     "wavenc",
730     "Encode raw audio into WAV",
731     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)