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