Fix problem transitioning to PLAYING. (#122481)
[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 #include <string.h>
26 #include <gstwavenc.h>
27
28 static void     gst_wavenc_class_init   (GstWavEncClass *klass);
29 static void     gst_wavenc_init         (GstWavEnc *wavenc);
30 static void     gst_wavenc_chain        (GstPad *pad, GstBuffer *buf);
31
32 #define WAVE_FORMAT_PCM 0x0001
33
34 #define WRITE_U32(buf, x) *(buf) = (unsigned char) (x&0xff);\
35 *((buf)+1) = (unsigned char)((x>>8)&0xff);\
36 *((buf)+2) = (unsigned char)((x>>16)&0xff);\
37 *((buf)+3) = (unsigned char)((x>>24)&0xff);
38
39 #define WRITE_U16(buf, x) *(buf) = (unsigned char) (x&0xff);\
40 *((buf)+1) = (unsigned char)((x>>8)&0xff);
41
42 struct riff_struct {
43   guint8        id[4];          /* RIFF */
44   guint32       len;
45   guint8        wav_id[4];      /* WAVE */
46 };
47
48 struct chunk_struct {
49   guint8        id[4];
50   guint32       len;
51 };
52
53 struct common_struct {
54   guint16       wFormatTag;
55   guint16       wChannels;
56   guint32       dwSamplesPerSec;
57   guint32       dwAvgBytesPerSec;
58   guint16       wBlockAlign;
59   guint16       wBitsPerSample;         /* Only for PCM */
60 };
61
62 struct wave_header {
63   struct riff_struct    riff;
64   struct chunk_struct   format;
65   struct common_struct  common;
66   struct chunk_struct   data;
67 };
68
69 static GstElementDetails gst_wavenc_details = {
70   "WAV encoder",
71   "Codec/Audio/Encoder",
72   "LGPL",
73   "Encode raw audio into WAV",
74   VERSION,
75   "Iain Holmes <iain@prettypeople.org>",
76   "(C) 2002",
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 static GstElementClass *parent_class = NULL;
115
116 static const GstEventMask *
117 gst_wavenc_get_event_masks (GstPad *pad)
118 {
119         static const GstEventMask src_event_masks[] = {
120                 { GST_EVENT_EOS, 0 },
121                 { 0, }
122         };
123
124         return src_event_masks;
125 }
126
127 static GType
128 gst_wavenc_get_type (void)
129 {
130   static GType type = 0;
131
132   if (type == 0) {
133     static const GTypeInfo info = {
134       sizeof (GstWavEncClass), 
135       NULL, 
136       NULL,
137       (GClassInitFunc) gst_wavenc_class_init, 
138       NULL, 
139       NULL,
140       sizeof (GstWavEnc), 
141       0, 
142       (GInstanceInitFunc) gst_wavenc_init
143     };
144
145     type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavEnc", &info, 0);
146   }
147
148   return type;
149 }
150
151 static GstElementStateReturn
152 gst_wavenc_change_state (GstElement *element)
153 {
154   GstWavEnc *wavenc = GST_WAVENC (element);
155   
156   switch (GST_STATE_TRANSITION (element)) {
157     case GST_STATE_PAUSED_TO_READY:
158       wavenc->setup = FALSE;
159       wavenc->flush_header = TRUE;
160       break;
161     default:
162       break;
163   }
164
165   if (parent_class->change_state) {
166     return parent_class->change_state (element);
167   }
168
169   return GST_STATE_SUCCESS;
170 }
171
172 static void
173 gst_wavenc_class_init (GstWavEncClass *klass)
174 {
175   GstElementClass *element_class;
176
177   element_class = (GstElementClass *) klass;
178   element_class->change_state = gst_wavenc_change_state;
179
180   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
181 }
182
183 static gboolean
184 gst_wavenc_setup (GstWavEnc *wavenc)
185 {
186   struct wave_header wave;
187   gint size = 0x7fffffff; /* Use a bogus size initially */
188
189   wave.common.wChannels = wavenc->channels;
190   wave.common.wBitsPerSample = wavenc->bits;
191   wave.common.dwSamplesPerSec = wavenc->rate;
192
193   memset (wavenc->header, 0, WAV_HEADER_LEN);
194
195   /* Fill out our wav-header with some information */
196   strncpy (wave.riff.id, "RIFF", 4);
197   wave.riff.len = size - 8;
198   strncpy (wave.riff.wav_id, "WAVE", 4);
199
200   strncpy (wave.format.id, "fmt ", 4);
201   wave.format.len = 16;
202
203   wave.common.wFormatTag = WAVE_FORMAT_PCM;
204   wave.common.dwAvgBytesPerSec = wave.common.wChannels * wave.common.dwSamplesPerSec * (wave.common.wBitsPerSample >> 3);
205   wave.common.wBlockAlign = wave.common.wChannels * (wave.common.wBitsPerSample >> 3);
206
207   strncpy (wave.data.id, "data", 4);
208   wave.data.len = size - 44;
209
210   strncpy (wavenc->header, wave.riff.id, 4);
211   WRITE_U32 (wavenc->header + 4, wave.riff.len);
212   strncpy (wavenc->header + 8, wave.riff.wav_id, 4);
213   strncpy (wavenc->header + 12, wave.format.id, 4);
214   WRITE_U32 (wavenc->header + 16, wave.format.len);
215   WRITE_U16 (wavenc->header + 20, wave.common.wFormatTag);
216   WRITE_U16 (wavenc->header + 22, wave.common.wChannels);
217   WRITE_U32 (wavenc->header + 24, wave.common.dwSamplesPerSec);
218   WRITE_U32 (wavenc->header + 28, wave.common.dwAvgBytesPerSec);
219   WRITE_U16 (wavenc->header + 32, wave.common.wBlockAlign);
220   WRITE_U16 (wavenc->header + 34, wave.common.wBitsPerSample);
221   strncpy (wavenc->header + 36, wave.data.id, 4);
222   WRITE_U32 (wavenc->header + 40, wave.data.len);
223
224   wavenc->setup = TRUE;
225   return TRUE;
226 }
227
228 static GstPadLinkReturn
229 gst_wavenc_sinkconnect (GstPad *pad,
230                         GstCaps *caps)
231 {
232   GstWavEnc *wavenc;
233
234   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
235
236   if (!GST_CAPS_IS_FIXED (caps)) {
237     return GST_PAD_LINK_DELAYED;
238   }
239
240   gst_caps_get_int (caps, "channels", &wavenc->channels);
241   gst_caps_get_int (caps, "rate", &wavenc->rate);
242   gst_caps_get_int (caps, "depth", &wavenc->bits);
243
244   gst_wavenc_setup (wavenc);
245
246   if (wavenc->setup) {
247     return GST_PAD_LINK_OK;
248   }
249
250   return GST_PAD_LINK_REFUSED;
251 }
252
253 static void
254 gst_wavenc_stop_file (GstWavEnc *wavenc)
255 {
256   GstEvent *event;
257   GstBuffer *outbuf;
258
259   event = gst_event_new_seek (GST_FORMAT_BYTES |
260                               GST_SEEK_METHOD_SET, 0);
261   gst_pad_send_event (GST_PAD_PEER (wavenc->srcpad), event);
262   
263   outbuf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
264   WRITE_U32 (wavenc->header + 4, wavenc->length);
265   memcpy (GST_BUFFER_DATA (outbuf), wavenc->header, WAV_HEADER_LEN);
266   
267   gst_pad_push (wavenc->srcpad, outbuf);
268 }
269
270 static gboolean
271 gst_wavenc_handle_event (GstPad *pad,
272                          GstEvent *event)
273 {
274   GstWavEnc *wavenc;
275   GstEventType type;
276
277   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
278
279   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
280
281   switch (type) {
282   case GST_EVENT_EOS:
283     wavenc->pad_eos = TRUE;
284     gst_wavenc_stop_file (wavenc);
285     gst_pad_push (wavenc->srcpad,
286                   GST_BUFFER (gst_event_new (GST_EVENT_EOS)));
287     gst_element_set_eos (GST_ELEMENT (wavenc));
288     break;
289
290   default:
291     break;
292   }
293
294   return TRUE;
295 }
296
297 static void
298 gst_wavenc_init (GstWavEnc *wavenc)
299 {
300   wavenc->sinkpad = gst_pad_new_from_template (sinktemplate, "sink");
301   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);
302   gst_pad_set_chain_function (wavenc->sinkpad, gst_wavenc_chain);
303   gst_pad_set_link_function (wavenc->sinkpad, gst_wavenc_sinkconnect);
304   gst_pad_set_event_function (wavenc->sinkpad, gst_wavenc_handle_event);
305   gst_pad_set_event_mask_function (wavenc->sinkpad, gst_wavenc_get_event_masks);
306   
307   wavenc->srcpad = gst_pad_new_from_template (srctemplate, "src");
308   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
309
310   wavenc->setup = FALSE;
311   wavenc->flush_header = TRUE;
312 }
313
314 static void
315 gst_wavenc_chain (GstPad *pad,
316                   GstBuffer *buf)
317 {
318   GstWavEnc *wavenc;
319
320   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
321
322   if (!wavenc->setup) {
323     gst_buffer_unref (buf);
324     gst_element_error (GST_ELEMENT (wavenc), "encoder not initialised (input is not audio?)");
325     return;
326   }
327
328   if (GST_PAD_IS_USABLE (wavenc->srcpad)) {
329     if (wavenc->flush_header) {
330       GstBuffer *outbuf;
331       
332       outbuf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
333       memcpy (GST_BUFFER_DATA (outbuf), wavenc->header, WAV_HEADER_LEN);
334       GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
335       
336       gst_pad_push (wavenc->srcpad, outbuf);
337       wavenc->flush_header = FALSE;
338     }
339
340     wavenc->length += GST_BUFFER_SIZE (buf);
341     gst_pad_push (wavenc->srcpad, buf);
342   }
343 }
344
345 static gboolean
346 plugin_init (GModule *module,
347              GstPlugin *plugin)
348 {
349   GstElementFactory *factory;
350
351   factory = gst_element_factory_new ("wavenc", GST_TYPE_WAVENC,
352                                      &gst_wavenc_details);
353   
354   srctemplate = src_factory ();
355   gst_element_factory_add_pad_template (factory, srctemplate);
356
357   sinktemplate = sink_factory ();
358   gst_element_factory_add_pad_template (factory, sinktemplate);
359
360   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
361
362   return TRUE;
363 }
364
365 GstPluginDesc plugin_desc = {
366   GST_VERSION_MAJOR,
367   GST_VERSION_MINOR,
368   "wavenc",
369   plugin_init
370 };
371