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