|
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ |
|
2 /* GStreamer |
|
3 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
|
4 * Copyright (C) <2006> Nokia Corporation, Stefan Kost <stefan.kost@nokia.com>. |
|
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 * SECTION:element-wavparse |
|
24 * |
|
25 * <refsect2> |
|
26 * <para> |
|
27 * Parse a .wav file into raw or compressed audio. |
|
28 * </para> |
|
29 * <para> |
|
30 * This element currently only supports pull based scheduling. |
|
31 * </para> |
|
32 * <title>Example launch line</title> |
|
33 * <para> |
|
34 * <programlisting> |
|
35 * gst-launch filesrc location=sine.wav ! wavparse ! audioconvert ! alsasink |
|
36 * </programlisting> |
|
37 * Read a wav file and output to the soundcard using the ALSA element. The |
|
38 * wav file is assumed to contain raw uncompressed samples. |
|
39 * </para> |
|
40 * <para> |
|
41 * <programlisting> |
|
42 * gst-launch gnomevfssrc location=http://www.example.org/sine.wav ! queue ! wavparse ! audioconvert ! alsasink |
|
43 * </programlisting> |
|
44 * Stream data from |
|
45 * </para> |
|
46 * </refsect2> |
|
47 * |
|
48 * Last reviewed on 2006-03-03 (0.10.3) |
|
49 */ |
|
50 |
|
51 #ifdef HAVE_CONFIG_H |
|
52 #include "config.h" |
|
53 #endif |
|
54 |
|
55 #include "string.h" |
|
56 #include "gstwavparse.h" |
|
57 #include <gst/riff/riff-ids.h> |
|
58 #include <gst/riff/riff-media.h> |
|
59 #include <gst/riff/riff-read.h> |
|
60 |
|
61 #ifndef __SYMBIAN32__ |
|
62 #include <gst/gst-i18n-plugin.h> |
|
63 #else |
|
64 #include "gst/gst-i18n-plugin.h" |
|
65 #endif |
|
66 |
|
67 #ifdef __SYMBIAN32__ |
|
68 #include <gst/gstinfo.h> |
|
69 #endif |
|
70 |
|
71 #ifndef G_MAXUINT32 |
|
72 #define G_MAXUINT32 0xffffffff |
|
73 #endif |
|
74 |
|
75 GST_DEBUG_CATEGORY_STATIC (wavparse_debug); |
|
76 #define GST_CAT_DEFAULT (wavparse_debug) |
|
77 |
|
78 static void gst_wavparse_base_init (gpointer g_class); |
|
79 static void gst_wavparse_class_init (GstWavParseClass * klass); |
|
80 static void gst_wavparse_init (GstWavParse * wavparse); |
|
81 static void gst_wavparse_dispose (GObject * object); |
|
82 |
|
83 static gboolean gst_wavparse_sink_activate (GstPad * sinkpad); |
|
84 static gboolean gst_wavparse_sink_activate_pull (GstPad * sinkpad, |
|
85 gboolean active); |
|
86 static gboolean gst_wavparse_send_event (GstElement * element, |
|
87 GstEvent * event); |
|
88 static GstFlowReturn gst_wavparse_chain (GstPad * pad, GstBuffer * buf); |
|
89 static GstStateChangeReturn gst_wavparse_change_state (GstElement * element, |
|
90 GstStateChange transition); |
|
91 |
|
92 static gboolean gst_wavparse_pad_query (GstPad * pad, GstQuery * query); |
|
93 static const GstQueryType *gst_wavparse_get_query_types (GstPad * pad); |
|
94 static gboolean gst_wavparse_pad_convert (GstPad * pad, |
|
95 GstFormat src_format, |
|
96 gint64 src_value, GstFormat * dest_format, gint64 * dest_value); |
|
97 |
|
98 static void gst_wavparse_loop (GstPad * pad); |
|
99 static gboolean gst_wavparse_srcpad_event (GstPad * pad, GstEvent * event); |
|
100 static void gst_wavparse_get_property (GObject * object, guint prop_id, |
|
101 GValue * value, GParamSpec * pspec); |
|
102 |
|
103 static const GstElementDetails gst_wavparse_details = |
|
104 GST_ELEMENT_DETAILS ("WAV audio demuxer", |
|
105 "Codec/Demuxer/Audio", |
|
106 "Parse a .wav file into raw audio", |
|
107 "Erik Walthinsen <omega@cse.ogi.edu>"); |
|
108 |
|
109 static GstStaticPadTemplate sink_template_factory = |
|
110 //GST_STATIC_PAD_TEMPLATE ("wavparse_sink", |
|
111 GST_STATIC_PAD_TEMPLATE ("sink", |
|
112 GST_PAD_SINK, |
|
113 GST_PAD_ALWAYS, |
|
114 GST_STATIC_CAPS ("audio/x-wav") |
|
115 ); |
|
116 |
|
117 /* the pad is marked a sometimes and is added to the element when the |
|
118 * exact type is known. This makes it much easier for a static autoplugger |
|
119 * to connect the right decoder when needed. |
|
120 */ |
|
121 static GstStaticPadTemplate src_template_factory = |
|
122 // GST_STATIC_PAD_TEMPLATE ("wavparse_src", |
|
123 GST_STATIC_PAD_TEMPLATE ("src", |
|
124 GST_PAD_SRC, |
|
125 GST_PAD_SOMETIMES, |
|
126 GST_STATIC_CAPS ("audio/x-raw-int, " |
|
127 "endianness = (int) little_endian, " |
|
128 "signed = (boolean) { true, false }, " |
|
129 "width = (int) { 8, 16, 24, 32 }, " |
|
130 "depth = (int) { 8, 16, 24, 32 }, " |
|
131 "rate = (int) [ 8000, 96000 ], " |
|
132 "channels = (int) [ 1, 8 ]; " |
|
133 "audio/mpeg, " |
|
134 "mpegversion = (int) 1, " |
|
135 "layer = (int) [ 1, 3 ], " |
|
136 "rate = (int) [ 8000, 48000 ], " |
|
137 "channels = (int) [ 1, 2 ]; " |
|
138 "audio/x-alaw, " |
|
139 "rate = (int) [ 8000, 48000 ], " |
|
140 "channels = (int) [ 1, 2 ]; " |
|
141 "audio/x-mulaw, " |
|
142 "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ];" |
|
143 "audio/x-adpcm, " |
|
144 "layout = (string) microsoft, " |
|
145 "block_align = (int) [ 1, 8192 ], " |
|
146 "rate = (int) [ 8000, 48000 ], " |
|
147 "channels = (int) [ 1, 2 ]; " |
|
148 "audio/x-adpcm, " |
|
149 "layout = (string) dvi, " |
|
150 "block_align = (int) [ 1, 8192 ], " |
|
151 "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ];" |
|
152 "audio/x-vnd.sony.atrac3;" |
|
153 "audio/x-dts;" "audio/x-wma, " "wmaversion = (int) [ 1, 2 ]") |
|
154 ); |
|
155 |
|
156 |
|
157 static GstElementClass *parent_class = NULL; |
|
158 |
|
159 GType |
|
160 gst_wavparse_get_type (void) |
|
161 { |
|
162 static GType wavparse_type = 0; |
|
163 |
|
164 if (!wavparse_type) { |
|
165 static const GTypeInfo wavparse_info = { |
|
166 sizeof (GstWavParseClass), |
|
167 gst_wavparse_base_init, |
|
168 NULL, |
|
169 (GClassInitFunc) gst_wavparse_class_init, |
|
170 NULL, |
|
171 NULL, |
|
172 sizeof (GstWavParse), |
|
173 0, |
|
174 (GInstanceInitFunc) gst_wavparse_init, |
|
175 }; |
|
176 |
|
177 wavparse_type = |
|
178 g_type_register_static (GST_TYPE_ELEMENT, "GstWavParse", |
|
179 &wavparse_info, 0); |
|
180 } |
|
181 return wavparse_type; |
|
182 } |
|
183 |
|
184 |
|
185 static void |
|
186 gst_wavparse_base_init (gpointer g_class) |
|
187 { |
|
188 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
|
189 |
|
190 /* register src pads */ |
|
191 gst_element_class_add_pad_template (element_class, |
|
192 gst_static_pad_template_get (&sink_template_factory)); |
|
193 gst_element_class_add_pad_template (element_class, |
|
194 gst_static_pad_template_get (&src_template_factory)); |
|
195 gst_element_class_set_details (element_class, &gst_wavparse_details); |
|
196 } |
|
197 |
|
198 static void |
|
199 gst_wavparse_class_init (GstWavParseClass * klass) |
|
200 { |
|
201 GstElementClass *gstelement_class; |
|
202 GObjectClass *object_class; |
|
203 |
|
204 gstelement_class = (GstElementClass *) klass; |
|
205 object_class = (GObjectClass *) klass; |
|
206 |
|
207 parent_class = g_type_class_peek_parent (klass); |
|
208 |
|
209 object_class->get_property = gst_wavparse_get_property; |
|
210 object_class->dispose = gst_wavparse_dispose; |
|
211 |
|
212 gstelement_class->change_state = gst_wavparse_change_state; |
|
213 gstelement_class->send_event = gst_wavparse_send_event; |
|
214 } |
|
215 |
|
216 |
|
217 static void |
|
218 gst_wavparse_dispose (GObject * object) |
|
219 { |
|
220 #ifndef __SYMBIAN32__ |
|
221 GST_DEBUG("WAV: Dispose\n"); |
|
222 #endif |
|
223 GstWavParse *wav = GST_WAVPARSE (object); |
|
224 #ifdef __SYMBIAN32__ |
|
225 GST_DEBUG("WAV: Dispose\n"); |
|
226 #endif |
|
227 |
|
228 if (wav->adapter) { |
|
229 g_object_unref (wav->adapter); |
|
230 wav->adapter = NULL; |
|
231 } |
|
232 |
|
233 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
234 } |
|
235 |
|
236 |
|
237 static void |
|
238 gst_wavparse_reset (GstWavParse * wavparse) |
|
239 { |
|
240 wavparse->state = GST_WAVPARSE_START; |
|
241 |
|
242 /* These will all be set correctly in the fmt chunk */ |
|
243 wavparse->depth = 0; |
|
244 wavparse->rate = 0; |
|
245 wavparse->width = 0; |
|
246 wavparse->channels = 0; |
|
247 wavparse->blockalign = 0; |
|
248 wavparse->bps = 0; |
|
249 wavparse->offset = 0; |
|
250 wavparse->end_offset = 0; |
|
251 wavparse->dataleft = 0; |
|
252 wavparse->datasize = 0; |
|
253 wavparse->datastart = 0; |
|
254 wavparse->got_fmt = FALSE; |
|
255 wavparse->first = TRUE; |
|
256 |
|
257 if (wavparse->seek_event) |
|
258 gst_event_unref (wavparse->seek_event); |
|
259 wavparse->seek_event = NULL; |
|
260 |
|
261 /* we keep the segment info in time */ |
|
262 gst_segment_init (&wavparse->segment, GST_FORMAT_TIME); |
|
263 } |
|
264 |
|
265 static void |
|
266 gst_wavparse_init (GstWavParse * wavparse) |
|
267 { |
|
268 gst_wavparse_reset (wavparse); |
|
269 |
|
270 /* sink */ |
|
271 wavparse->sinkpad = |
|
272 gst_pad_new_from_static_template (&sink_template_factory, "sink"); |
|
273 gst_pad_set_activate_function (wavparse->sinkpad, |
|
274 GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate)); |
|
275 gst_pad_set_activatepull_function (wavparse->sinkpad, |
|
276 GST_DEBUG_FUNCPTR (gst_wavparse_sink_activate_pull)); |
|
277 gst_pad_set_chain_function (wavparse->sinkpad, |
|
278 GST_DEBUG_FUNCPTR (gst_wavparse_chain)); |
|
279 gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->sinkpad); |
|
280 |
|
281 /* src, will be created later */ |
|
282 wavparse->srcpad = NULL; |
|
283 } |
|
284 |
|
285 static void |
|
286 gst_wavparse_destroy_sourcepad (GstWavParse * wavparse) |
|
287 { |
|
288 if (wavparse->srcpad) { |
|
289 gst_element_remove_pad (GST_ELEMENT (wavparse), wavparse->srcpad); |
|
290 wavparse->srcpad = NULL; |
|
291 } |
|
292 } |
|
293 |
|
294 static void |
|
295 gst_wavparse_create_sourcepad (GstWavParse * wavparse) |
|
296 { |
|
297 /* destroy previous one */ |
|
298 gst_wavparse_destroy_sourcepad (wavparse); |
|
299 |
|
300 /* source */ |
|
301 wavparse->srcpad = |
|
302 gst_pad_new_from_static_template (&src_template_factory, "src"); |
|
303 gst_pad_use_fixed_caps (wavparse->srcpad); |
|
304 gst_pad_set_query_type_function (wavparse->srcpad, |
|
305 GST_DEBUG_FUNCPTR (gst_wavparse_get_query_types)); |
|
306 gst_pad_set_query_function (wavparse->srcpad, |
|
307 GST_DEBUG_FUNCPTR (gst_wavparse_pad_query)); |
|
308 gst_pad_set_event_function (wavparse->srcpad, |
|
309 GST_DEBUG_FUNCPTR (gst_wavparse_srcpad_event)); |
|
310 |
|
311 GST_DEBUG_OBJECT (wavparse, "srcpad created"); |
|
312 } |
|
313 |
|
314 static void |
|
315 gst_wavparse_get_property (GObject * object, |
|
316 guint prop_id, GValue * value, GParamSpec * pspec) |
|
317 { |
|
318 GstWavParse *wavparse; |
|
319 |
|
320 wavparse = GST_WAVPARSE (object); |
|
321 |
|
322 switch (prop_id) { |
|
323 default: |
|
324 break; |
|
325 } |
|
326 } |
|
327 |
|
328 |
|
329 |
|
330 #if 0 |
|
331 static void |
|
332 gst_wavparse_parse_adtl (GstWavParse * wavparse, int len) |
|
333 { |
|
334 guint32 got_bytes; |
|
335 GstByteStream *bs = wavparse->bs; |
|
336 gst_riff_chunk *temp_chunk, chunk; |
|
337 guint8 *tempdata; |
|
338 struct _gst_riff_labl labl, *temp_labl; |
|
339 struct _gst_riff_ltxt ltxt, *temp_ltxt; |
|
340 struct _gst_riff_note note, *temp_note; |
|
341 char *label_name; |
|
342 GstProps *props; |
|
343 GstPropsEntry *entry; |
|
344 GstCaps *new_caps; |
|
345 GList *caps = NULL; |
|
346 |
|
347 props = wavparse->metadata->properties; |
|
348 |
|
349 while (len > 0) { |
|
350 got_bytes = |
|
351 gst_bytestream_peek_bytes (bs, &tempdata, sizeof (gst_riff_chunk)); |
|
352 if (got_bytes != sizeof (gst_riff_chunk)) { |
|
353 return; |
|
354 } |
|
355 temp_chunk = (gst_riff_chunk *) tempdata; |
|
356 |
|
357 chunk.id = GUINT32_FROM_LE (temp_chunk->id); |
|
358 chunk.size = GUINT32_FROM_LE (temp_chunk->size); |
|
359 |
|
360 if (chunk.size == 0) { |
|
361 gst_bytestream_flush (bs, sizeof (gst_riff_chunk)); |
|
362 len -= sizeof (gst_riff_chunk); |
|
363 continue; |
|
364 } |
|
365 |
|
366 switch (chunk.id) { |
|
367 case GST_RIFF_adtl_labl: |
|
368 got_bytes = |
|
369 gst_bytestream_peek_bytes (bs, &tempdata, |
|
370 sizeof (struct _gst_riff_labl)); |
|
371 if (got_bytes != sizeof (struct _gst_riff_labl)) { |
|
372 return; |
|
373 } |
|
374 |
|
375 temp_labl = (struct _gst_riff_labl *) tempdata; |
|
376 labl.id = GUINT32_FROM_LE (temp_labl->id); |
|
377 labl.size = GUINT32_FROM_LE (temp_labl->size); |
|
378 labl.identifier = GUINT32_FROM_LE (temp_labl->identifier); |
|
379 |
|
380 gst_bytestream_flush (bs, sizeof (struct _gst_riff_labl)); |
|
381 len -= sizeof (struct _gst_riff_labl); |
|
382 |
|
383 got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, labl.size - 4); |
|
384 if (got_bytes != labl.size - 4) { |
|
385 return; |
|
386 } |
|
387 |
|
388 label_name = (char *) tempdata; |
|
389 |
|
390 gst_bytestream_flush (bs, ((labl.size - 4) + 1) & ~1); |
|
391 len -= (((labl.size - 4) + 1) & ~1); |
|
392 |
|
393 new_caps = gst_caps_new ("label", |
|
394 "application/x-gst-metadata", |
|
395 gst_props_new ("identifier", G_TYPE_INT (labl.identifier), |
|
396 "name", G_TYPE_STRING (label_name), NULL)); |
|
397 |
|
398 if (gst_props_get (props, "labels", &caps, NULL)) { |
|
399 caps = g_list_append (caps, new_caps); |
|
400 } else { |
|
401 caps = g_list_append (NULL, new_caps); |
|
402 |
|
403 entry = gst_props_entry_new ("labels", GST_PROPS_GLIST (caps)); |
|
404 gst_props_add_entry (props, entry); |
|
405 } |
|
406 |
|
407 break; |
|
408 |
|
409 case GST_RIFF_adtl_ltxt: |
|
410 got_bytes = |
|
411 gst_bytestream_peek_bytes (bs, &tempdata, |
|
412 sizeof (struct _gst_riff_ltxt)); |
|
413 if (got_bytes != sizeof (struct _gst_riff_ltxt)) { |
|
414 return; |
|
415 } |
|
416 |
|
417 temp_ltxt = (struct _gst_riff_ltxt *) tempdata; |
|
418 ltxt.id = GUINT32_FROM_LE (temp_ltxt->id); |
|
419 ltxt.size = GUINT32_FROM_LE (temp_ltxt->size); |
|
420 ltxt.identifier = GUINT32_FROM_LE (temp_ltxt->identifier); |
|
421 ltxt.length = GUINT32_FROM_LE (temp_ltxt->length); |
|
422 ltxt.purpose = GUINT32_FROM_LE (temp_ltxt->purpose); |
|
423 ltxt.country = GUINT16_FROM_LE (temp_ltxt->country); |
|
424 ltxt.language = GUINT16_FROM_LE (temp_ltxt->language); |
|
425 ltxt.dialect = GUINT16_FROM_LE (temp_ltxt->dialect); |
|
426 ltxt.codepage = GUINT16_FROM_LE (temp_ltxt->codepage); |
|
427 |
|
428 gst_bytestream_flush (bs, sizeof (struct _gst_riff_ltxt)); |
|
429 len -= sizeof (struct _gst_riff_ltxt); |
|
430 |
|
431 if (ltxt.size - 20 > 0) { |
|
432 got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, ltxt.size - 20); |
|
433 if (got_bytes != ltxt.size - 20) { |
|
434 return; |
|
435 } |
|
436 |
|
437 gst_bytestream_flush (bs, ((ltxt.size - 20) + 1) & ~1); |
|
438 len -= (((ltxt.size - 20) + 1) & ~1); |
|
439 |
|
440 label_name = (char *) tempdata; |
|
441 } else { |
|
442 label_name = ""; |
|
443 } |
|
444 |
|
445 new_caps = gst_caps_new ("ltxt", |
|
446 "application/x-gst-metadata", |
|
447 gst_props_new ("identifier", G_TYPE_INT (ltxt.identifier), |
|
448 "name", G_TYPE_STRING (label_name), |
|
449 "length", G_TYPE_INT (ltxt.length), NULL)); |
|
450 |
|
451 if (gst_props_get (props, "ltxts", &caps, NULL)) { |
|
452 caps = g_list_append (caps, new_caps); |
|
453 } else { |
|
454 caps = g_list_append (NULL, new_caps); |
|
455 |
|
456 entry = gst_props_entry_new ("ltxts", GST_PROPS_GLIST (caps)); |
|
457 gst_props_add_entry (props, entry); |
|
458 } |
|
459 |
|
460 break; |
|
461 |
|
462 case GST_RIFF_adtl_note: |
|
463 got_bytes = |
|
464 gst_bytestream_peek_bytes (bs, &tempdata, |
|
465 sizeof (struct _gst_riff_note)); |
|
466 if (got_bytes != sizeof (struct _gst_riff_note)) { |
|
467 return; |
|
468 } |
|
469 |
|
470 temp_note = (struct _gst_riff_note *) tempdata; |
|
471 note.id = GUINT32_FROM_LE (temp_note->id); |
|
472 note.size = GUINT32_FROM_LE (temp_note->size); |
|
473 note.identifier = GUINT32_FROM_LE (temp_note->identifier); |
|
474 |
|
475 gst_bytestream_flush (bs, sizeof (struct _gst_riff_note)); |
|
476 len -= sizeof (struct _gst_riff_note); |
|
477 |
|
478 got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, note.size - 4); |
|
479 if (got_bytes != note.size - 4) { |
|
480 return; |
|
481 } |
|
482 |
|
483 gst_bytestream_flush (bs, ((note.size - 4) + 1) & ~1); |
|
484 len -= (((note.size - 4) + 1) & ~1); |
|
485 |
|
486 label_name = (char *) tempdata; |
|
487 |
|
488 new_caps = gst_caps_new ("note", |
|
489 "application/x-gst-metadata", |
|
490 gst_props_new ("identifier", G_TYPE_INT (note.identifier), |
|
491 "name", G_TYPE_STRING (label_name), NULL)); |
|
492 |
|
493 if (gst_props_get (props, "notes", &caps, NULL)) { |
|
494 caps = g_list_append (caps, new_caps); |
|
495 } else { |
|
496 caps = g_list_append (NULL, new_caps); |
|
497 |
|
498 entry = gst_props_entry_new ("notes", GST_PROPS_GLIST (caps)); |
|
499 gst_props_add_entry (props, entry); |
|
500 } |
|
501 |
|
502 break; |
|
503 |
|
504 default: |
|
505 g_print ("Unknown chunk: %" GST_FOURCC_FORMAT "\n", |
|
506 GST_FOURCC_ARGS (chunk.id)); |
|
507 return; |
|
508 } |
|
509 } |
|
510 |
|
511 g_object_notify (G_OBJECT (wavparse), "metadata"); |
|
512 } |
|
513 |
|
514 static void |
|
515 gst_wavparse_parse_cues (GstWavParse * wavparse, int len) |
|
516 { |
|
517 guint32 got_bytes; |
|
518 GstByteStream *bs = wavparse->bs; |
|
519 struct _gst_riff_cue *temp_cue, cue; |
|
520 struct _gst_riff_cuepoints *points; |
|
521 guint8 *tempdata; |
|
522 int i; |
|
523 GList *cues = NULL; |
|
524 GstPropsEntry *entry; |
|
525 |
|
526 while (len > 0) { |
|
527 int required; |
|
528 |
|
529 got_bytes = |
|
530 gst_bytestream_peek_bytes (bs, &tempdata, |
|
531 sizeof (struct _gst_riff_cue)); |
|
532 temp_cue = (struct _gst_riff_cue *) tempdata; |
|
533 |
|
534 /* fixup for our big endian friends */ |
|
535 cue.id = GUINT32_FROM_LE (temp_cue->id); |
|
536 cue.size = GUINT32_FROM_LE (temp_cue->size); |
|
537 cue.cuepoints = GUINT32_FROM_LE (temp_cue->cuepoints); |
|
538 |
|
539 gst_bytestream_flush (bs, sizeof (struct _gst_riff_cue)); |
|
540 if (got_bytes != sizeof (struct _gst_riff_cue)) { |
|
541 return; |
|
542 } |
|
543 |
|
544 len -= sizeof (struct _gst_riff_cue); |
|
545 |
|
546 /* -4 because cue.size contains the cuepoints size |
|
547 and we've already flushed that out of the system */ |
|
548 required = cue.size - 4; |
|
549 got_bytes = gst_bytestream_peek_bytes (bs, &tempdata, required); |
|
550 gst_bytestream_flush (bs, ((required) + 1) & ~1); |
|
551 if (got_bytes != required) { |
|
552 return; |
|
553 } |
|
554 |
|
555 len -= (((cue.size - 4) + 1) & ~1); |
|
556 |
|
557 /* now we have an array of struct _gst_riff_cuepoints in tempdata */ |
|
558 points = (struct _gst_riff_cuepoints *) tempdata; |
|
559 |
|
560 for (i = 0; i < cue.cuepoints; i++) { |
|
561 GstCaps *caps; |
|
562 |
|
563 caps = gst_caps_new ("cues", |
|
564 "application/x-gst-metadata", |
|
565 gst_props_new ("identifier", G_TYPE_INT (points[i].identifier), |
|
566 "position", G_TYPE_INT (points[i].offset), NULL)); |
|
567 cues = g_list_append (cues, caps); |
|
568 } |
|
569 |
|
570 entry = gst_props_entry_new ("cues", GST_PROPS_GLIST (cues)); |
|
571 gst_props_add_entry (wavparse->metadata->properties, entry); |
|
572 } |
|
573 |
|
574 g_object_notify (G_OBJECT (wavparse), "metadata"); |
|
575 } |
|
576 |
|
577 /* Read 'fmt ' header */ |
|
578 static gboolean |
|
579 gst_wavparse_fmt (GstWavParse * wav) |
|
580 { |
|
581 gst_riff_strf_auds *header = NULL; |
|
582 GstCaps *caps; |
|
583 |
|
584 if (!gst_riff_read_strf_auds (wav, &header)) { |
|
585 g_warning ("Not fmt"); |
|
586 return FALSE; |
|
587 } |
|
588 |
|
589 wav->format = header->format; |
|
590 wav->rate = header->rate; |
|
591 wav->channels = header->channels; |
|
592 if (wav->channels == 0) { |
|
593 GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), |
|
594 ("Stream claims to contain zero channels - invalid data")); |
|
595 g_free (header); |
|
596 return FALSE; |
|
597 } |
|
598 wav->blockalign = header->blockalign; |
|
599 wav->width = (header->blockalign * 8) / header->channels; |
|
600 wav->depth = header->size; |
|
601 wav->bps = header->av_bps; |
|
602 if (wav->bps <= 0) { |
|
603 GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), |
|
604 ("Stream claims to bitrate of <= zero - invalid data")); |
|
605 g_free (header); |
|
606 return FALSE; |
|
607 } |
|
608 |
|
609 /* Note: gst_riff_create_audio_caps might nedd to fix values in |
|
610 * the header header depending on the format, so call it first */ |
|
611 caps = gst_riff_create_audio_caps (header->format, NULL, header, NULL); |
|
612 |
|
613 g_free (header); |
|
614 |
|
615 if (caps) { |
|
616 gst_wavparse_create_sourcepad (wav); |
|
617 gst_pad_use_fixed_caps (wav->srcpad); |
|
618 gst_pad_set_active (wav->srcpad, TRUE); |
|
619 gst_pad_set_caps (wav->srcpad, caps); |
|
620 gst_caps_free (caps); |
|
621 gst_element_add_pad (GST_ELEMENT (wav), wav->srcpad); |
|
622 gst_element_no_more_pads (GST_ELEMENT (wav)); |
|
623 GST_DEBUG ("frequency %d, channels %d", wav->rate, wav->channels); |
|
624 } else { |
|
625 GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL), (NULL)); |
|
626 return FALSE; |
|
627 } |
|
628 |
|
629 return TRUE; |
|
630 } |
|
631 |
|
632 static gboolean |
|
633 gst_wavparse_other (GstWavParse * wav) |
|
634 { |
|
635 guint32 tag, length; |
|
636 |
|
637 if (!gst_riff_peek_head (wav, &tag, &length, NULL)) { |
|
638 GST_WARNING_OBJECT (wav, "could not peek head"); |
|
639 return FALSE; |
|
640 } |
|
641 GST_DEBUG_OBJECT (wav, "got tag (%08x) %4.4s, length %d", tag, |
|
642 (gchar *) & tag, length); |
|
643 |
|
644 switch (tag) { |
|
645 case GST_RIFF_TAG_LIST: |
|
646 if (!(tag = gst_riff_peek_list (wav))) { |
|
647 GST_WARNING_OBJECT (wav, "could not peek list"); |
|
648 return FALSE; |
|
649 } |
|
650 |
|
651 switch (tag) { |
|
652 case GST_RIFF_LIST_INFO: |
|
653 if (!gst_riff_read_list (wav, &tag) || !gst_riff_read_info (wav)) { |
|
654 GST_WARNING_OBJECT (wav, "could not read list"); |
|
655 return FALSE; |
|
656 } |
|
657 break; |
|
658 |
|
659 case GST_RIFF_LIST_adtl: |
|
660 if (!gst_riff_read_skip (wav)) { |
|
661 GST_WARNING_OBJECT (wav, "could not read skip"); |
|
662 return FALSE; |
|
663 } |
|
664 break; |
|
665 |
|
666 default: |
|
667 GST_DEBUG_OBJECT (wav, "skipping tag (%08x) %4.4s", tag, |
|
668 (gchar *) & tag); |
|
669 if (!gst_riff_read_skip (wav)) { |
|
670 GST_WARNING_OBJECT (wav, "could not read skip"); |
|
671 return FALSE; |
|
672 } |
|
673 break; |
|
674 } |
|
675 |
|
676 break; |
|
677 |
|
678 case GST_RIFF_TAG_data: |
|
679 if (!gst_bytestream_flush (wav->bs, 8)) { |
|
680 GST_WARNING_OBJECT (wav, "could not flush 8 bytes"); |
|
681 return FALSE; |
|
682 } |
|
683 |
|
684 GST_DEBUG_OBJECT (wav, "switching to data mode"); |
|
685 wav->state = GST_WAVPARSE_DATA; |
|
686 wav->datastart = gst_bytestream_tell (wav->bs); |
|
687 if (length == 0) { |
|
688 guint64 file_length; |
|
689 |
|
690 /* length is 0, data probably stretches to the end |
|
691 * of file */ |
|
692 GST_DEBUG_OBJECT (wav, "length is 0 trying to find length"); |
|
693 /* get length of file */ |
|
694 file_length = gst_bytestream_length (wav->bs); |
|
695 if (file_length == -1) { |
|
696 GST_DEBUG_OBJECT (wav, |
|
697 "could not get file length, assuming data to eof"); |
|
698 /* could not get length, assuming till eof */ |
|
699 length = G_MAXUINT32; |
|
700 } |
|
701 if (file_length > G_MAXUINT32) { |
|
702 GST_DEBUG_OBJECT (wav, "file length %lld, clipping to 32 bits"); |
|
703 /* could not get length, assuming till eof */ |
|
704 length = G_MAXUINT32; |
|
705 } else { |
|
706 GST_DEBUG_OBJECT (wav, "file length %lld, datalength", |
|
707 file_length, length); |
|
708 /* substract offset of datastart from length */ |
|
709 length = file_length - wav->datastart; |
|
710 GST_DEBUG_OBJECT (wav, "datalength %lld", length); |
|
711 } |
|
712 } |
|
713 wav->datasize = (guint64) length; |
|
714 break; |
|
715 |
|
716 case GST_RIFF_TAG_cue: |
|
717 if (!gst_riff_read_skip (wav)) { |
|
718 GST_WARNING_OBJECT (wav, "could not read skip"); |
|
719 return FALSE; |
|
720 } |
|
721 break; |
|
722 |
|
723 default: |
|
724 GST_DEBUG_OBJECT (wav, "skipping tag (%08x) %4.4s", tag, (gchar *) & tag); |
|
725 if (!gst_riff_read_skip (wav)) |
|
726 return FALSE; |
|
727 break; |
|
728 } |
|
729 |
|
730 return TRUE; |
|
731 } |
|
732 #endif |
|
733 |
|
734 |
|
735 |
|
736 static gboolean |
|
737 gst_wavparse_parse_file_header (GstElement * element, GstBuffer * buf) |
|
738 { |
|
739 guint32 doctype; |
|
740 |
|
741 if (!gst_riff_parse_file_header (element, buf, &doctype)) |
|
742 return FALSE; |
|
743 |
|
744 if (doctype != GST_RIFF_RIFF_WAVE) |
|
745 goto not_wav; |
|
746 |
|
747 return TRUE; |
|
748 |
|
749 /* ERRORS */ |
|
750 not_wav: |
|
751 |
|
752 { |
|
753 GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL), |
|
754 ("File is not an WAVE file: %" GST_FOURCC_FORMAT, |
|
755 GST_FOURCC_ARGS (doctype))); |
|
756 return FALSE; |
|
757 } |
|
758 } |
|
759 |
|
760 static GstFlowReturn |
|
761 gst_wavparse_stream_init (GstWavParse * wav) |
|
762 { |
|
763 GstFlowReturn res; |
|
764 GstBuffer *buf = NULL; |
|
765 |
|
766 |
|
767 if ((res = gst_pad_pull_range (wav->sinkpad, |
|
768 wav->offset, 12, &buf)) != GST_FLOW_OK) |
|
769 { |
|
770 |
|
771 return res; |
|
772 } |
|
773 else if (!gst_wavparse_parse_file_header (GST_ELEMENT (wav), buf)) |
|
774 return GST_FLOW_ERROR; |
|
775 |
|
776 wav->offset += 12; |
|
777 |
|
778 return GST_FLOW_OK; |
|
779 } |
|
780 |
|
781 /* This function is used to perform seeks on the element in |
|
782 * pull mode. |
|
783 * |
|
784 * It also works when event is NULL, in which case it will just |
|
785 * start from the last configured segment. This technique is |
|
786 * used when activating the element and to perform the seek in |
|
787 * READY. |
|
788 */ |
|
789 static gboolean |
|
790 gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event) |
|
791 { |
|
792 gboolean res; |
|
793 gdouble rate; |
|
794 GstEvent *newsegment; |
|
795 GstFormat format; |
|
796 GstSeekFlags flags; |
|
797 GstSeekType cur_type, stop_type; |
|
798 gint64 cur, stop; |
|
799 gboolean flush; |
|
800 gboolean update; |
|
801 GstSegment seeksegment; |
|
802 |
|
803 if (event) { |
|
804 GST_DEBUG_OBJECT (wav, "doing seek with event"); |
|
805 |
|
806 gst_event_parse_seek (event, &rate, &format, &flags, |
|
807 &cur_type, &cur, &stop_type, &stop); |
|
808 |
|
809 /* we have to have a format as the segment format. Try to convert |
|
810 * if not. */ |
|
811 if (format != GST_FORMAT_TIME) { |
|
812 GstFormat fmt; |
|
813 |
|
814 fmt = GST_FORMAT_TIME; |
|
815 res = TRUE; |
|
816 if (cur_type != GST_SEEK_TYPE_NONE) |
|
817 res = gst_pad_query_convert (wav->srcpad, format, cur, &fmt, &cur); |
|
818 if (res && stop_type != GST_SEEK_TYPE_NONE) |
|
819 res = gst_pad_query_convert (wav->srcpad, format, stop, &fmt, &stop); |
|
820 if (!res) |
|
821 goto no_format; |
|
822 |
|
823 format = fmt; |
|
824 } |
|
825 } else { |
|
826 GST_DEBUG_OBJECT (wav, "doing seek without event"); |
|
827 flags = 0; |
|
828 } |
|
829 |
|
830 flush = flags & GST_SEEK_FLAG_FLUSH; |
|
831 |
|
832 if (flush && wav->srcpad) { |
|
833 GST_DEBUG_OBJECT (wav, "sending flush start"); |
|
834 gst_pad_push_event (wav->srcpad, gst_event_new_flush_start ()); |
|
835 } else { |
|
836 gst_pad_pause_task (wav->sinkpad); |
|
837 } |
|
838 |
|
839 GST_PAD_STREAM_LOCK (wav->sinkpad); |
|
840 |
|
841 /* copy segment, we need this because we still need the old |
|
842 * segment when we close the current segment. */ |
|
843 memcpy (&seeksegment, &wav->segment, sizeof (GstSegment)); |
|
844 |
|
845 if (event) { |
|
846 GST_DEBUG_OBJECT (wav, "configuring seek"); |
|
847 gst_segment_set_seek (&seeksegment, rate, format, flags, |
|
848 cur_type, cur, stop_type, stop, &update); |
|
849 } |
|
850 |
|
851 if ((stop = seeksegment.stop) == -1) |
|
852 stop = seeksegment.duration; |
|
853 |
|
854 if (cur_type != GST_SEEK_TYPE_NONE) { |
|
855 wav->offset = |
|
856 gst_util_uint64_scale_int (seeksegment.last_stop, wav->bps, GST_SECOND); |
|
857 wav->offset -= wav->offset % wav->bytes_per_sample; |
|
858 wav->offset += wav->datastart; |
|
859 } |
|
860 |
|
861 if (stop != -1) { |
|
862 wav->end_offset = gst_util_uint64_scale_int (stop, wav->bps, GST_SECOND); |
|
863 wav->end_offset += |
|
864 wav->bytes_per_sample - (wav->end_offset % wav->bytes_per_sample); |
|
865 wav->end_offset += wav->datastart; |
|
866 } else { |
|
867 wav->end_offset = wav->datasize + wav->datastart; |
|
868 } |
|
869 wav->offset = MIN (wav->offset, wav->end_offset); |
|
870 wav->dataleft = wav->end_offset - wav->offset; |
|
871 |
|
872 GST_DEBUG_OBJECT (wav, |
|
873 "seek: offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT ", segment %" |
|
874 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, wav->offset, wav->end_offset, |
|
875 GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop)); |
|
876 |
|
877 /* prepare for streaming again */ |
|
878 if (wav->srcpad) { |
|
879 if (flush) { |
|
880 GST_DEBUG_OBJECT (wav, "sending flush stop"); |
|
881 gst_pad_push_event (wav->srcpad, gst_event_new_flush_stop ()); |
|
882 } else if (wav->segment_running) { |
|
883 /* we are running the current segment and doing a non-flushing seek, |
|
884 * close the segment first based on the last_stop. */ |
|
885 GST_DEBUG_OBJECT (wav, "closing running segment %" G_GINT64_FORMAT |
|
886 " to %" G_GINT64_FORMAT, wav->segment.start, wav->segment.last_stop); |
|
887 |
|
888 gst_pad_push_event (wav->srcpad, |
|
889 gst_event_new_new_segment (TRUE, |
|
890 wav->segment.rate, wav->segment.format, |
|
891 wav->segment.start, wav->segment.last_stop, wav->segment.time)); |
|
892 } |
|
893 } |
|
894 |
|
895 memcpy (&wav->segment, &seeksegment, sizeof (GstSegment)); |
|
896 |
|
897 if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) { |
|
898 gst_element_post_message (GST_ELEMENT (wav), |
|
899 gst_message_new_segment_start (GST_OBJECT (wav), |
|
900 wav->segment.format, wav->segment.last_stop)); |
|
901 } |
|
902 |
|
903 /* now send the newsegment */ |
|
904 GST_DEBUG_OBJECT (wav, "Sending newsegment from %" G_GINT64_FORMAT |
|
905 " to %" G_GINT64_FORMAT, wav->segment.start, stop); |
|
906 |
|
907 newsegment = |
|
908 gst_event_new_new_segment (FALSE, wav->segment.rate, |
|
909 wav->segment.format, wav->segment.last_stop, stop, wav->segment.time); |
|
910 |
|
911 if (wav->srcpad) { |
|
912 gst_pad_push_event (wav->srcpad, newsegment); |
|
913 } else { |
|
914 /* send later when we actually create the source pad */ |
|
915 g_assert (wav->newsegment == NULL); |
|
916 wav->newsegment = newsegment; |
|
917 } |
|
918 |
|
919 wav->segment_running = TRUE; |
|
920 if (!wav->streaming) { |
|
921 gst_pad_start_task (wav->sinkpad, (GstTaskFunction) gst_wavparse_loop, |
|
922 wav->sinkpad); |
|
923 } |
|
924 |
|
925 GST_PAD_STREAM_UNLOCK (wav->sinkpad); |
|
926 |
|
927 return TRUE; |
|
928 |
|
929 /* ERRORS */ |
|
930 no_format: |
|
931 { |
|
932 GST_DEBUG_OBJECT (wav, "unsupported format given, seek aborted."); |
|
933 return FALSE; |
|
934 } |
|
935 } |
|
936 |
|
937 /* |
|
938 * gst_wavparse_peek_chunk_info: |
|
939 * @wav Wavparse object |
|
940 * @tag holder for tag |
|
941 * @size holder for tag size |
|
942 * |
|
943 * Peek next chunk info (tag and size) |
|
944 * |
|
945 * Returns: %TRUE when one chunk info has been got from the adapter |
|
946 */ |
|
947 static gboolean |
|
948 gst_wavparse_peek_chunk_info (GstWavParse * wav, guint32 * tag, guint32 * size) |
|
949 { |
|
950 const guint8 *data = NULL; |
|
951 |
|
952 if (gst_adapter_available (wav->adapter) < 8) { |
|
953 return FALSE; |
|
954 } |
|
955 |
|
956 GST_DEBUG ("Next chunk size is %d bytes", *size); |
|
957 data = gst_adapter_peek (wav->adapter, 8); |
|
958 *tag = GST_READ_UINT32_LE (data); |
|
959 *size = GST_READ_UINT32_LE (data + 4); |
|
960 |
|
961 return TRUE; |
|
962 } |
|
963 |
|
964 /* |
|
965 * gst_wavparse_peek_chunk: |
|
966 * @wav Wavparse object |
|
967 * @tag holder for tag |
|
968 * @size holder for tag size |
|
969 * |
|
970 * Peek enough data for one full chunk |
|
971 * |
|
972 * Returns: %TRUE when one chunk has been got |
|
973 */ |
|
974 static gboolean |
|
975 gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size) |
|
976 { |
|
977 guint32 peek_size = 0; |
|
978 |
|
979 gst_wavparse_peek_chunk_info (wav, tag, size); |
|
980 GST_DEBUG ("Need to peek chunk of %d bytes", *size); |
|
981 peek_size = (*size + 1) & ~1; |
|
982 |
|
983 if (gst_adapter_available (wav->adapter) >= (8 + peek_size)) { |
|
984 return TRUE; |
|
985 } else { |
|
986 return FALSE; |
|
987 } |
|
988 } |
|
989 |
|
990 static gboolean |
|
991 gst_wavparse_get_upstream_size (GstWavParse * wav, gint64 * len) |
|
992 { |
|
993 gboolean res = FALSE; |
|
994 GstFormat fmt = GST_FORMAT_BYTES; |
|
995 GstPad *peer; |
|
996 |
|
997 if ((peer = gst_pad_get_peer (wav->sinkpad))) { |
|
998 res = gst_pad_query_duration (peer, &fmt, len); |
|
999 gst_object_unref (peer); |
|
1000 } |
|
1001 |
|
1002 return res; |
|
1003 } |
|
1004 |
|
1005 static GstFlowReturn |
|
1006 gst_wavparse_stream_headers (GstWavParse * wav) |
|
1007 { |
|
1008 GstFlowReturn res; |
|
1009 GstBuffer *buf; |
|
1010 gst_riff_strf_auds *header = NULL; |
|
1011 guint32 tag, size; |
|
1012 gboolean gotdata = FALSE; |
|
1013 GstCaps *caps; |
|
1014 gint64 duration; |
|
1015 gchar *codec_name = NULL; |
|
1016 GstEvent **event_p; |
|
1017 |
|
1018 |
|
1019 if (!wav->got_fmt) { |
|
1020 GstBuffer *extra; |
|
1021 |
|
1022 /* The header start with a 'fmt ' tag */ |
|
1023 |
|
1024 if (wav->streaming) { |
|
1025 if (!gst_wavparse_peek_chunk (wav, &tag, &size)) |
|
1026 return GST_FLOW_OK; |
|
1027 |
|
1028 buf = gst_buffer_new (); |
|
1029 gst_buffer_ref (buf); |
|
1030 gst_adapter_flush (wav->adapter, 8); |
|
1031 wav->offset += 8; |
|
1032 GST_BUFFER_DATA (buf) = (guint8 *) gst_adapter_peek (wav->adapter, size); |
|
1033 GST_BUFFER_SIZE (buf) = size; |
|
1034 |
|
1035 } else { |
|
1036 if ((res = gst_riff_read_chunk (GST_ELEMENT (wav), wav->sinkpad, |
|
1037 &wav->offset, &tag, &buf)) != GST_FLOW_OK) |
|
1038 return res; |
|
1039 } |
|
1040 |
|
1041 if (tag != GST_RIFF_TAG_fmt) |
|
1042 goto invalid_wav; |
|
1043 |
|
1044 if (!(gst_riff_parse_strf_auds (GST_ELEMENT (wav), buf, &header, &extra))) |
|
1045 goto parse_header_error; |
|
1046 |
|
1047 if (wav->streaming) { |
|
1048 gst_adapter_flush (wav->adapter, size); |
|
1049 wav->offset += size; |
|
1050 GST_BUFFER_DATA (buf) = NULL; |
|
1051 gst_buffer_unref (buf); |
|
1052 } |
|
1053 |
|
1054 /* Note: gst_riff_create_audio_caps might nedd to fix values in |
|
1055 * the header header depending on the format, so call it first */ |
|
1056 caps = |
|
1057 gst_riff_create_audio_caps (header->format, NULL, header, extra, |
|
1058 NULL, &codec_name); |
|
1059 |
|
1060 if (extra) |
|
1061 gst_buffer_unref (extra); |
|
1062 |
|
1063 wav->format = header->format; |
|
1064 wav->rate = header->rate; |
|
1065 wav->channels = header->channels; |
|
1066 |
|
1067 if (wav->channels == 0) |
|
1068 goto no_channels; |
|
1069 |
|
1070 wav->blockalign = header->blockalign; |
|
1071 wav->width = (header->blockalign * 8) / header->channels; |
|
1072 wav->depth = header->size; |
|
1073 wav->bps = header->av_bps; |
|
1074 |
|
1075 if (wav->bps <= 0) |
|
1076 goto no_bitrate; |
|
1077 |
|
1078 wav->bytes_per_sample = wav->channels * wav->width / 8; |
|
1079 if (wav->bytes_per_sample <= 0) |
|
1080 goto no_bytes_per_sample; |
|
1081 |
|
1082 g_free (header); |
|
1083 |
|
1084 if (!caps) |
|
1085 goto unknown_format; |
|
1086 |
|
1087 GST_DEBUG_OBJECT (wav, "blockalign = %u", (guint) wav->blockalign); |
|
1088 GST_DEBUG_OBJECT (wav, "width = %u", (guint) wav->width); |
|
1089 GST_DEBUG_OBJECT (wav, "depth = %u", (guint) wav->depth); |
|
1090 GST_DEBUG_OBJECT (wav, "bps = %u", (guint) wav->bps); |
|
1091 |
|
1092 /* create pad later so we can sniff the first few bytes |
|
1093 * of the real data and correct our caps if necessary */ |
|
1094 gst_caps_replace (&wav->caps, caps); |
|
1095 gst_caps_replace (&caps, NULL); |
|
1096 |
|
1097 wav->got_fmt = TRUE; |
|
1098 |
|
1099 if (codec_name) { |
|
1100 wav->tags = gst_tag_list_new (); |
|
1101 |
|
1102 gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE, |
|
1103 GST_TAG_AUDIO_CODEC, codec_name, NULL); |
|
1104 |
|
1105 g_free (codec_name); |
|
1106 codec_name = NULL; |
|
1107 } |
|
1108 |
|
1109 GST_DEBUG_OBJECT (wav, "frequency %d, channels %d", wav->rate, |
|
1110 wav->channels); |
|
1111 } |
|
1112 |
|
1113 /* loop headers until we get data */ |
|
1114 while (!gotdata) { |
|
1115 if (wav->streaming) { |
|
1116 if (!gst_wavparse_peek_chunk_info (wav, &tag, &size)) |
|
1117 return GST_FLOW_OK; |
|
1118 } else { |
|
1119 if ((res = |
|
1120 gst_pad_pull_range (wav->sinkpad, wav->offset, 8, |
|
1121 &buf)) != GST_FLOW_OK) |
|
1122 goto header_read_error; |
|
1123 tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)); |
|
1124 size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4); |
|
1125 } |
|
1126 |
|
1127 /* |
|
1128 wav is a st00pid format, we don't know for sure where data starts. |
|
1129 So we have to go bit by bit until we find the 'data' header |
|
1130 */ |
|
1131 |
|
1132 switch (tag) { |
|
1133 /* TODO : Implement the various cases */ |
|
1134 case GST_RIFF_TAG_data:{ |
|
1135 gint64 upstream_size; |
|
1136 |
|
1137 GST_DEBUG_OBJECT (wav, "Got 'data' TAG, size : %d", size); |
|
1138 gotdata = TRUE; |
|
1139 if (wav->streaming) { |
|
1140 gst_adapter_flush (wav->adapter, 8); |
|
1141 } else { |
|
1142 gst_buffer_unref (buf); |
|
1143 } |
|
1144 wav->offset += 8; |
|
1145 wav->datastart = wav->offset; |
|
1146 /* file might be truncated */ |
|
1147 if (gst_wavparse_get_upstream_size (wav, &upstream_size)) { |
|
1148 size = MIN (size, (upstream_size - wav->datastart)); |
|
1149 } |
|
1150 wav->datasize = size; |
|
1151 wav->dataleft = size; |
|
1152 wav->end_offset = size + wav->datastart; |
|
1153 break; |
|
1154 } |
|
1155 default: |
|
1156 if (wav->streaming) { |
|
1157 if (!gst_wavparse_peek_chunk (wav, &tag, &size)) |
|
1158 return GST_FLOW_OK; |
|
1159 } |
|
1160 GST_DEBUG_OBJECT (wav, "Ignoring tag %" GST_FOURCC_FORMAT, |
|
1161 GST_FOURCC_ARGS (tag)); |
|
1162 wav->offset += 8 + ((size + 1) & ~1); |
|
1163 if (wav->streaming) { |
|
1164 gst_adapter_flush (wav->adapter, 8 + ((size + 1) & ~1)); |
|
1165 } else { |
|
1166 gst_buffer_unref (buf); |
|
1167 } |
|
1168 } |
|
1169 } |
|
1170 |
|
1171 GST_DEBUG_OBJECT (wav, "Finished parsing headers"); |
|
1172 |
|
1173 duration = gst_util_uint64_scale_int (wav->datasize, GST_SECOND, wav->bps); |
|
1174 GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT, |
|
1175 GST_TIME_ARGS (duration)); |
|
1176 gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, duration); |
|
1177 |
|
1178 /* now we have all the info to perform a pending seek if any, if no |
|
1179 * event, this will still do the right thing and it will also send |
|
1180 * the right newsegment event downstream. */ |
|
1181 gst_wavparse_perform_seek (wav, wav->seek_event); |
|
1182 /* remove pending event */ |
|
1183 event_p = &wav->seek_event; |
|
1184 gst_event_replace (event_p, NULL); |
|
1185 |
|
1186 wav->state = GST_WAVPARSE_DATA; |
|
1187 |
|
1188 return GST_FLOW_OK; |
|
1189 |
|
1190 /* ERROR */ |
|
1191 invalid_wav: |
|
1192 { |
|
1193 GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL), |
|
1194 ("Invalid WAV header (no fmt at start): %" |
|
1195 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag))); |
|
1196 g_free (codec_name); |
|
1197 |
|
1198 return GST_FLOW_ERROR; |
|
1199 } |
|
1200 parse_header_error: |
|
1201 { |
|
1202 GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL), |
|
1203 ("Couldn't parse audio header")); |
|
1204 gst_buffer_unref (buf); |
|
1205 g_free (codec_name); |
|
1206 |
|
1207 return GST_FLOW_ERROR; |
|
1208 } |
|
1209 no_channels: |
|
1210 { |
|
1211 GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), |
|
1212 ("Stream claims to contain no channels - invalid data")); |
|
1213 g_free (header); |
|
1214 g_free (codec_name); |
|
1215 return GST_FLOW_ERROR; |
|
1216 } |
|
1217 no_bitrate: |
|
1218 { |
|
1219 GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), |
|
1220 ("Stream claims to have a bitrate of <= zero - invalid data")); |
|
1221 g_free (header); |
|
1222 g_free (codec_name); |
|
1223 return GST_FLOW_ERROR; |
|
1224 } |
|
1225 no_bytes_per_sample: |
|
1226 { |
|
1227 GST_ELEMENT_ERROR (wav, STREAM, FAILED, (NULL), |
|
1228 ("could not caluclate bytes per sample - invalid data")); |
|
1229 g_free (header); |
|
1230 g_free (codec_name); |
|
1231 return GST_FLOW_ERROR; |
|
1232 } |
|
1233 unknown_format: |
|
1234 { |
|
1235 GST_ELEMENT_ERROR (wav, STREAM, TYPE_NOT_FOUND, (NULL), |
|
1236 ("No caps found for format 0x%x, %d channels, %d Hz", |
|
1237 wav->format, wav->channels, wav->rate)); |
|
1238 g_free (codec_name); |
|
1239 return GST_FLOW_ERROR; |
|
1240 } |
|
1241 header_read_error: |
|
1242 { |
|
1243 GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL), ("Couldn't read in header")); |
|
1244 g_free (codec_name); |
|
1245 return GST_FLOW_ERROR; |
|
1246 } |
|
1247 } |
|
1248 |
|
1249 |
|
1250 /* |
|
1251 * Read WAV file tag when streaming |
|
1252 */ |
|
1253 static GstFlowReturn |
|
1254 gst_wavparse_parse_stream_init (GstWavParse * wav) |
|
1255 { |
|
1256 if (gst_adapter_available (wav->adapter) >= 12) { |
|
1257 GstBuffer *tmp = gst_buffer_new (); |
|
1258 |
|
1259 /* _take flushes the data */ |
|
1260 GST_BUFFER_DATA (tmp) = gst_adapter_take (wav->adapter, 12); |
|
1261 GST_BUFFER_SIZE (tmp) = 12; |
|
1262 |
|
1263 GST_DEBUG ("Parsing wav header"); |
|
1264 if (!gst_wavparse_parse_file_header (GST_ELEMENT (wav), tmp)) { |
|
1265 return GST_FLOW_ERROR; |
|
1266 } |
|
1267 |
|
1268 wav->offset += 12; |
|
1269 /* Go to next state */ |
|
1270 wav->state = GST_WAVPARSE_HEADER; |
|
1271 } |
|
1272 return GST_FLOW_OK; |
|
1273 } |
|
1274 |
|
1275 /* handle an event sent directly to the element. |
|
1276 * |
|
1277 * This event can be sent either in the READY state or the |
|
1278 * >READY state. The only event of interest really is the seek |
|
1279 * event. |
|
1280 * |
|
1281 * In the READY state we can only store the event and try to |
|
1282 * respect it when going to PAUSED. We assume we are in the |
|
1283 * READY state when our parsing state != GST_WAVPARSE_DATA. |
|
1284 * |
|
1285 * When we are steaming, we can simply perform the seek right |
|
1286 * away. |
|
1287 */ |
|
1288 static gboolean |
|
1289 gst_wavparse_send_event (GstElement * element, GstEvent * event) |
|
1290 { |
|
1291 GstWavParse *wav = GST_WAVPARSE (element); |
|
1292 gboolean res = FALSE; |
|
1293 GstEvent **event_p; |
|
1294 |
|
1295 GST_DEBUG_OBJECT (wav, "received event %s", GST_EVENT_TYPE_NAME (event)); |
|
1296 |
|
1297 switch (GST_EVENT_TYPE (event)) { |
|
1298 case GST_EVENT_SEEK: |
|
1299 if (wav->state == GST_WAVPARSE_DATA) { |
|
1300 /* we can handle the seek directly when streaming data */ |
|
1301 res = gst_wavparse_perform_seek (wav, event); |
|
1302 } else { |
|
1303 GST_DEBUG_OBJECT (wav, "queuing seek for later"); |
|
1304 |
|
1305 event_p = &wav->seek_event; |
|
1306 gst_event_replace (event_p, event); |
|
1307 |
|
1308 /* we always return true */ |
|
1309 res = TRUE; |
|
1310 } |
|
1311 break; |
|
1312 default: |
|
1313 break; |
|
1314 } |
|
1315 gst_event_unref (event); |
|
1316 return res; |
|
1317 } |
|
1318 |
|
1319 static void |
|
1320 gst_wavparse_add_src_pad (GstWavParse * wav, GstBuffer * buf) |
|
1321 { |
|
1322 GstStructure *s; |
|
1323 const guint8 dts_marker[] = { 0xFF, 0x1F, 0x00, 0xE8, 0xF1, 0x07 }; |
|
1324 |
|
1325 |
|
1326 s = gst_caps_get_structure (wav->caps, 0); |
|
1327 if (gst_structure_has_name (s, "audio/x-raw-int") && |
|
1328 GST_BUFFER_SIZE (buf) > 6 && |
|
1329 memcmp (GST_BUFFER_DATA (buf), dts_marker, 6) == 0) { |
|
1330 |
|
1331 GST_WARNING_OBJECT (wav, "Found DTS marker in file marked as raw PCM"); |
|
1332 gst_caps_unref (wav->caps); |
|
1333 wav->caps = gst_caps_from_string ("audio/x-dts"); |
|
1334 |
|
1335 gst_tag_list_add (wav->tags, GST_TAG_MERGE_REPLACE, |
|
1336 GST_TAG_AUDIO_CODEC, "dts", NULL); |
|
1337 |
|
1338 } |
|
1339 |
|
1340 gst_wavparse_create_sourcepad (wav); |
|
1341 gst_pad_set_active (wav->srcpad, TRUE); |
|
1342 gst_pad_set_caps (wav->srcpad, wav->caps); |
|
1343 gst_caps_replace (&wav->caps, NULL); |
|
1344 |
|
1345 gst_element_add_pad (GST_ELEMENT (wav), wav->srcpad); |
|
1346 |
|
1347 |
|
1348 gst_element_no_more_pads (GST_ELEMENT (wav)); |
|
1349 |
|
1350 GST_DEBUG_OBJECT (wav, "Send newsegment event on newpad"); |
|
1351 gst_pad_push_event (wav->srcpad, wav->newsegment); |
|
1352 wav->newsegment = NULL; |
|
1353 |
|
1354 |
|
1355 if (wav->tags) { |
|
1356 gst_element_found_tags_for_pad (GST_ELEMENT (wav), wav->srcpad, wav->tags); |
|
1357 wav->tags = NULL; |
|
1358 } |
|
1359 |
|
1360 } |
|
1361 |
|
1362 #define MAX_BUFFER_SIZE 4096 |
|
1363 |
|
1364 static GstFlowReturn |
|
1365 gst_wavparse_stream_data (GstWavParse * wav) |
|
1366 { |
|
1367 GstBuffer *buf = NULL; |
|
1368 GstFlowReturn res = GST_FLOW_OK; |
|
1369 guint64 desired, obtained; |
|
1370 GstClockTime timestamp, next_timestamp; |
|
1371 guint64 pos, nextpos; |
|
1372 |
|
1373 |
|
1374 iterate_adapter: |
|
1375 GST_LOG_OBJECT (wav, |
|
1376 "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT " , dataleft: %" |
|
1377 G_GINT64_FORMAT, wav->offset, wav->end_offset, wav->dataleft); |
|
1378 |
|
1379 /* Get the next n bytes and output them */ |
|
1380 if (wav->dataleft == 0 || wav->dataleft < wav->blockalign) |
|
1381 goto found_eos; |
|
1382 |
|
1383 /* scale the amount of data by the segment rate so we get equal |
|
1384 * amounts of data regardless of the playback rate */ |
|
1385 desired = |
|
1386 MIN (gst_guint64_to_gdouble (wav->dataleft), |
|
1387 MAX_BUFFER_SIZE * ABS (wav->segment.rate)); |
|
1388 if (desired >= wav->blockalign && wav->blockalign > 0) |
|
1389 desired -= (desired % wav->blockalign); |
|
1390 |
|
1391 |
|
1392 GST_LOG_OBJECT (wav, "Fetching %" G_GINT64_FORMAT " bytes of data " |
|
1393 "from the sinkpad", desired); |
|
1394 |
|
1395 if (wav->streaming) { |
|
1396 guint avail = gst_adapter_available (wav->adapter); |
|
1397 |
|
1398 if (avail < desired) { |
|
1399 |
|
1400 GST_LOG_OBJECT (wav, "Got only %d bytes of data from the sinkpad", avail); |
|
1401 return GST_FLOW_OK; |
|
1402 } |
|
1403 |
|
1404 buf = gst_buffer_new (); |
|
1405 GST_BUFFER_DATA (buf) = gst_adapter_take (wav->adapter, desired); |
|
1406 GST_BUFFER_SIZE (buf) = desired; |
|
1407 |
|
1408 |
|
1409 } else { |
|
1410 if ((res = gst_pad_pull_range (wav->sinkpad, wav->offset, |
|
1411 desired, &buf)) != GST_FLOW_OK) |
|
1412 { |
|
1413 |
|
1414 goto pull_error; |
|
1415 } |
|
1416 |
|
1417 } |
|
1418 |
|
1419 /* first chunk of data? create the source pad. We do this only here so |
|
1420 * we can detect broken .wav files with dts disguised as raw PCM (sigh) */ |
|
1421 if (G_UNLIKELY (wav->first)) { |
|
1422 wav->first = FALSE; |
|
1423 |
|
1424 gst_wavparse_add_src_pad (wav, buf); |
|
1425 } |
|
1426 |
|
1427 obtained = GST_BUFFER_SIZE (buf); |
|
1428 |
|
1429 /* our positions */ |
|
1430 pos = wav->offset - wav->datastart; |
|
1431 nextpos = pos + obtained; |
|
1432 |
|
1433 /* update offsets, does not overflow. */ |
|
1434 GST_BUFFER_OFFSET (buf) = pos / wav->bytes_per_sample; |
|
1435 GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample; |
|
1436 |
|
1437 /* and timestamps, be carefull for overflows */ |
|
1438 timestamp = gst_util_uint64_scale_int (pos, GST_SECOND, wav->bps); |
|
1439 next_timestamp = gst_util_uint64_scale_int (nextpos, GST_SECOND, wav->bps); |
|
1440 |
|
1441 GST_BUFFER_TIMESTAMP (buf) = timestamp; |
|
1442 GST_BUFFER_DURATION (buf) = next_timestamp - timestamp; |
|
1443 |
|
1444 /* update current running segment position */ |
|
1445 gst_segment_set_last_stop (&wav->segment, GST_FORMAT_TIME, next_timestamp); |
|
1446 |
|
1447 /* don't forget to set the caps on the buffer */ |
|
1448 gst_buffer_set_caps (buf, GST_PAD_CAPS (wav->srcpad)); |
|
1449 |
|
1450 GST_LOG_OBJECT (wav, |
|
1451 "Got buffer. timestamp:%" GST_TIME_FORMAT " , duration:%" GST_TIME_FORMAT |
|
1452 ", size:%u", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), |
|
1453 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_SIZE (buf)); |
|
1454 |
|
1455 |
|
1456 if ((res = gst_pad_push (wav->srcpad, buf)) != GST_FLOW_OK) |
|
1457 { |
|
1458 |
|
1459 goto push_error; |
|
1460 } |
|
1461 |
|
1462 |
|
1463 if (obtained < wav->dataleft) { |
|
1464 wav->dataleft -= obtained; |
|
1465 } else { |
|
1466 wav->dataleft = 0; |
|
1467 } |
|
1468 wav->offset += obtained; |
|
1469 /* Iterate until need more data, so adapter size won't grow */ |
|
1470 if (wav->streaming) { |
|
1471 GST_LOG_OBJECT (wav, |
|
1472 "offset: %" G_GINT64_FORMAT " , end: %" G_GINT64_FORMAT, wav->offset, |
|
1473 wav->end_offset); |
|
1474 |
|
1475 goto iterate_adapter; |
|
1476 } |
|
1477 |
|
1478 return res; |
|
1479 |
|
1480 /* ERROR */ |
|
1481 found_eos: |
|
1482 { |
|
1483 |
|
1484 GST_DEBUG_OBJECT (wav, "found EOS"); |
|
1485 /* we completed the segment */ |
|
1486 wav->segment_running = FALSE; |
|
1487 if (wav->segment.flags & GST_SEEK_FLAG_SEGMENT) { |
|
1488 GstClockTime stop; |
|
1489 |
|
1490 if ((stop = wav->segment.stop) == -1) |
|
1491 stop = wav->segment.duration; |
|
1492 |
|
1493 gst_element_post_message (GST_ELEMENT (wav), |
|
1494 gst_message_new_segment_done (GST_OBJECT (wav), GST_FORMAT_TIME, |
|
1495 stop)); |
|
1496 |
|
1497 } else { |
|
1498 gst_pad_push_event (wav->srcpad, gst_event_new_eos ()); |
|
1499 |
|
1500 } |
|
1501 return GST_FLOW_WRONG_STATE; |
|
1502 } |
|
1503 pull_error: |
|
1504 { |
|
1505 |
|
1506 GST_DEBUG_OBJECT (wav, "Error getting %" G_GINT64_FORMAT " bytes from the " |
|
1507 "sinkpad (dataleft = %" G_GINT64_FORMAT ")", desired, wav->dataleft); |
|
1508 return res; |
|
1509 } |
|
1510 push_error: |
|
1511 { |
|
1512 |
|
1513 GST_DEBUG_OBJECT (wav, "Error pushing on srcpad"); |
|
1514 return res; |
|
1515 } |
|
1516 } |
|
1517 |
|
1518 static void |
|
1519 gst_wavparse_loop (GstPad * pad) |
|
1520 { |
|
1521 GstFlowReturn ret; |
|
1522 GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad)); |
|
1523 |
|
1524 GST_LOG_OBJECT (wav, "process data"); |
|
1525 |
|
1526 switch (wav->state) { |
|
1527 case GST_WAVPARSE_START: |
|
1528 GST_DEBUG_OBJECT (wav, "GST_WAVPARSE_START"); |
|
1529 if ((ret = gst_wavparse_stream_init (wav)) != GST_FLOW_OK) |
|
1530 goto pause; |
|
1531 |
|
1532 wav->state = GST_WAVPARSE_HEADER; |
|
1533 /* fall-through */ |
|
1534 |
|
1535 case GST_WAVPARSE_HEADER: |
|
1536 GST_DEBUG_OBJECT (wav, "GST_WAVPARSE_HEADER"); |
|
1537 if ((ret = gst_wavparse_stream_headers (wav)) != GST_FLOW_OK) |
|
1538 goto pause; |
|
1539 |
|
1540 wav->state = GST_WAVPARSE_DATA; |
|
1541 /* fall-through */ |
|
1542 |
|
1543 case GST_WAVPARSE_DATA: |
|
1544 if ((ret = gst_wavparse_stream_data (wav)) != GST_FLOW_OK) |
|
1545 { |
|
1546 |
|
1547 goto pause; |
|
1548 } |
|
1549 |
|
1550 break; |
|
1551 default: |
|
1552 g_assert_not_reached (); |
|
1553 } |
|
1554 |
|
1555 return; |
|
1556 |
|
1557 /* ERRORS */ |
|
1558 pause: |
|
1559 GST_LOG_OBJECT (wav, "pausing task %d", ret); |
|
1560 gst_pad_pause_task (wav->sinkpad); |
|
1561 if (GST_FLOW_IS_FATAL (ret)) { |
|
1562 |
|
1563 |
|
1564 /* for fatal errors we post an error message */ |
|
1565 GST_ELEMENT_ERROR (wav, STREAM, FAILED, |
|
1566 (_("Internal data stream error.")), |
|
1567 ("streaming stopped, reason %s", gst_flow_get_name (ret))); |
|
1568 if (wav->srcpad != NULL) |
|
1569 { |
|
1570 gst_pad_push_event (wav->srcpad, gst_event_new_eos ()); |
|
1571 } |
|
1572 } |
|
1573 |
|
1574 } |
|
1575 |
|
1576 static GstFlowReturn |
|
1577 gst_wavparse_chain (GstPad * pad, GstBuffer * buf) |
|
1578 { |
|
1579 GstFlowReturn ret; |
|
1580 GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad)); |
|
1581 GST_LOG_OBJECT (wav, "adapter_push %" G_GINT64_FORMAT " bytes", |
|
1582 GST_BUFFER_SIZE (buf)); |
|
1583 |
|
1584 gst_adapter_push (wav->adapter, buf); |
|
1585 switch (wav->state) { |
|
1586 case GST_WAVPARSE_START: |
|
1587 GST_DEBUG_OBJECT (wav, "GST_WAVPARSE_START"); |
|
1588 if ((ret = gst_wavparse_parse_stream_init (wav)) != GST_FLOW_OK) |
|
1589 { |
|
1590 |
|
1591 goto pause; |
|
1592 } |
|
1593 |
|
1594 wav->state = GST_WAVPARSE_HEADER; |
|
1595 /* fall-through */ |
|
1596 |
|
1597 case GST_WAVPARSE_HEADER: |
|
1598 GST_DEBUG_OBJECT (wav, "GST_WAVPARSE_HEADER"); |
|
1599 if ((ret = gst_wavparse_stream_headers (wav)) != GST_FLOW_OK) |
|
1600 goto pause; |
|
1601 |
|
1602 wav->state = GST_WAVPARSE_DATA; |
|
1603 /* fall-through */ |
|
1604 |
|
1605 case GST_WAVPARSE_DATA: |
|
1606 if ((ret = gst_wavparse_stream_data (wav)) != GST_FLOW_OK) |
|
1607 { |
|
1608 |
|
1609 goto pause; |
|
1610 } |
|
1611 |
|
1612 break; |
|
1613 |
|
1614 default: |
|
1615 g_assert_not_reached (); |
|
1616 } |
|
1617 |
|
1618 return ret; |
|
1619 |
|
1620 pause: |
|
1621 |
|
1622 GST_LOG_OBJECT (wav, "pausing task %d", ret); |
|
1623 gst_pad_pause_task (wav->sinkpad); |
|
1624 if (GST_FLOW_IS_FATAL (ret)) { |
|
1625 /* for fatal errors we post an error message */ |
|
1626 |
|
1627 GST_ELEMENT_ERROR (wav, STREAM, FAILED, |
|
1628 (_("Internal data stream error.")), |
|
1629 ("streaming stopped, reason %s", gst_flow_get_name (ret))); |
|
1630 if (wav->srcpad != NULL) |
|
1631 { |
|
1632 gst_pad_push_event (wav->srcpad, gst_event_new_eos ()); |
|
1633 |
|
1634 } |
|
1635 } |
|
1636 |
|
1637 return ret; |
|
1638 } |
|
1639 |
|
1640 #if 0 |
|
1641 /* convert and query stuff */ |
|
1642 static const GstFormat * |
|
1643 gst_wavparse_get_formats (GstPad * pad) |
|
1644 { |
|
1645 static GstFormat formats[] = { |
|
1646 GST_FORMAT_TIME, |
|
1647 GST_FORMAT_BYTES, |
|
1648 GST_FORMAT_DEFAULT, /* a "frame", ie a set of samples per Hz */ |
|
1649 0 |
|
1650 }; |
|
1651 |
|
1652 return formats; |
|
1653 } |
|
1654 #endif |
|
1655 |
|
1656 static gboolean |
|
1657 gst_wavparse_pad_convert (GstPad * pad, |
|
1658 GstFormat src_format, gint64 src_value, |
|
1659 GstFormat * dest_format, gint64 * dest_value) |
|
1660 { |
|
1661 GstWavParse *wavparse; |
|
1662 gboolean res = TRUE; |
|
1663 |
|
1664 wavparse = GST_WAVPARSE (gst_pad_get_parent (pad)); |
|
1665 |
|
1666 if (wavparse->bytes_per_sample == 0) |
|
1667 goto no_bytes_per_sample; |
|
1668 |
|
1669 if (wavparse->bps == 0) |
|
1670 goto no_bps; |
|
1671 |
|
1672 switch (src_format) { |
|
1673 case GST_FORMAT_BYTES: |
|
1674 switch (*dest_format) { |
|
1675 case GST_FORMAT_DEFAULT: |
|
1676 *dest_value = src_value / wavparse->bytes_per_sample; |
|
1677 break; |
|
1678 case GST_FORMAT_TIME: |
|
1679 *dest_value = |
|
1680 gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->bps); |
|
1681 break; |
|
1682 default: |
|
1683 res = FALSE; |
|
1684 goto done; |
|
1685 } |
|
1686 *dest_value -= *dest_value % wavparse->bytes_per_sample; |
|
1687 break; |
|
1688 |
|
1689 case GST_FORMAT_DEFAULT: |
|
1690 switch (*dest_format) { |
|
1691 case GST_FORMAT_BYTES: |
|
1692 *dest_value = src_value * wavparse->bytes_per_sample; |
|
1693 break; |
|
1694 case GST_FORMAT_TIME: |
|
1695 *dest_value = |
|
1696 gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->rate); |
|
1697 break; |
|
1698 default: |
|
1699 res = FALSE; |
|
1700 goto done; |
|
1701 } |
|
1702 break; |
|
1703 |
|
1704 case GST_FORMAT_TIME: |
|
1705 switch (*dest_format) { |
|
1706 case GST_FORMAT_BYTES: |
|
1707 /* make sure we end up on a sample boundary */ |
|
1708 *dest_value = |
|
1709 gst_util_uint64_scale_int (src_value, wavparse->bps, GST_SECOND); |
|
1710 *dest_value -= *dest_value % wavparse->blockalign; |
|
1711 break; |
|
1712 case GST_FORMAT_DEFAULT: |
|
1713 *dest_value = |
|
1714 gst_util_uint64_scale_int (src_value, wavparse->rate, GST_SECOND); |
|
1715 break; |
|
1716 default: |
|
1717 res = FALSE; |
|
1718 goto done; |
|
1719 } |
|
1720 break; |
|
1721 |
|
1722 default: |
|
1723 res = FALSE; |
|
1724 goto done; |
|
1725 } |
|
1726 |
|
1727 done: |
|
1728 gst_object_unref (wavparse); |
|
1729 |
|
1730 return res; |
|
1731 |
|
1732 /* ERRORS */ |
|
1733 no_bytes_per_sample: |
|
1734 { |
|
1735 GST_DEBUG_OBJECT (wavparse, |
|
1736 "bytes_per_sample 0, probably an mp3 - channels %d, width %d", |
|
1737 wavparse->channels, wavparse->width); |
|
1738 res = FALSE; |
|
1739 goto done; |
|
1740 } |
|
1741 no_bps: |
|
1742 { |
|
1743 GST_DEBUG_OBJECT (wavparse, "bps 0, cannot convert"); |
|
1744 res = FALSE; |
|
1745 goto done; |
|
1746 } |
|
1747 } |
|
1748 |
|
1749 static const GstQueryType * |
|
1750 gst_wavparse_get_query_types (GstPad * pad) |
|
1751 { |
|
1752 static const GstQueryType types[] = { |
|
1753 GST_QUERY_POSITION, |
|
1754 GST_QUERY_DURATION, |
|
1755 GST_QUERY_CONVERT, |
|
1756 0 |
|
1757 }; |
|
1758 |
|
1759 return types; |
|
1760 } |
|
1761 |
|
1762 /* handle queries for location and length in requested format */ |
|
1763 static gboolean |
|
1764 gst_wavparse_pad_query (GstPad * pad, GstQuery * query) |
|
1765 { |
|
1766 gboolean res = TRUE; |
|
1767 GstWavParse *wav = GST_WAVPARSE (GST_PAD_PARENT (pad)); |
|
1768 |
|
1769 /* only if we know */ |
|
1770 if (wav->state != GST_WAVPARSE_DATA) |
|
1771 return FALSE; |
|
1772 |
|
1773 switch (GST_QUERY_TYPE (query)) { |
|
1774 case GST_QUERY_POSITION: |
|
1775 { |
|
1776 gint64 curb; |
|
1777 gint64 cur; |
|
1778 GstFormat format; |
|
1779 gboolean res = TRUE; |
|
1780 |
|
1781 curb = wav->offset - wav->datastart; |
|
1782 gst_query_parse_position (query, &format, NULL); |
|
1783 |
|
1784 switch (format) { |
|
1785 case GST_FORMAT_TIME: |
|
1786 res &= |
|
1787 gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb, |
|
1788 &format, &cur); |
|
1789 break; |
|
1790 default: |
|
1791 format = GST_FORMAT_BYTES; |
|
1792 cur = curb; |
|
1793 break; |
|
1794 } |
|
1795 if (res) |
|
1796 gst_query_set_position (query, format, cur); |
|
1797 break; |
|
1798 } |
|
1799 case GST_QUERY_DURATION: |
|
1800 { |
|
1801 gint64 endb; |
|
1802 gint64 end; |
|
1803 GstFormat format; |
|
1804 gboolean res = TRUE; |
|
1805 |
|
1806 endb = wav->datasize; |
|
1807 gst_query_parse_duration (query, &format, NULL); |
|
1808 |
|
1809 switch (format) { |
|
1810 case GST_FORMAT_TIME: |
|
1811 res &= |
|
1812 gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb, |
|
1813 &format, &end); |
|
1814 break; |
|
1815 default: |
|
1816 format = GST_FORMAT_BYTES; |
|
1817 end = endb; |
|
1818 break; |
|
1819 } |
|
1820 if (res) |
|
1821 gst_query_set_duration (query, format, end); |
|
1822 break; |
|
1823 } |
|
1824 case GST_QUERY_CONVERT: |
|
1825 { |
|
1826 gint64 srcvalue, dstvalue; |
|
1827 GstFormat srcformat, dstformat; |
|
1828 |
|
1829 gst_query_parse_convert (query, &srcformat, &srcvalue, |
|
1830 &dstformat, &dstvalue); |
|
1831 res &= |
|
1832 gst_wavparse_pad_convert (pad, srcformat, srcvalue, |
|
1833 &dstformat, &dstvalue); |
|
1834 if (res) |
|
1835 gst_query_set_convert (query, srcformat, srcvalue, dstformat, dstvalue); |
|
1836 break; |
|
1837 } |
|
1838 default: |
|
1839 res = gst_pad_query_default (pad, query); |
|
1840 break; |
|
1841 } |
|
1842 return res; |
|
1843 } |
|
1844 |
|
1845 static gboolean |
|
1846 gst_wavparse_srcpad_event (GstPad * pad, GstEvent * event) |
|
1847 { |
|
1848 GstWavParse *wavparse = GST_WAVPARSE (GST_PAD_PARENT (pad)); |
|
1849 gboolean res = TRUE; |
|
1850 |
|
1851 GST_DEBUG_OBJECT (wavparse, "event %d, %s", GST_EVENT_TYPE (event), |
|
1852 GST_EVENT_TYPE_NAME (event)); |
|
1853 |
|
1854 /* can only handle events when we are in the data state */ |
|
1855 if (wavparse->state != GST_WAVPARSE_DATA) |
|
1856 return FALSE; |
|
1857 |
|
1858 switch (GST_EVENT_TYPE (event)) { |
|
1859 case GST_EVENT_SEEK: |
|
1860 { |
|
1861 res = gst_wavparse_perform_seek (wavparse, event); |
|
1862 break; |
|
1863 } |
|
1864 default: |
|
1865 res = FALSE; |
|
1866 break; |
|
1867 } |
|
1868 |
|
1869 gst_event_unref (event); |
|
1870 |
|
1871 return res; |
|
1872 } |
|
1873 |
|
1874 static gboolean |
|
1875 gst_wavparse_sink_activate (GstPad * sinkpad) |
|
1876 { |
|
1877 GstWavParse *wav = GST_WAVPARSE (gst_pad_get_parent (sinkpad)); |
|
1878 gboolean res; |
|
1879 |
|
1880 if (gst_pad_check_pull_range (sinkpad)) { |
|
1881 GST_DEBUG ("going to pull mode"); |
|
1882 wav->streaming = FALSE; |
|
1883 wav->adapter = NULL; |
|
1884 res = gst_pad_activate_pull (sinkpad, TRUE); |
|
1885 } else { |
|
1886 GST_DEBUG ("going to push (streaming) mode"); |
|
1887 wav->streaming = TRUE; |
|
1888 wav->adapter = gst_adapter_new (); |
|
1889 res = gst_pad_activate_push (sinkpad, TRUE); |
|
1890 } |
|
1891 gst_object_unref (wav); |
|
1892 return res; |
|
1893 } |
|
1894 |
|
1895 |
|
1896 static gboolean |
|
1897 gst_wavparse_sink_activate_pull (GstPad * sinkpad, gboolean active) |
|
1898 { |
|
1899 GstWavParse *wav = GST_WAVPARSE (gst_pad_get_parent (sinkpad)); |
|
1900 |
|
1901 GST_DEBUG_OBJECT (wav, "activating pull"); |
|
1902 |
|
1903 if (active) { |
|
1904 /* if we have a scheduler we can start the task */ |
|
1905 wav->segment_running = TRUE; |
|
1906 gst_pad_start_task (sinkpad, (GstTaskFunction) gst_wavparse_loop, sinkpad); |
|
1907 } else { |
|
1908 gst_pad_stop_task (sinkpad); |
|
1909 } |
|
1910 gst_object_unref (wav); |
|
1911 |
|
1912 return TRUE; |
|
1913 } |
|
1914 |
|
1915 static GstStateChangeReturn |
|
1916 gst_wavparse_change_state (GstElement * element, GstStateChange transition) |
|
1917 { |
|
1918 GstStateChangeReturn ret; |
|
1919 GstWavParse *wav = GST_WAVPARSE (element); |
|
1920 |
|
1921 GST_DEBUG_OBJECT (wav, "changing state %s - %s", |
|
1922 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), |
|
1923 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); |
|
1924 |
|
1925 switch (transition) { |
|
1926 case GST_STATE_CHANGE_NULL_TO_READY: |
|
1927 break; |
|
1928 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
1929 gst_wavparse_reset (wav); |
|
1930 break; |
|
1931 case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
|
1932 break; |
|
1933 default: |
|
1934 break; |
|
1935 } |
|
1936 |
|
1937 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
1938 |
|
1939 switch (transition) { |
|
1940 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
|
1941 break; |
|
1942 case GST_STATE_CHANGE_PAUSED_TO_READY:{ |
|
1943 GstEvent **event_p = &wav->seek_event; |
|
1944 |
|
1945 gst_wavparse_destroy_sourcepad (wav); |
|
1946 gst_event_replace (event_p, NULL); |
|
1947 gst_wavparse_reset (wav); |
|
1948 if (wav->adapter) { |
|
1949 gst_adapter_clear (wav->adapter); |
|
1950 } |
|
1951 break; |
|
1952 } |
|
1953 case GST_STATE_CHANGE_READY_TO_NULL: |
|
1954 break; |
|
1955 default: |
|
1956 break; |
|
1957 } |
|
1958 return ret; |
|
1959 } |
|
1960 |
|
1961 static gboolean |
|
1962 plugin_init (GstPlugin * plugin) |
|
1963 { |
|
1964 gst_riff_init (); |
|
1965 |
|
1966 GST_DEBUG_CATEGORY_INIT (wavparse_debug, "wavparse", 0, "WAV parser"); |
|
1967 |
|
1968 return gst_element_register (plugin, "wavparse", GST_RANK_PRIMARY, |
|
1969 GST_TYPE_WAVPARSE); |
|
1970 } |
|
1971 |
|
1972 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
|
1973 GST_VERSION_MINOR, |
|
1974 "wavparse", |
|
1975 "Parse a .wav file into raw audio", |
|
1976 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |
|
1977 |
|
1978 |
|
1979 EXPORT_C GstPluginDesc* _GST_PLUGIN_DESC() |
|
1980 { |
|
1981 return &gst_plugin_desc; |
|
1982 } |
|
1983 |