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