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