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