remove copyright field from plugins
[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
3  * Copyright (C) <2002> Iain Holmes <iain@prettypeople.org>
4  *
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.
9  *
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.
14  *
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.
19  * 
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <gstwavenc.h>
28 #include <riff.h>
29
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);
34
35 #define WAVE_FORMAT_PCM 0x0001
36
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);
41
42 #define WRITE_U16(buf, x) *(buf) = (unsigned char) (x&0xff);\
43 *((buf)+1) = (unsigned char)((x>>8)&0xff);
44
45 struct riff_struct {
46   guint8        id[4];          /* RIFF */
47   guint32       len;
48   guint8        wav_id[4];      /* WAVE */
49 };
50
51 struct chunk_struct {
52   guint8        id[4];
53   guint32       len;
54 };
55
56 struct common_struct {
57   guint16       wFormatTag;
58   guint16       wChannels;
59   guint32       dwSamplesPerSec;
60   guint32       dwAvgBytesPerSec;
61   guint16       wBlockAlign;
62   guint16       wBitsPerSample;         /* Only for PCM */
63 };
64
65 struct wave_header {
66   struct riff_struct    riff;
67   struct chunk_struct   format;
68   struct common_struct  common;
69   struct chunk_struct   data;
70 };
71
72 static GstElementDetails gst_wavenc_details = GST_ELEMENT_DETAILS (
73   "WAV encoder",
74   "Codec/Encoder/Audio",
75   "Encode raw audio into WAV",
76   "Iain Holmes <iain@prettypeople.org>"
77 );
78
79 static GstPadTemplate *srctemplate, *sinktemplate;
80
81 GST_PAD_TEMPLATE_FACTORY (sink_factory,
82   "sink",
83   GST_PAD_SINK,
84   GST_PAD_ALWAYS,
85   GST_CAPS_NEW (
86     "wavenc_raw",
87     "audio/x-raw-int",
88       "endianness",  GST_PROPS_INT (G_LITTLE_ENDIAN),
89       "signed",      GST_PROPS_BOOLEAN (TRUE),
90       "width",       GST_PROPS_LIST (
91                        GST_PROPS_INT (8),
92                        GST_PROPS_INT (16)
93                      ),
94       "depth",       GST_PROPS_LIST (
95                        GST_PROPS_INT (8),
96                        GST_PROPS_INT (16)
97                      ),
98       "rate",        GST_PROPS_INT_RANGE (8000, 48000),
99       "channels",    GST_PROPS_INT_RANGE (1, 2)
100   )
101 )
102
103 GST_PAD_TEMPLATE_FACTORY (src_factory,
104   "src",
105   GST_PAD_SRC,
106   GST_PAD_ALWAYS,
107   GST_CAPS_NEW (
108     "wavenc_wav",
109     "audio/x-wav",
110     NULL
111   )
112 )
113
114 enum {
115         PROP_0,
116         PROP_METADATA
117 };
118
119 static GstElementClass *parent_class = NULL;
120
121 static GType
122 gst_wavenc_get_type (void)
123 {
124   static GType type = 0;
125
126   if (type == 0) {
127     static const GTypeInfo info = {
128       sizeof (GstWavEncClass), 
129       gst_wavenc_base_init, 
130       NULL,
131       (GClassInitFunc) gst_wavenc_class_init, 
132       NULL, 
133       NULL,
134       sizeof (GstWavEnc), 
135       0, 
136       (GInstanceInitFunc) gst_wavenc_init
137     };
138
139     type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavEnc", &info, 0);
140   }
141
142   return type;
143 }
144
145 static GstElementStateReturn
146 gst_wavenc_change_state (GstElement *element)
147 {
148   GstWavEnc *wavenc = GST_WAVENC (element);
149   
150   switch (GST_STATE_TRANSITION (element)) {
151     case GST_STATE_PAUSED_TO_READY:
152       wavenc->setup = FALSE;
153       wavenc->flush_header = TRUE;
154       break;
155     default:
156       break;
157   }
158
159   if (parent_class->change_state) {
160     return parent_class->change_state (element);
161   }
162
163   return GST_STATE_SUCCESS;
164 }
165
166 static void
167 set_property (GObject *object,
168                                                         guint prop_id,
169                                                         const GValue *value,
170                                                         GParamSpec *pspec)
171 {
172         GstWavEnc *enc;
173
174         enc = GST_WAVENC (object);
175         
176         switch (prop_id) {
177         case PROP_METADATA:
178                 enc->metadata = g_value_get_boxed (value);
179                 break;
180
181         default:
182                 break;
183         }
184 }
185
186 static void
187 gst_wavenc_base_init (gpointer g_class)
188 {
189   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
190
191   gst_element_class_set_details (element_class, &gst_wavenc_details);
192   
193   srctemplate = src_factory ();
194   gst_element_class_add_pad_template (element_class, srctemplate);
195
196   sinktemplate = sink_factory ();
197   gst_element_class_add_pad_template (element_class, sinktemplate);
198 }
199 static void
200 gst_wavenc_class_init (GstWavEncClass *klass)
201 {
202   GstElementClass *element_class;
203         GObjectClass *object_class;
204         
205   element_class = (GstElementClass *) klass;
206         object_class = (GObjectClass *) klass;
207
208         object_class->set_property = set_property;
209         
210   element_class->change_state = gst_wavenc_change_state;
211
212   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
213
214         g_object_class_install_property (object_class,
215                                                                                                                                          PROP_METADATA,
216                                                                                                                                          g_param_spec_boxed ("metadata", "", "",
217                                                                                                                                                                                                                          GST_TYPE_CAPS,
218                                                                                                                                                                                                                          G_PARAM_WRITABLE));
219 }
220
221 static gboolean
222 gst_wavenc_setup (GstWavEnc *wavenc)
223 {
224   struct wave_header wave;
225   gint size = 0x7fffffff; /* Use a bogus size initially */
226
227   wave.common.wChannels = wavenc->channels;
228   wave.common.wBitsPerSample = wavenc->bits;
229   wave.common.dwSamplesPerSec = wavenc->rate;
230
231   memset (wavenc->header, 0, WAV_HEADER_LEN);
232
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);
237
238   strncpy (wave.format.id, "fmt ", 4);
239   wave.format.len = 16;
240
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);
244
245   strncpy (wave.data.id, "data", 4);
246   wave.data.len = size - 44;
247
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);
261
262   wavenc->setup = TRUE;
263   return TRUE;
264 }
265
266 static GstPadLinkReturn
267 gst_wavenc_sinkconnect (GstPad *pad,
268                                                                                                 GstCaps *caps)
269 {
270   GstWavEnc *wavenc;
271
272   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
273
274   if (!GST_CAPS_IS_FIXED (caps)) {
275     return GST_PAD_LINK_DELAYED;
276   }
277
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);
281
282   gst_wavenc_setup (wavenc);
283
284   if (wavenc->setup) {
285     return GST_PAD_LINK_OK;
286   }
287
288   return GST_PAD_LINK_REFUSED;
289 }
290
291 static void
292 gst_wavenc_stop_file (GstWavEnc *wavenc)
293 {
294   GstEvent *event;
295   GstBuffer *outbuf;
296         
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);
300   
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);
304   
305   gst_pad_push (wavenc->srcpad, GST_DATA (outbuf));
306 }
307
308 static void
309 gst_wavenc_init (GstWavEnc *wavenc)
310 {
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);
315   
316   wavenc->srcpad = gst_pad_new_from_template (srctemplate, "src");
317   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
318
319   wavenc->setup = FALSE;
320   wavenc->flush_header = TRUE;
321         wavenc->metadata = NULL;
322         
323         GST_FLAG_SET (wavenc, GST_ELEMENT_EVENT_AWARE);
324 }
325
326 struct _maps {
327         guint32 id;
328         char *name;
329 } maps[] = {
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" },
344         { 0, NULL }
345 };
346
347 static guint32
348 get_id_from_name (const char *name)
349 {
350         int i;
351
352         for (i = 0; maps[i].name; i++) {
353                 if (strcasecmp (maps[i].name, name) == 0) {
354                         return maps[i].id;
355                 }
356         }
357
358         return 0;
359 }
360
361 static void
362 write_metadata (GstWavEnc *wavenc)
363 {
364         GString *info_str;
365         GList *props;
366         int total = 4;
367         gboolean need_to_write = FALSE;
368         
369         info_str = g_string_new ("LIST    INFO");
370
371         for (props = wavenc->metadata->properties->properties; props; props = props->next) {
372                 GstPropsEntry *entry = props->data;
373                 const char *name;
374                 guint32 id;
375
376                 name = gst_props_entry_get_name (entry);
377                 id = get_id_from_name (name);
378                 if (id != 0) {
379                         const char *text;
380                         char *tmp;
381                         int len, req, i;
382
383                         need_to_write = TRUE; /* We've got at least one entry */
384                         
385                         gst_props_entry_get_string (entry, &text);
386                         len = strlen (text) + 1; /* The length in the file includes the \0 */
387
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);
391                         g_free (tmp);
392
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);
397                         }
398
399                         total += req;
400                 }
401         }
402
403         if (need_to_write) {
404                 GstBuffer *buf;
405                 /* Now we've got all the strings together, we can write our length in */
406                 info_str->str[4] = GUINT32_TO_LE (total);
407
408                 buf = gst_buffer_new ();
409                 gst_buffer_set_data (buf, info_str->str, info_str->len);
410
411                 gst_pad_push (wavenc->srcpad, GST_DATA (buf));
412                 g_string_free (info_str, FALSE);
413         }
414 }
415
416 static void
417 write_cues (GstWavEnc *wavenc)
418 {
419         GString *cue_string, *point_string;
420         GstBuffer *buf;
421         GList *cue_list, *c;
422         int num_cues, total = 4;
423
424         if (gst_props_get (wavenc->metadata->properties,
425                                                                                  "cues", &cue_list,
426                                                                                  NULL) == FALSE) {
427                 /* No cues, move along please, nothing to see here */
428                 return;
429         }
430
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);
435         
436         for (c = cue_list, num_cues = 0; c; c = c->next, num_cues++) {
437                 GstCaps *cue_caps = c->data;
438                 guint32 pos;
439                 
440                 gst_props_get (cue_caps->properties, "position", &pos, NULL);
441                 
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);
452                 
453                 total += CUEPOINT_SIZE;
454         }
455
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);
462
463         buf = gst_buffer_new ();
464         gst_buffer_set_data (buf, cue_string->str, cue_string->len);
465
466         gst_pad_push (wavenc->srcpad, GST_DATA (buf));
467         g_string_free (cue_string, FALSE);
468 }
469
470 static void
471 write_labels (GstWavEnc *wavenc)
472 {
473         GstBuffer *buf;
474         GString *info_str;
475         int total = 4;
476         GList *caps;
477         
478         info_str = g_string_new ("LIST    adtl");
479         if (gst_props_get (wavenc->metadata->properties, "ltxts", &caps, NULL)) {
480                 GList *p;
481                 int i;
482
483                 for (p = caps, i = 1; p; p = p->next, i++) {
484                         GstCaps *ltxt_caps = p->data;
485                         GString *ltxt;
486                         char *label = NULL;
487                         int len, req, j;
488                         
489                         gst_props_get (ltxt_caps->properties, "name", &label, NULL);
490                         len = strlen (label);
491
492 #define LTXT_SIZE 28
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);
502                         g_free (label);
503
504                         len += LTXT_SIZE;
505
506                         ltxt->str[4] = GUINT32_TO_LE (len);
507                         
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);
512                         }
513
514                         total += req;
515
516                         g_string_append (info_str, ltxt->str);
517                         g_string_free (ltxt, TRUE);
518                 }
519         }
520
521         if (gst_props_get (wavenc->metadata->properties, "labels", &caps, NULL)) {
522                 GList *p;
523                 int i;
524
525                 for (p = caps, i = 1; p; p = p->next, i++) {
526                         GstCaps *labl_caps = p->data;
527                         GString *labl;
528                         char *label = NULL;
529                         int len, req, j;
530
531                         gst_props_get (labl_caps->properties, "name", &label, NULL);
532                         len = strlen (label);
533
534 #define LABL_SIZE 4
535                         labl = g_string_new ("labl        ");
536                         labl->str[8] = GUINT32_TO_LE (i);
537                         g_string_append (labl, label);
538                         g_free (label);
539                         
540                         len += LABL_SIZE;
541
542                         labl->str[4] = GUINT32_TO_LE (len);
543
544                         /* Check our size */
545                         req = ((len + 8) + 1) & ~1;
546                         for (j = 0; j < req - len; j++) {
547                                 g_string_append_printf (labl, "%c", 0);
548                         }
549
550                         total += req;
551
552                         g_string_append (info_str, labl->str);
553                         g_string_free (labl, TRUE);
554                 }
555         }
556
557         if (gst_props_get (wavenc->metadata->properties, "notes", &caps, NULL)) {
558                 GList *p;
559                 int i;
560
561                 for (p = caps, i = 1; p; p = p->next, i++) {
562                         GstCaps *note_caps = p->data;
563                         GString *note;
564                         char *label = NULL;
565                         int len, req, j;
566
567                         gst_props_get (note_caps->properties, "name", &label, NULL);
568                         len = strlen (label);
569
570 #define NOTE_SIZE 4
571                         note = g_string_new ("note        ");
572                         note->str[8] = GUINT32_TO_LE (i);
573                         g_string_append (note, label);
574                         g_free (label);
575
576                         len += NOTE_SIZE;
577
578                         note->str[4] = GUINT32_TO_LE (len);
579
580                         /* Size check */
581                         req = ((len + 8) + 1) & ~1;
582                         for (j = 0; j < req - len; j++) {
583                                 g_string_append_printf (note, "%c", 0);
584                         }
585
586                         total += req;
587
588                         g_string_append (info_str, note->str);
589                         g_string_free (note, TRUE);
590                 }
591         }
592
593         info_str->str[4] = GUINT32_TO_LE (total);
594
595         buf = gst_buffer_new ();
596         gst_buffer_set_data (buf, info_str->str, info_str->len);
597
598         gst_pad_push (wavenc->srcpad, GST_DATA (buf));
599         g_string_free (info_str, FALSE);
600 }
601
602 static void
603 gst_wavenc_chain (GstPad *pad,
604                                                                         GstData *_data)
605 {
606   GstBuffer *buf = GST_BUFFER (_data);
607   GstWavEnc *wavenc;
608
609   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
610
611   if (!wavenc->setup) {
612     gst_buffer_unref (buf);
613     gst_element_error (GST_ELEMENT (wavenc), "encoder not initialised (input is not audio?)");
614     return;
615   }
616
617         if (GST_IS_EVENT (buf)) {
618                 if (GST_EVENT_TYPE (buf) == GST_EVENT_EOS) {
619                         wavenc->pad_eos = TRUE;
620
621                         /* Write our metadata if we have any */
622                         if (wavenc->metadata) {
623                                 write_metadata (wavenc);
624                                 write_cues (wavenc);
625                                 write_labels (wavenc);
626                         }
627                         
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));
632                 } else {
633                         gst_pad_event_default (wavenc->srcpad, GST_EVENT (buf));
634                 }
635                 return;
636         }
637
638   if (GST_PAD_IS_USABLE (wavenc->srcpad)) {
639     if (wavenc->flush_header) {
640       GstBuffer *outbuf;
641       
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);
645       
646       gst_pad_push (wavenc->srcpad, GST_DATA (outbuf));
647       wavenc->flush_header = FALSE;
648     }
649
650     wavenc->length += GST_BUFFER_SIZE (buf);
651     gst_pad_push (wavenc->srcpad, GST_DATA (buf));
652   }
653 }
654
655 static gboolean
656 plugin_init (GstPlugin *plugin)
657 {
658   return gst_element_register (plugin, "wavenc", GST_RANK_NONE, GST_TYPE_WAVENC);
659 }
660
661 GST_PLUGIN_DEFINE (
662   GST_VERSION_MAJOR,
663   GST_VERSION_MINOR,
664   "wavenc",
665   "Encode raw audio into WAV",
666   plugin_init,
667   VERSION,
668   GST_LICENSE,
669   GST_PACKAGE,
670   GST_ORIGIN
671 )
672