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