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