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