1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 * Copyright (C) <2002> Iain Holmes <iain@prettypeople.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
27 #include <gstwavenc.h>
30 static void gst_wavenc_base_init (gpointer g_class);
31 static void gst_wavenc_class_init (GstWavEncClass *klass);
32 static void gst_wavenc_init (GstWavEnc *wavenc);
33 static void gst_wavenc_chain (GstPad *pad, GstData *_data);
35 #define WAVE_FORMAT_PCM 0x0001
37 #define WRITE_U32(buf, x) *(buf) = (unsigned char) (x&0xff);\
38 *((buf)+1) = (unsigned char)((x>>8)&0xff);\
39 *((buf)+2) = (unsigned char)((x>>16)&0xff);\
40 *((buf)+3) = (unsigned char)((x>>24)&0xff);
42 #define WRITE_U16(buf, x) *(buf) = (unsigned char) (x&0xff);\
43 *((buf)+1) = (unsigned char)((x>>8)&0xff);
46 guint8 id[4]; /* RIFF */
48 guint8 wav_id[4]; /* WAVE */
56 struct common_struct {
59 guint32 dwSamplesPerSec;
60 guint32 dwAvgBytesPerSec;
62 guint16 wBitsPerSample; /* Only for PCM */
66 struct riff_struct riff;
67 struct chunk_struct format;
68 struct common_struct common;
69 struct chunk_struct data;
72 static GstElementDetails gst_wavenc_details = GST_ELEMENT_DETAILS (
74 "Codec/Encoder/Audio",
75 "Encode raw audio into WAV",
76 "Iain Holmes <iain@prettypeople.org>"
79 static GstPadTemplate *srctemplate, *sinktemplate;
81 GST_PAD_TEMPLATE_FACTORY (sink_factory,
88 "endianness", GST_PROPS_INT (G_LITTLE_ENDIAN),
89 "signed", GST_PROPS_BOOLEAN (TRUE),
90 "width", GST_PROPS_LIST (
94 "depth", GST_PROPS_LIST (
98 "rate", GST_PROPS_INT_RANGE (8000, 48000),
99 "channels", GST_PROPS_INT_RANGE (1, 2)
103 GST_PAD_TEMPLATE_FACTORY (src_factory,
119 static GstElementClass *parent_class = NULL;
122 gst_wavenc_get_type (void)
124 static GType type = 0;
127 static const GTypeInfo info = {
128 sizeof (GstWavEncClass),
129 gst_wavenc_base_init,
131 (GClassInitFunc) gst_wavenc_class_init,
136 (GInstanceInitFunc) gst_wavenc_init
139 type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavEnc", &info, 0);
145 static GstElementStateReturn
146 gst_wavenc_change_state (GstElement *element)
148 GstWavEnc *wavenc = GST_WAVENC (element);
150 switch (GST_STATE_TRANSITION (element)) {
151 case GST_STATE_PAUSED_TO_READY:
152 wavenc->setup = FALSE;
153 wavenc->flush_header = TRUE;
159 if (parent_class->change_state) {
160 return parent_class->change_state (element);
163 return GST_STATE_SUCCESS;
167 set_property (GObject *object,
174 enc = GST_WAVENC (object);
178 enc->metadata = g_value_get_boxed (value);
187 gst_wavenc_base_init (gpointer g_class)
189 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
191 gst_element_class_set_details (element_class, &gst_wavenc_details);
193 srctemplate = src_factory ();
194 gst_element_class_add_pad_template (element_class, srctemplate);
196 sinktemplate = sink_factory ();
197 gst_element_class_add_pad_template (element_class, sinktemplate);
200 gst_wavenc_class_init (GstWavEncClass *klass)
202 GstElementClass *element_class;
203 GObjectClass *object_class;
205 element_class = (GstElementClass *) klass;
206 object_class = (GObjectClass *) klass;
208 object_class->set_property = set_property;
210 element_class->change_state = gst_wavenc_change_state;
212 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
214 g_object_class_install_property (object_class,
216 g_param_spec_boxed ("metadata", "", "",
222 gst_wavenc_setup (GstWavEnc *wavenc)
224 struct wave_header wave;
225 gint size = 0x7fffffff; /* Use a bogus size initially */
227 wave.common.wChannels = wavenc->channels;
228 wave.common.wBitsPerSample = wavenc->bits;
229 wave.common.dwSamplesPerSec = wavenc->rate;
231 memset (wavenc->header, 0, WAV_HEADER_LEN);
233 /* Fill out our wav-header with some information */
234 strncpy (wave.riff.id, "RIFF", 4);
235 wave.riff.len = size - 8;
236 strncpy (wave.riff.wav_id, "WAVE", 4);
238 strncpy (wave.format.id, "fmt ", 4);
239 wave.format.len = 16;
241 wave.common.wFormatTag = WAVE_FORMAT_PCM;
242 wave.common.dwAvgBytesPerSec = wave.common.wChannels * wave.common.dwSamplesPerSec * (wave.common.wBitsPerSample >> 3);
243 wave.common.wBlockAlign = wave.common.wChannels * (wave.common.wBitsPerSample >> 3);
245 strncpy (wave.data.id, "data", 4);
246 wave.data.len = size - 44;
248 strncpy (wavenc->header, wave.riff.id, 4);
249 WRITE_U32 (wavenc->header + 4, wave.riff.len);
250 strncpy (wavenc->header + 8, wave.riff.wav_id, 4);
251 strncpy (wavenc->header + 12, wave.format.id, 4);
252 WRITE_U32 (wavenc->header + 16, wave.format.len);
253 WRITE_U16 (wavenc->header + 20, wave.common.wFormatTag);
254 WRITE_U16 (wavenc->header + 22, wave.common.wChannels);
255 WRITE_U32 (wavenc->header + 24, wave.common.dwSamplesPerSec);
256 WRITE_U32 (wavenc->header + 28, wave.common.dwAvgBytesPerSec);
257 WRITE_U16 (wavenc->header + 32, wave.common.wBlockAlign);
258 WRITE_U16 (wavenc->header + 34, wave.common.wBitsPerSample);
259 strncpy (wavenc->header + 36, wave.data.id, 4);
260 WRITE_U32 (wavenc->header + 40, wave.data.len);
262 wavenc->setup = TRUE;
266 static GstPadLinkReturn
267 gst_wavenc_sinkconnect (GstPad *pad,
272 wavenc = GST_WAVENC (gst_pad_get_parent (pad));
274 if (!GST_CAPS_IS_FIXED (caps)) {
275 return GST_PAD_LINK_DELAYED;
278 gst_caps_get_int (caps, "channels", &wavenc->channels);
279 gst_caps_get_int (caps, "rate", &wavenc->rate);
280 gst_caps_get_int (caps, "depth", &wavenc->bits);
282 gst_wavenc_setup (wavenc);
285 return GST_PAD_LINK_OK;
288 return GST_PAD_LINK_REFUSED;
292 gst_wavenc_stop_file (GstWavEnc *wavenc)
297 event = gst_event_new_seek (GST_FORMAT_BYTES |
298 GST_SEEK_METHOD_SET, 0);
299 gst_pad_send_event (GST_PAD_PEER (wavenc->srcpad), event);
301 outbuf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
302 WRITE_U32 (wavenc->header + 4, wavenc->length);
303 memcpy (GST_BUFFER_DATA (outbuf), wavenc->header, WAV_HEADER_LEN);
305 gst_pad_push (wavenc->srcpad, GST_DATA (outbuf));
309 gst_wavenc_init (GstWavEnc *wavenc)
311 wavenc->sinkpad = gst_pad_new_from_template (sinktemplate, "sink");
312 gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);
313 gst_pad_set_chain_function (wavenc->sinkpad, gst_wavenc_chain);
314 gst_pad_set_link_function (wavenc->sinkpad, gst_wavenc_sinkconnect);
316 wavenc->srcpad = gst_pad_new_from_template (srctemplate, "src");
317 gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
319 wavenc->setup = FALSE;
320 wavenc->flush_header = TRUE;
321 wavenc->metadata = NULL;
323 GST_FLAG_SET (wavenc, GST_ELEMENT_EVENT_AWARE);
330 { GST_RIFF_INFO_IARL, "Location" },
331 { GST_RIFF_INFO_IART, "Artist" },
332 { GST_RIFF_INFO_ICMS, "Commissioner" },
333 { GST_RIFF_INFO_ICMT, "Comment" },
334 { GST_RIFF_INFO_ICOP, "Copyright" },
335 { GST_RIFF_INFO_ICRD, "Creation Date" },
336 { GST_RIFF_INFO_IENG, "Engineer" },
337 { GST_RIFF_INFO_IGNR, "Genre" },
338 { GST_RIFF_INFO_IKEY, "Keywords" },
339 { GST_RIFF_INFO_INAM, "Title" }, /* Name */
340 { GST_RIFF_INFO_IPRD, "Product" },
341 { GST_RIFF_INFO_ISBJ, "Subject" },
342 { GST_RIFF_INFO_ISFT, "Software" },
343 { GST_RIFF_INFO_ITCH, "Technician" },
348 get_id_from_name (const char *name)
352 for (i = 0; maps[i].name; i++) {
353 if (strcasecmp (maps[i].name, name) == 0) {
362 write_metadata (GstWavEnc *wavenc)
367 gboolean need_to_write = FALSE;
369 info_str = g_string_new ("LIST INFO");
371 for (props = wavenc->metadata->properties->properties; props; props = props->next) {
372 GstPropsEntry *entry = props->data;
376 name = gst_props_entry_get_name (entry);
377 id = get_id_from_name (name);
383 need_to_write = TRUE; /* We've got at least one entry */
385 gst_props_entry_get_string (entry, &text);
386 len = strlen (text) + 1; /* The length in the file includes the \0 */
388 tmp = g_strdup_printf (GST_FOURCC_FORMAT "%d%s", GST_FOURCC_ARGS (id),
389 GUINT32_TO_LE (len), text);
390 g_string_append (info_str, tmp);
393 /* Check that we end on an even boundary */
394 req = ((len + 8) + 1) & ~1;
395 for (i = 0; i < req - len; i++) {
396 g_string_append_printf (info_str, "%c", 0);
405 /* Now we've got all the strings together, we can write our length in */
406 info_str->str[4] = GUINT32_TO_LE (total);
408 buf = gst_buffer_new ();
409 gst_buffer_set_data (buf, info_str->str, info_str->len);
411 gst_pad_push (wavenc->srcpad, GST_DATA (buf));
412 g_string_free (info_str, FALSE);
417 write_cues (GstWavEnc *wavenc)
419 GString *cue_string, *point_string;
422 int num_cues, total = 4;
424 if (gst_props_get (wavenc->metadata->properties,
427 /* No cues, move along please, nothing to see here */
431 /* Space for 'cue ', chunk size and number of cuepoints */
432 cue_string = g_string_new ("cue ");
433 #define CUEPOINT_SIZE 24
434 point_string = g_string_sized_new (CUEPOINT_SIZE);
436 for (c = cue_list, num_cues = 0; c; c = c->next, num_cues++) {
437 GstCaps *cue_caps = c->data;
440 gst_props_get (cue_caps->properties, "position", &pos, NULL);
442 point_string->str[0] = GUINT32_TO_LE (num_cues + 1);
443 point_string->str[4] = GUINT32_TO_LE (0);
444 /* Fixme: There is probably a macro for this */
445 point_string->str[8] = 'd';
446 point_string->str[9] = 'a';
447 point_string->str[10] = 't';
448 point_string->str[11] = 'a';
449 point_string->str[12] = GUINT32_TO_LE (0);
450 point_string->str[16] = GUINT32_TO_LE (0);
451 point_string->str[20] = GUINT32_TO_LE (pos);
453 total += CUEPOINT_SIZE;
456 /* Set the length and chunk size */
457 cue_string->str[4] = GUINT32_TO_LE (total);
458 cue_string->str[8] = GUINT32_TO_LE (num_cues);
459 /* Stick the cue points on the end */
460 g_string_append (cue_string, point_string->str);
461 g_string_free (point_string, TRUE);
463 buf = gst_buffer_new ();
464 gst_buffer_set_data (buf, cue_string->str, cue_string->len);
466 gst_pad_push (wavenc->srcpad, GST_DATA (buf));
467 g_string_free (cue_string, FALSE);
471 write_labels (GstWavEnc *wavenc)
478 info_str = g_string_new ("LIST adtl");
479 if (gst_props_get (wavenc->metadata->properties, "ltxts", &caps, NULL)) {
483 for (p = caps, i = 1; p; p = p->next, i++) {
484 GstCaps *ltxt_caps = p->data;
489 gst_props_get (ltxt_caps->properties, "name", &label, NULL);
490 len = strlen (label);
493 ltxt = g_string_new ("ltxt ");
494 ltxt->str[8] = GUINT32_TO_LE (i); /* Identifier */
495 ltxt->str[12] = GUINT32_TO_LE (0); /* Sample Length */
496 ltxt->str[16] = GUINT32_TO_LE (0); /* FIXME: Don't save the purpose yet */
497 ltxt->str[20] = GUINT16_TO_LE (0); /* Country */
498 ltxt->str[22] = GUINT16_TO_LE (0); /* Language */
499 ltxt->str[24] = GUINT16_TO_LE (0); /* Dialect */
500 ltxt->str[26] = GUINT16_TO_LE (0); /* Code Page */
501 g_string_append (ltxt, label);
506 ltxt->str[4] = GUINT32_TO_LE (len);
508 /* Check that we end on an even boundary */
509 req = ((len + 8) + 1) & ~1;
510 for (j = 0; j < req - len; j++) {
511 g_string_append_printf (ltxt, "%c", 0);
516 g_string_append (info_str, ltxt->str);
517 g_string_free (ltxt, TRUE);
521 if (gst_props_get (wavenc->metadata->properties, "labels", &caps, NULL)) {
525 for (p = caps, i = 1; p; p = p->next, i++) {
526 GstCaps *labl_caps = p->data;
531 gst_props_get (labl_caps->properties, "name", &label, NULL);
532 len = strlen (label);
535 labl = g_string_new ("labl ");
536 labl->str[8] = GUINT32_TO_LE (i);
537 g_string_append (labl, label);
542 labl->str[4] = GUINT32_TO_LE (len);
545 req = ((len + 8) + 1) & ~1;
546 for (j = 0; j < req - len; j++) {
547 g_string_append_printf (labl, "%c", 0);
552 g_string_append (info_str, labl->str);
553 g_string_free (labl, TRUE);
557 if (gst_props_get (wavenc->metadata->properties, "notes", &caps, NULL)) {
561 for (p = caps, i = 1; p; p = p->next, i++) {
562 GstCaps *note_caps = p->data;
567 gst_props_get (note_caps->properties, "name", &label, NULL);
568 len = strlen (label);
571 note = g_string_new ("note ");
572 note->str[8] = GUINT32_TO_LE (i);
573 g_string_append (note, label);
578 note->str[4] = GUINT32_TO_LE (len);
581 req = ((len + 8) + 1) & ~1;
582 for (j = 0; j < req - len; j++) {
583 g_string_append_printf (note, "%c", 0);
588 g_string_append (info_str, note->str);
589 g_string_free (note, TRUE);
593 info_str->str[4] = GUINT32_TO_LE (total);
595 buf = gst_buffer_new ();
596 gst_buffer_set_data (buf, info_str->str, info_str->len);
598 gst_pad_push (wavenc->srcpad, GST_DATA (buf));
599 g_string_free (info_str, FALSE);
603 gst_wavenc_chain (GstPad *pad,
606 GstBuffer *buf = GST_BUFFER (_data);
609 wavenc = GST_WAVENC (gst_pad_get_parent (pad));
611 if (!wavenc->setup) {
612 gst_buffer_unref (buf);
613 gst_element_error (GST_ELEMENT (wavenc), "encoder not initialised (input is not audio?)");
617 if (GST_IS_EVENT (buf)) {
618 if (GST_EVENT_TYPE (buf) == GST_EVENT_EOS) {
619 wavenc->pad_eos = TRUE;
621 /* Write our metadata if we have any */
622 if (wavenc->metadata) {
623 write_metadata (wavenc);
625 write_labels (wavenc);
628 gst_wavenc_stop_file (wavenc);
629 gst_pad_push (wavenc->srcpad,
630 GST_DATA (gst_event_new (GST_EVENT_EOS)));
631 gst_element_set_eos (GST_ELEMENT (wavenc));
633 gst_pad_event_default (wavenc->srcpad, GST_EVENT (buf));
638 if (GST_PAD_IS_USABLE (wavenc->srcpad)) {
639 if (wavenc->flush_header) {
642 outbuf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
643 memcpy (GST_BUFFER_DATA (outbuf), wavenc->header, WAV_HEADER_LEN);
644 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
646 gst_pad_push (wavenc->srcpad, GST_DATA (outbuf));
647 wavenc->flush_header = FALSE;
650 wavenc->length += GST_BUFFER_SIZE (buf);
651 gst_pad_push (wavenc->srcpad, GST_DATA (buf));
656 plugin_init (GstPlugin *plugin)
658 return gst_element_register (plugin, "wavenc", GST_RANK_NONE, GST_TYPE_WAVENC);
665 "Encode raw audio into WAV",