22 |
22 |
23 #ifdef HAVE_CONFIG_H |
23 #ifdef HAVE_CONFIG_H |
24 #include "config.h" |
24 #include "config.h" |
25 #endif |
25 #endif |
26 |
26 |
27 |
|
28 |
|
29 #include <string.h> |
27 #include <string.h> |
30 #include "gstwavenc.h" |
28 #include "gstwavenc.h" |
31 //#include "riff.h" rj |
29 #include "riff.h" |
32 |
30 |
33 #include "gst/riff/riff-ids.h" |
|
34 #include "gst/riff/riff-media.h" |
|
35 #include <gst/gst-i18n-plugin.h> |
|
36 |
31 |
37 #ifdef __SYMBIAN32__ |
32 #ifdef __SYMBIAN32__ |
38 #include<gst/gstinfo.h> |
33 #include<gst/gstinfo.h> |
39 #endif |
34 #endif |
40 |
35 |
41 GST_DEBUG_CATEGORY_STATIC (wavenc_debug); |
36 GST_DEBUG_CATEGORY_STATIC (wavenc_debug); |
42 #define GST_CAT_DEFAULT (wavenc_debug) //rj |
37 #define GST_CAT_DEFAULT wavenc_debug |
43 |
|
44 #define WAVE_FORMAT_PCM 0x0001 |
|
45 |
38 |
46 struct riff_struct |
39 struct riff_struct |
47 { |
40 { |
48 guint8 id[4]; /* RIFF */ |
41 guint8 id[4]; /* RIFF */ |
49 guint32 len; |
42 guint32 len; |
82 |
75 |
83 /* FIXME: mono doesn't produce correct files it seems, at least mplayer xruns */ |
76 /* FIXME: mono doesn't produce correct files it seems, at least mplayer xruns */ |
84 /* Max. of two channels, more channels need WAVFORMATEX with |
77 /* Max. of two channels, more channels need WAVFORMATEX with |
85 * channel layout, which we do not support yet */ |
78 * channel layout, which we do not support yet */ |
86 #define SINK_CAPS \ |
79 #define SINK_CAPS \ |
|
80 "audio/x-raw-int, " \ |
|
81 "rate = (int) [ 1, MAX ], " \ |
|
82 "channels = (int) [ 1, 2 ], " \ |
|
83 "endianness = (int) LITTLE_ENDIAN, " \ |
|
84 "width = (int) 32, " \ |
|
85 "depth = (int) 32, " \ |
|
86 "signed = (boolean) true" \ |
|
87 "; " \ |
|
88 "audio/x-raw-int, " \ |
|
89 "rate = (int) [ 1, MAX ], " \ |
|
90 "channels = (int) [ 1, 2 ], " \ |
|
91 "endianness = (int) LITTLE_ENDIAN, " \ |
|
92 "width = (int) 24, " \ |
|
93 "depth = (int) 24, " \ |
|
94 "signed = (boolean) true" \ |
|
95 "; " \ |
87 "audio/x-raw-int, " \ |
96 "audio/x-raw-int, " \ |
88 "rate = (int) [ 1, MAX ], " \ |
97 "rate = (int) [ 1, MAX ], " \ |
89 "channels = (int) [ 1, 2 ], " \ |
98 "channels = (int) [ 1, 2 ], " \ |
90 "endianness = (int) LITTLE_ENDIAN, " \ |
99 "endianness = (int) LITTLE_ENDIAN, " \ |
91 "width = (int) 16, " \ |
100 "width = (int) 16, " \ |
94 "; " \ |
103 "; " \ |
95 "audio/x-raw-int, " \ |
104 "audio/x-raw-int, " \ |
96 "rate = (int) [ 1, MAX ], " \ |
105 "rate = (int) [ 1, MAX ], " \ |
97 "channels = (int) [ 1, 2 ], " \ |
106 "channels = (int) [ 1, 2 ], " \ |
98 "width = (int) 8, " \ |
107 "width = (int) 8, " \ |
|
108 "depth = (int) 8, " \ |
|
109 "signed = (boolean) false" \ |
|
110 "; " \ |
|
111 "audio/x-raw-float, " \ |
|
112 "rate = (int) [ 1, MAX ], " \ |
|
113 "channels = (int) [ 1, 2 ], " \ |
|
114 "endianness = (int) LITTLE_ENDIAN, " \ |
|
115 "width = (int) { 32, 64 }; " \ |
|
116 "audio/x-alaw, " \ |
|
117 "rate = (int) [ 8000, 192000 ], " \ |
|
118 "channels = (int) [ 1, 2 ], " \ |
|
119 "width = (int) 8, " \ |
|
120 "depth = (int) 8, " \ |
|
121 "signed = (boolean) false; " \ |
|
122 "audio/x-mulaw, " \ |
|
123 "rate = (int) [ 8000, 192000 ], " \ |
|
124 "channels = (int) [ 1, 2 ], " \ |
|
125 "width = (int) 8, " \ |
|
126 "depth = (int) 8, " \ |
99 "signed = (boolean) false" |
127 "signed = (boolean) false" |
100 |
128 |
101 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", |
129 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", |
102 GST_PAD_SINK, |
130 GST_PAD_SINK, |
103 GST_PAD_ALWAYS, |
131 GST_PAD_ALWAYS, |
108 GST_PAD_SRC, |
136 GST_PAD_SRC, |
109 GST_PAD_ALWAYS, |
137 GST_PAD_ALWAYS, |
110 GST_STATIC_CAPS ("audio/x-wav") |
138 GST_STATIC_CAPS ("audio/x-wav") |
111 ); |
139 ); |
112 |
140 |
113 #ifndef __SYMBIAN32__ //rj |
|
114 GST_BOILERPLATE (GstWavEnc, gst_wavenc, GstElement, GST_TYPE_ELEMENT); |
141 GST_BOILERPLATE (GstWavEnc, gst_wavenc, GstElement, GST_TYPE_ELEMENT); |
115 #endif |
|
116 |
|
117 //rj |
|
118 static void gst_wavenc_base_init (gpointer g_class); |
|
119 static void gst_wavenc_class_init (GstWavEncClass * klass); |
|
120 static void gst_wavenc_init (GstWavEnc * wavenc, GstWavEncClass * klass); |
|
121 static GstElementClass *parent_class = NULL; |
|
122 //rj |
|
123 |
142 |
124 static GstFlowReturn gst_wavenc_chain (GstPad * pad, GstBuffer * buf); |
143 static GstFlowReturn gst_wavenc_chain (GstPad * pad, GstBuffer * buf); |
125 static gboolean gst_wavenc_event (GstPad * pad, GstEvent * event); |
144 static gboolean gst_wavenc_event (GstPad * pad, GstEvent * event); |
126 static GstStateChangeReturn gst_wavenc_change_state (GstElement * element, |
145 static GstStateChangeReturn gst_wavenc_change_state (GstElement * element, |
127 GstStateChange transition); |
146 GstStateChange transition); |
128 static gboolean gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps); |
147 static gboolean gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps); |
129 |
148 |
130 |
|
131 |
|
132 |
|
133 GType gst_wavenc_get_type (void) |
|
134 { |
|
135 static GType wavenc_type = 0; |
|
136 |
|
137 if (!wavenc_type) { |
|
138 static const GTypeInfo wavenc_info = { |
|
139 sizeof (GstWavEncClass), |
|
140 gst_wavenc_base_init, |
|
141 NULL, |
|
142 (GClassInitFunc) gst_wavenc_class_init, |
|
143 NULL, |
|
144 NULL, |
|
145 sizeof (GstWavEnc), |
|
146 0, |
|
147 (GInstanceInitFunc) gst_wavenc_init, |
|
148 }; |
|
149 |
|
150 wavenc_type = |
|
151 g_type_register_static (GST_TYPE_ELEMENT, "GstWavEnc", |
|
152 &wavenc_info, 0); |
|
153 } |
|
154 return wavenc_type; |
|
155 } |
|
156 |
|
157 |
|
158 static void |
149 static void |
159 gst_wavenc_base_init (gpointer g_class) |
150 gst_wavenc_base_init (gpointer g_class) |
160 { |
151 { |
161 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
152 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
162 |
153 |
214 buf = gst_buffer_new_and_alloc (WAV_HEADER_LEN); |
202 buf = gst_buffer_new_and_alloc (WAV_HEADER_LEN); |
215 header = GST_BUFFER_DATA (buf); |
203 header = GST_BUFFER_DATA (buf); |
216 memset (header, 0, WAV_HEADER_LEN); |
204 memset (header, 0, WAV_HEADER_LEN); |
217 |
205 |
218 wave.common.wChannels = wavenc->channels; |
206 wave.common.wChannels = wavenc->channels; |
219 wave.common.wBitsPerSample = wavenc->depth; |
207 wave.common.wBitsPerSample = wavenc->width; |
220 wave.common.dwSamplesPerSec = wavenc->rate; |
208 wave.common.dwSamplesPerSec = wavenc->rate; |
221 |
209 |
222 /* Fill out our wav-header with some information */ |
210 /* Fill out our wav-header with some information */ |
223 memcpy (wave.riff.id, "RIFF", 4); |
211 memcpy (wave.riff.id, "RIFF", 4); |
|
212 //g_printf("before wave.riff.len %d \n" ,wave.riff.len); |
224 wave.riff.len = audio_data_size + WAV_HEADER_LEN - 8; |
213 wave.riff.len = audio_data_size + WAV_HEADER_LEN - 8; |
|
214 //g_printf("after wave.riff.len %d \n" ,wave.riff.len); |
225 memcpy (wave.riff.wav_id, "WAVE", 4); |
215 memcpy (wave.riff.wav_id, "WAVE", 4); |
226 |
216 |
227 memcpy (wave.format.id, "fmt ", 4); |
217 memcpy (wave.format.id, "fmt ", 4); |
228 wave.format.len = 16; |
218 wave.format.len = 16; |
229 |
219 |
230 wave.common.wFormatTag = WAVE_FORMAT_PCM; |
220 wave.common.wFormatTag = wavenc->format; |
|
221 wave.common.wBlockAlign = (wavenc->width / 8) * wave.common.wChannels; |
231 wave.common.dwAvgBytesPerSec = |
222 wave.common.dwAvgBytesPerSec = |
232 wave.common.wChannels * wave.common.dwSamplesPerSec * |
223 wave.common.wBlockAlign * wave.common.dwSamplesPerSec; |
233 (wave.common.wBitsPerSample >> 3); |
|
234 wave.common.wBlockAlign = |
|
235 wave.common.wChannels * (wave.common.wBitsPerSample >> 3); |
|
236 |
224 |
237 memcpy (wave.data.id, "data", 4); |
225 memcpy (wave.data.id, "data", 4); |
238 wave.data.len = audio_data_size; |
226 wave.data.len = audio_data_size; |
239 |
227 |
240 memcpy (header, (char *) wave.riff.id, 4); |
228 memcpy (header, (char *) wave.riff.id, 4); |
285 static gboolean |
273 static gboolean |
286 gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps) |
274 gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps) |
287 { |
275 { |
288 GstWavEnc *wavenc; |
276 GstWavEnc *wavenc; |
289 GstStructure *structure; |
277 GstStructure *structure; |
290 gint chans, rate, width, depth; |
278 const gchar *name; |
|
279 gint chans, rate, width; |
291 |
280 |
292 wavenc = GST_WAVENC (gst_pad_get_parent (pad)); |
281 wavenc = GST_WAVENC (gst_pad_get_parent (pad)); |
293 |
282 |
294 if (wavenc->sent_header) { |
283 if (wavenc->sent_header) { |
295 GST_WARNING_OBJECT (wavenc, "cannot change format in middle of stream"); |
284 GST_WARNING_OBJECT (wavenc, "cannot change format in middle of stream"); |
297 } |
286 } |
298 |
287 |
299 GST_DEBUG_OBJECT (wavenc, "got caps: %" GST_PTR_FORMAT, caps); |
288 GST_DEBUG_OBJECT (wavenc, "got caps: %" GST_PTR_FORMAT, caps); |
300 |
289 |
301 structure = gst_caps_get_structure (caps, 0); |
290 structure = gst_caps_get_structure (caps, 0); |
|
291 name = gst_structure_get_name (structure); |
|
292 |
302 if (!gst_structure_get_int (structure, "channels", &chans) || |
293 if (!gst_structure_get_int (structure, "channels", &chans) || |
303 !gst_structure_get_int (structure, "rate", &rate) || |
294 !gst_structure_get_int (structure, "rate", &rate)) { |
304 !gst_structure_get_int (structure, "width", &width) || |
|
305 (width != 8 && !gst_structure_get_int (structure, "depth", &depth))) { |
|
306 GST_WARNING_OBJECT (wavenc, "caps incomplete"); |
295 GST_WARNING_OBJECT (wavenc, "caps incomplete"); |
307 goto fail; |
296 goto fail; |
308 } |
297 } |
309 |
298 |
|
299 if (strcmp (name, "audio/x-raw-int") == 0) { |
|
300 if (!gst_structure_get_int (structure, "width", &width)) { |
|
301 GST_WARNING_OBJECT (wavenc, "caps incomplete"); |
|
302 goto fail; |
|
303 } |
|
304 wavenc->format = GST_RIFF_WAVE_FORMAT_PCM; |
|
305 wavenc->width = width; |
|
306 } else if (strcmp (name, "audio/x-raw-float") == 0) { |
|
307 if (!gst_structure_get_int (structure, "width", &width)) { |
|
308 GST_WARNING_OBJECT (wavenc, "caps incomplete"); |
|
309 goto fail; |
|
310 } |
|
311 wavenc->format = GST_RIFF_WAVE_FORMAT_FLOAT; |
|
312 wavenc->width = width; |
|
313 } else if (strcmp (name, "audio/x-alaw") == 0) { |
|
314 wavenc->format = GST_RIFF_WAVE_FORMAT_ALAW; |
|
315 wavenc->width = 8; |
|
316 } else if (strcmp (name, "audio/x-mulaw") == 0) { |
|
317 wavenc->format = GST_RIFF_WAVE_FORMAT_MULAW; |
|
318 wavenc->width = 8; |
|
319 } else { |
|
320 GST_WARNING_OBJECT (wavenc, "Unsupported format %s", name); |
|
321 goto fail; |
|
322 } |
|
323 |
310 wavenc->channels = chans; |
324 wavenc->channels = chans; |
311 wavenc->width = width; |
|
312 wavenc->depth = (width == 8) ? 8 : depth; |
|
313 wavenc->rate = rate; |
325 wavenc->rate = rate; |
314 |
326 |
315 GST_LOG_OBJECT (wavenc, "accepted caps: chans=%u width=%u depth=%u rate=%u", |
327 GST_LOG_OBJECT (wavenc, |
316 wavenc->channels, wavenc->width, wavenc->depth, wavenc->rate); |
328 "accepted caps: format=0x%04x chans=%u width=%u rate=%u", |
|
329 wavenc->format, wavenc->channels, wavenc->width, wavenc->rate); |
317 |
330 |
318 gst_object_unref (wavenc); |
331 gst_object_unref (wavenc); |
319 return TRUE; |
332 return TRUE; |
320 |
333 |
321 fail: |
334 fail: |
623 } |
636 } |
624 #endif |
637 #endif |
625 /* write header with correct length values */ |
638 /* write header with correct length values */ |
626 gst_wavenc_push_header (wavenc, wavenc->length); |
639 gst_wavenc_push_header (wavenc, wavenc->length); |
627 |
640 |
|
641 /* we're done with this file */ |
|
642 wavenc->finished_properly = TRUE; |
|
643 |
628 /* and forward the EOS event */ |
644 /* and forward the EOS event */ |
629 res = gst_pad_event_default (pad, event); |
645 res = gst_pad_event_default (pad, event); |
630 break; |
646 break; |
631 } |
647 } |
632 case GST_EVENT_NEWSEGMENT: |
648 case GST_EVENT_NEWSEGMENT: |
654 if (!wavenc->sent_header) { |
670 if (!wavenc->sent_header) { |
655 /* use bogus size initially, we'll write the real |
671 /* use bogus size initially, we'll write the real |
656 * header when we get EOS and know the exact length */ |
672 * header when we get EOS and know the exact length */ |
657 flow = gst_wavenc_push_header (wavenc, 0x7FFF0000); |
673 flow = gst_wavenc_push_header (wavenc, 0x7FFF0000); |
658 |
674 |
|
675 /* starting a file, means we have to finish it properly */ |
|
676 wavenc->finished_properly = FALSE; |
|
677 |
659 if (flow != GST_FLOW_OK) |
678 if (flow != GST_FLOW_OK) |
660 return flow; |
679 return flow; |
661 |
680 |
662 GST_DEBUG_OBJECT (wavenc, "wrote dummy header"); |
681 GST_DEBUG_OBJECT (wavenc, "wrote dummy header"); |
663 wavenc->sent_header = TRUE; |
682 wavenc->sent_header = TRUE; |
664 } |
683 } |
665 |
|
666 wavenc->length += GST_BUFFER_SIZE (buf); |
|
667 |
684 |
668 GST_LOG_OBJECT (wavenc, "pushing %u bytes raw audio, ts=%" GST_TIME_FORMAT, |
685 GST_LOG_OBJECT (wavenc, "pushing %u bytes raw audio, ts=%" GST_TIME_FORMAT, |
669 GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); |
686 GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); |
670 |
687 |
671 buf = gst_buffer_make_metadata_writable (buf); |
688 buf = gst_buffer_make_metadata_writable (buf); |
672 gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad)); |
689 gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad)); |
673 GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length; |
690 GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length; |
674 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; |
691 GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; |
675 |
692 |
|
693 wavenc->length += GST_BUFFER_SIZE (buf); |
|
694 |
676 flow = gst_pad_push (wavenc->srcpad, buf); |
695 flow = gst_pad_push (wavenc->srcpad, buf); |
677 |
696 |
678 return flow; |
697 return flow; |
679 } |
698 } |
680 |
699 |
684 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
703 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
685 GstWavEnc *wavenc = GST_WAVENC (element); |
704 GstWavEnc *wavenc = GST_WAVENC (element); |
686 |
705 |
687 switch (transition) { |
706 switch (transition) { |
688 case GST_STATE_CHANGE_NULL_TO_READY: |
707 case GST_STATE_CHANGE_NULL_TO_READY: |
|
708 wavenc->format = 0; |
689 wavenc->channels = 0; |
709 wavenc->channels = 0; |
690 wavenc->width = 0; |
710 wavenc->width = 0; |
691 wavenc->depth = 0; |
|
692 wavenc->rate = 0; |
711 wavenc->rate = 0; |
693 wavenc->length = 0; |
712 wavenc->length = 0; |
694 wavenc->sent_header = FALSE; |
713 wavenc->sent_header = FALSE; |
|
714 /* its true because we haven't writen anything */ |
|
715 wavenc->finished_properly = TRUE; |
695 break; |
716 break; |
696 default: |
717 default: |
697 break; |
718 break; |
698 } |
719 } |
699 |
720 |
700 ret = parent_class->change_state (element, transition); |
721 ret = parent_class->change_state (element, transition); |
701 if (ret != GST_STATE_CHANGE_SUCCESS) |
722 if (ret != GST_STATE_CHANGE_SUCCESS) |
702 return ret; |
723 return ret; |
703 |
724 |
|
725 switch (transition) { |
|
726 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
727 if (!wavenc->finished_properly) { |
|
728 GST_ELEMENT_WARNING (wavenc, STREAM, MUX, |
|
729 ("Wav stream not finished properly"), |
|
730 ("Wav stream not finished properly, no EOS received " |
|
731 "before shutdown")); |
|
732 } |
|
733 break; |
|
734 default: |
|
735 break; |
|
736 } |
|
737 |
704 return ret; |
738 return ret; |
705 } |
739 } |
706 |
740 |
707 static gboolean |
741 static gboolean |
708 plugin_init (GstPlugin * plugin) |
742 plugin_init (GstPlugin * plugin) |
709 { |
743 { |
710 return gst_element_register (plugin, "wavenc", GST_RANK_NONE, |
744 return gst_element_register (plugin, "wavenc", GST_RANK_PRIMARY, |
711 GST_TYPE_WAVENC); |
745 GST_TYPE_WAVENC); |
712 } |
746 } |
713 |
747 |
714 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
748 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
715 GST_VERSION_MINOR, |
749 GST_VERSION_MINOR, |