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