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