Branch data Line data Source code
1 : : /* GStreamer wavpack plugin
2 : : * Copyright (c) 2005 Arwed v. Merkatz <v.merkatz@gmx.net>
3 : : * Copyright (c) 2006 Tim-Philipp Müller <tim centricular net>
4 : : * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.org>
5 : : *
6 : : * gstwavpackparse.c: wavpack file parser
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Library General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Library General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Library General Public
19 : : * License along with this library; if not, write to the
20 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 : : * Boston, MA 02111-1307, USA.
22 : : */
23 : :
24 : : /**
25 : : * SECTION:element-wavpackparse
26 : : *
27 : : * WavpackParse takes raw, unframed Wavpack streams and splits them into
28 : : * single Wavpack chunks with information like bit depth and the position
29 : : * in the stream.
30 : : * <ulink url="http://www.wavpack.com/">Wavpack</ulink> is an open-source
31 : : * audio codec that features both lossless and lossy encoding.
32 : : *
33 : : * <refsect2>
34 : : * <title>Example launch line</title>
35 : : * |[
36 : : * gst-launch filesrc location=test.wv ! wavpackparse ! wavpackdec ! fakesink
37 : : * ]| This pipeline decodes the Wavpack file test.wv into raw audio buffers.
38 : : * </refsect2>
39 : : */
40 : :
41 : : #ifdef HAVE_CONFIG_H
42 : : #include "config.h"
43 : : #endif
44 : : #include <gst/gst.h>
45 : : #include <gst/gst-i18n-plugin.h>
46 : :
47 : : #include <math.h>
48 : : #include <string.h>
49 : :
50 : : #include <wavpack/wavpack.h>
51 : : #include "gstwavpackparse.h"
52 : : #include "gstwavpackstreamreader.h"
53 : : #include "gstwavpackcommon.h"
54 : :
55 : : GST_DEBUG_CATEGORY_STATIC (gst_wavpack_parse_debug);
56 : : #define GST_CAT_DEFAULT gst_wavpack_parse_debug
57 : :
58 : : static inline GstWavpackParseIndexEntry *
59 : 0 : gst_wavpack_parse_index_entry_new (void)
60 : : {
61 : 0 : return g_slice_new (GstWavpackParseIndexEntry);
62 : : }
63 : :
64 : : static inline void
65 : 0 : gst_wavpack_parse_index_entry_free (GstWavpackParseIndexEntry * entry)
66 : : {
67 : 0 : g_slice_free (GstWavpackParseIndexEntry, entry);
68 : 0 : }
69 : :
70 : : static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
71 : : GST_PAD_SINK,
72 : : GST_PAD_ALWAYS,
73 : : GST_STATIC_CAPS ("audio/x-wavpack, "
74 : : "framed = (boolean) false; "
75 : : "audio/x-wavpack-correction, " "framed = (boolean) false")
76 : : );
77 : :
78 : : static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
79 : : GST_PAD_SRC,
80 : : GST_PAD_SOMETIMES,
81 : : GST_STATIC_CAPS ("audio/x-wavpack, "
82 : : "width = (int) [ 1, 32 ], "
83 : : "channels = (int) [ 1, 8 ], "
84 : : "rate = (int) [ 6000, 192000 ], " "framed = (boolean) true")
85 : : );
86 : :
87 : : static GstStaticPadTemplate wvc_src_factory = GST_STATIC_PAD_TEMPLATE ("wvcsrc",
88 : : GST_PAD_SRC,
89 : : GST_PAD_SOMETIMES,
90 : : GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) true")
91 : : );
92 : :
93 : : static gboolean gst_wavpack_parse_sink_activate (GstPad * sinkpad);
94 : :
95 : : static gboolean
96 : : gst_wavpack_parse_sink_activate_pull (GstPad * sinkpad, gboolean active);
97 : :
98 : : static void gst_wavpack_parse_loop (GstElement * element);
99 : :
100 : : static GstStateChangeReturn gst_wavpack_parse_change_state (GstElement *
101 : : element, GstStateChange transition);
102 : : static void gst_wavpack_parse_reset (GstWavpackParse * parse);
103 : :
104 : : static gint64 gst_wavpack_parse_get_upstream_length (GstWavpackParse * wvparse);
105 : :
106 : : static GstBuffer *gst_wavpack_parse_pull_buffer (GstWavpackParse * wvparse,
107 : : gint64 offset, guint size, GstFlowReturn * flow);
108 : : static GstFlowReturn gst_wavpack_parse_chain (GstPad * pad, GstBuffer * buf);
109 : :
110 [ + + ]: 72 : GST_BOILERPLATE (GstWavpackParse, gst_wavpack_parse, GstElement,
111 : 72 : GST_TYPE_ELEMENT);
112 : :
113 : : static void
114 : 6 : gst_wavpack_parse_base_init (gpointer klass)
115 : : {
116 : 6 : GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
117 : :
118 : 6 : gst_element_class_add_pad_template (element_class,
119 : : gst_static_pad_template_get (&src_factory));
120 : 6 : gst_element_class_add_pad_template (element_class,
121 : : gst_static_pad_template_get (&wvc_src_factory));
122 : 6 : gst_element_class_add_pad_template (element_class,
123 : : gst_static_pad_template_get (&sink_factory));
124 : :
125 : 6 : gst_element_class_set_details_simple (element_class, "Wavpack parser",
126 : : "Codec/Demuxer/Audio",
127 : : "Parses Wavpack files",
128 : : "Arwed v. Merkatz <v.merkatz@gmx.net>, "
129 : : "Sebastian Dröge <slomo@circular-chaos.org>");
130 : 6 : }
131 : :
132 : : static void
133 : 5 : gst_wavpack_parse_finalize (GObject * object)
134 : : {
135 : 5 : gst_wavpack_parse_reset (GST_WAVPACK_PARSE (object));
136 : :
137 : 5 : G_OBJECT_CLASS (parent_class)->finalize (object);
138 : 5 : }
139 : :
140 : : static void
141 : 6 : gst_wavpack_parse_class_init (GstWavpackParseClass * klass)
142 : : {
143 : : GObjectClass *gobject_class;
144 : :
145 : : GstElementClass *gstelement_class;
146 : :
147 : 6 : gobject_class = (GObjectClass *) klass;
148 : 6 : gstelement_class = (GstElementClass *) klass;
149 : :
150 : 6 : gobject_class->finalize = gst_wavpack_parse_finalize;
151 : 6 : gstelement_class->change_state =
152 : 6 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_change_state);
153 : 6 : }
154 : :
155 : : static GstWavpackParseIndexEntry *
156 : 0 : gst_wavpack_parse_index_get_last_entry (GstWavpackParse * wvparse)
157 : : {
158 [ # # ]: 0 : g_assert (wvparse->entries != NULL);
159 : :
160 : 0 : return wvparse->entries->data;
161 : : }
162 : :
163 : : static GstWavpackParseIndexEntry *
164 : 0 : gst_wavpack_parse_index_get_entry_from_sample (GstWavpackParse * wvparse,
165 : : gint64 sample_offset)
166 : : {
167 : : gint i;
168 : :
169 : : GSList *node;
170 : :
171 [ # # ]: 0 : if (wvparse->entries == NULL)
172 : 0 : return NULL;
173 : :
174 [ # # ]: 0 : for (node = wvparse->entries, i = 0; node; node = node->next, i++) {
175 : : GstWavpackParseIndexEntry *entry;
176 : :
177 : 0 : entry = node->data;
178 : :
179 [ # # ]: 0 : GST_LOG_OBJECT (wvparse, "Index entry %03u: sample %" G_GINT64_FORMAT " @"
180 : : " byte %" G_GINT64_FORMAT, i, entry->sample_offset, entry->byte_offset);
181 : :
182 [ # # ][ # # ]: 0 : if (entry->sample_offset <= sample_offset &&
183 : 0 : sample_offset < entry->sample_offset_end) {
184 [ # # ]: 0 : GST_LOG_OBJECT (wvparse, "found match");
185 : 0 : return entry;
186 : : }
187 : :
188 : : /* as the list is sorted and we first look at the latest entry
189 : : * we can abort searching for an entry if the sample we want is
190 : : * after the latest one */
191 [ # # ]: 0 : if (sample_offset >= entry->sample_offset_end)
192 : 0 : break;
193 : : }
194 [ # # ]: 0 : GST_LOG_OBJECT (wvparse, "no match in index");
195 : 0 : return NULL;
196 : : }
197 : :
198 : : static void
199 : 0 : gst_wavpack_parse_index_append_entry (GstWavpackParse * wvparse,
200 : : gint64 byte_offset, gint64 sample_offset, gint64 num_samples)
201 : : {
202 : : GstWavpackParseIndexEntry *entry;
203 : :
204 : : /* do we have this one already? */
205 [ # # ]: 0 : if (wvparse->entries) {
206 : 0 : entry = gst_wavpack_parse_index_get_last_entry (wvparse);
207 [ # # ]: 0 : if (entry->byte_offset >= byte_offset
208 [ # # ]: 0 : || entry->sample_offset >= sample_offset)
209 : 0 : return;
210 : : }
211 : :
212 [ # # ][ # # ]: 0 : GST_LOG_OBJECT (wvparse, "Adding index entry %8" G_GINT64_FORMAT " - %"
[ # # ][ # # ]
[ # # ]
213 : : GST_TIME_FORMAT " @ offset 0x%08" G_GINT64_MODIFIER "x", sample_offset,
214 : : GST_TIME_ARGS (gst_util_uint64_scale_int (sample_offset,
215 : : GST_SECOND, wvparse->samplerate)), byte_offset);
216 : :
217 : 0 : entry = gst_wavpack_parse_index_entry_new ();
218 : 0 : entry->byte_offset = byte_offset;
219 : 0 : entry->sample_offset = sample_offset;
220 : 0 : entry->sample_offset_end = sample_offset + num_samples;
221 : 0 : wvparse->entries = g_slist_prepend (wvparse->entries, entry);
222 : : }
223 : :
224 : : static void
225 : 19 : gst_wavpack_parse_reset (GstWavpackParse * parse)
226 : : {
227 : 19 : parse->total_samples = G_GINT64_CONSTANT (-1);
228 : 19 : parse->samplerate = 0;
229 : 19 : parse->channels = 0;
230 : :
231 : 19 : gst_segment_init (&parse->segment, GST_FORMAT_UNDEFINED);
232 : 19 : parse->next_block_index = 0;
233 : :
234 : 19 : parse->current_offset = 0;
235 : 19 : parse->need_newsegment = TRUE;
236 : 19 : parse->discont = TRUE;
237 : 19 : parse->upstream_length = -1;
238 : :
239 [ - + ]: 19 : if (parse->entries) {
240 : 0 : g_slist_foreach (parse->entries, (GFunc) gst_wavpack_parse_index_entry_free,
241 : : NULL);
242 : 0 : g_slist_free (parse->entries);
243 : 0 : parse->entries = NULL;
244 : : }
245 : :
246 [ + + ]: 19 : if (parse->adapter) {
247 : 2 : gst_adapter_clear (parse->adapter);
248 : 2 : g_object_unref (parse->adapter);
249 : 2 : parse->adapter = NULL;
250 : : }
251 : :
252 [ + + ]: 19 : if (parse->srcpad != NULL) {
253 : : gboolean res;
254 : :
255 [ - + ]: 2 : GST_DEBUG_OBJECT (parse, "Removing src pad");
256 : 2 : res = gst_element_remove_pad (GST_ELEMENT (parse), parse->srcpad);
257 [ - + ]: 21 : g_return_if_fail (res != FALSE);
258 : 2 : gst_object_unref (parse->srcpad);
259 : 2 : parse->srcpad = NULL;
260 : : }
261 : :
262 : 19 : g_list_foreach (parse->queued_events, (GFunc) gst_mini_object_unref, NULL);
263 : 19 : g_list_free (parse->queued_events);
264 : 19 : parse->queued_events = NULL;
265 : :
266 [ - + ]: 19 : if (parse->pending_buffer)
267 : 0 : gst_buffer_unref (parse->pending_buffer);
268 : :
269 : 19 : parse->pending_buffer = NULL;
270 : : }
271 : :
272 : : static const GstQueryType *
273 : 0 : gst_wavpack_parse_get_src_query_types (GstPad * pad)
274 : : {
275 : : static const GstQueryType types[] = {
276 : : GST_QUERY_POSITION,
277 : : GST_QUERY_DURATION,
278 : : GST_QUERY_SEEKING,
279 : : 0
280 : : };
281 : :
282 : 0 : return types;
283 : : }
284 : :
285 : : static gboolean
286 : 2 : gst_wavpack_parse_src_query (GstPad * pad, GstQuery * query)
287 : : {
288 : 2 : GstWavpackParse *parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
289 : :
290 : : GstFormat format;
291 : :
292 : 2 : gboolean ret = FALSE;
293 : :
294 [ + + - - ]: 2 : switch (GST_QUERY_TYPE (query)) {
295 : : case GST_QUERY_POSITION:{
296 : : gint64 cur;
297 : :
298 : : guint rate;
299 : :
300 : 1 : GST_OBJECT_LOCK (parse);
301 : 1 : cur = parse->segment.last_stop;
302 : 1 : rate = parse->samplerate;
303 : 1 : GST_OBJECT_UNLOCK (parse);
304 : :
305 [ - + ]: 1 : if (rate == 0) {
306 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "haven't read header yet");
307 : 0 : break;
308 : : }
309 : :
310 : 1 : gst_query_parse_position (query, &format, NULL);
311 : :
312 [ - + - ]: 1 : switch (format) {
313 : : case GST_FORMAT_TIME:
314 : 0 : cur = gst_util_uint64_scale_int (cur, GST_SECOND, rate);
315 : 0 : gst_query_set_position (query, GST_FORMAT_TIME, cur);
316 : 0 : ret = TRUE;
317 : 0 : break;
318 : : case GST_FORMAT_DEFAULT:
319 : 1 : gst_query_set_position (query, GST_FORMAT_DEFAULT, cur);
320 : 1 : ret = TRUE;
321 : 1 : break;
322 : : default:
323 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "cannot handle position query in "
324 : : "%s format. Forwarding upstream.", gst_format_get_name (format));
325 : 0 : ret = gst_pad_query_default (pad, query);
326 : 0 : break;
327 : : }
328 : 1 : break;
329 : : }
330 : : case GST_QUERY_DURATION:{
331 : : gint64 len;
332 : :
333 : : guint rate;
334 : :
335 : 1 : GST_OBJECT_LOCK (parse);
336 : 1 : rate = parse->samplerate;
337 : 1 : len = parse->total_samples;
338 : 1 : GST_OBJECT_UNLOCK (parse);
339 : :
340 [ - + ]: 1 : if (rate == 0) {
341 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "haven't read header yet");
342 : 0 : break;
343 : : }
344 : :
345 : 1 : gst_query_parse_duration (query, &format, NULL);
346 : :
347 [ - + - ]: 1 : switch (format) {
348 : : case GST_FORMAT_TIME:
349 [ # # ]: 0 : if (len != G_GINT64_CONSTANT (-1))
350 : 0 : len = gst_util_uint64_scale_int (len, GST_SECOND, rate);
351 : 0 : gst_query_set_duration (query, GST_FORMAT_TIME, len);
352 : 0 : ret = TRUE;
353 : 0 : break;
354 : : case GST_FORMAT_DEFAULT:
355 : 1 : gst_query_set_duration (query, GST_FORMAT_DEFAULT, len);
356 : 1 : ret = TRUE;
357 : 1 : break;
358 : : default:
359 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "cannot handle duration query in "
360 : : "%s format. Forwarding upstream.", gst_format_get_name (format));
361 : 0 : ret = gst_pad_query_default (pad, query);
362 : 0 : break;
363 : : }
364 : 1 : break;
365 : : }
366 : : case GST_QUERY_SEEKING:{
367 : 0 : gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
368 [ # # ][ # # ]: 0 : if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
369 : : gboolean seekable;
370 : :
371 : 0 : gint64 duration = -1;
372 : :
373 : : /* only fails if we didn't read the headers yet and can't say
374 : : * anything about our seeking capabilities */
375 [ # # ]: 0 : if (!gst_pad_query_duration (pad, &format, &duration))
376 : 0 : break;
377 : :
378 : : /* can't seek in streaming mode yet */
379 : 0 : GST_OBJECT_LOCK (parse);
380 : 0 : seekable = (parse->adapter == NULL);
381 : 0 : GST_OBJECT_UNLOCK (parse);
382 : :
383 : 0 : gst_query_set_seeking (query, format, seekable, 0, duration);
384 : 0 : ret = TRUE;
385 : : }
386 : 0 : break;
387 : : }
388 : : default:{
389 : 0 : ret = gst_pad_query_default (pad, query);
390 : 0 : break;
391 : : }
392 : : }
393 : :
394 : 2 : gst_object_unref (parse);
395 : 2 : return ret;
396 : :
397 : : }
398 : :
399 : : /* returns TRUE on success, with byte_offset set to the offset of the
400 : : * wavpack chunk containing the sample requested. start_sample will be
401 : : * set to the first sample in the chunk starting at byte_offset.
402 : : * Scanning from the last known header offset to the wanted position
403 : : * when seeking forward isn't very clever, but seems fast enough in
404 : : * practice and has the nice side effect of populating our index
405 : : * table */
406 : : static gboolean
407 : 0 : gst_wavpack_parse_scan_to_find_sample (GstWavpackParse * parse,
408 : : gint64 sample, gint64 * byte_offset, gint64 * start_sample)
409 : : {
410 : : GstWavpackParseIndexEntry *entry;
411 : :
412 : : GstFlowReturn ret;
413 : :
414 : 0 : gint64 off = 0;
415 : :
416 : : /* first, check if we have to scan at all */
417 : 0 : entry = gst_wavpack_parse_index_get_entry_from_sample (parse, sample);
418 [ # # ]: 0 : if (entry) {
419 : 0 : *byte_offset = entry->byte_offset;
420 : 0 : *start_sample = entry->sample_offset;
421 [ # # ]: 0 : GST_LOG_OBJECT (parse, "Found index entry: sample %" G_GINT64_FORMAT
422 : : " @ offset %" G_GINT64_FORMAT, entry->sample_offset,
423 : : entry->byte_offset);
424 : 0 : return TRUE;
425 : : }
426 : :
427 [ # # ]: 0 : GST_LOG_OBJECT (parse, "No matching entry in index, scanning file ...");
428 : :
429 : : /* if we have an index, we can start scanning from the last known offset
430 : : * in there, after all we know our wanted sample is not in the index */
431 [ # # ]: 0 : if (parse->entries) {
432 : : GstWavpackParseIndexEntry *entry;
433 : :
434 : 0 : entry = gst_wavpack_parse_index_get_last_entry (parse);
435 : 0 : off = entry->byte_offset;
436 : : }
437 : :
438 : : /* now scan forward until we find the chunk we're looking for or hit EOS */
439 : : do {
440 : : WavpackHeader header;
441 : :
442 : : GstBuffer *buf;
443 : :
444 : 0 : buf = gst_wavpack_parse_pull_buffer (parse, off, sizeof (WavpackHeader),
445 : : &ret);
446 : :
447 [ # # ]: 0 : if (buf == NULL)
448 : 0 : break;
449 : :
450 : 0 : gst_wavpack_read_header (&header, GST_BUFFER_DATA (buf));
451 : 0 : gst_buffer_unref (buf);
452 : :
453 [ # # ]: 0 : if (header.flags & INITIAL_BLOCK)
454 : 0 : gst_wavpack_parse_index_append_entry (parse, off, header.block_index,
455 : 0 : header.block_samples);
456 : : else
457 : 0 : continue;
458 : :
459 [ # # ][ # # ]: 0 : if (header.block_index <= sample &&
460 : 0 : sample < (header.block_index + header.block_samples)) {
461 : 0 : *byte_offset = off;
462 : 0 : *start_sample = header.block_index;
463 : 0 : return TRUE;
464 : : }
465 : :
466 : 0 : off += header.ckSize + 8;
467 : 0 : } while (1);
468 : :
469 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "scan failed: %s (off=0x%08" G_GINT64_MODIFIER "x)",
470 : : gst_flow_get_name (ret), off);
471 : :
472 : 0 : return FALSE;
473 : : }
474 : :
475 : : static gboolean
476 : 2 : gst_wavpack_parse_send_newsegment (GstWavpackParse * wvparse, gboolean update)
477 : : {
478 : 2 : GstSegment *s = &wvparse->segment;
479 : :
480 : : gboolean ret;
481 : :
482 : 2 : gint64 stop_time = -1;
483 : :
484 : 2 : gint64 start_time = 0;
485 : :
486 : : gint64 cur_pos_time;
487 : :
488 : : gint64 diff;
489 : :
490 : : /* segment is in DEFAULT format, but we want to send a TIME newsegment */
491 : 2 : start_time = gst_util_uint64_scale_int (s->start, GST_SECOND,
492 : 2 : wvparse->samplerate);
493 : :
494 [ - + ]: 2 : if (s->stop != -1) {
495 : 0 : stop_time = gst_util_uint64_scale_int (s->stop, GST_SECOND,
496 : 0 : wvparse->samplerate);
497 : : }
498 : :
499 [ - + ][ # # ]: 2 : GST_DEBUG_OBJECT (wvparse, "sending newsegment from %" GST_TIME_FORMAT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
500 : : " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time),
501 : : GST_TIME_ARGS (stop_time));
502 : :
503 : : /* after a seek, s->last_stop will point to a chunk boundary, ie. from
504 : : * which sample we will start sending data again, while s->start will
505 : : * point to the sample we actually want to seek to and want to start
506 : : * playing right after the seek. Adjust clock-time for the difference
507 : : * so we start playing from start_time */
508 : 2 : cur_pos_time = gst_util_uint64_scale_int (s->last_stop, GST_SECOND,
509 : 2 : wvparse->samplerate);
510 : 2 : diff = start_time - cur_pos_time;
511 : :
512 : 2 : ret = gst_pad_push_event (wvparse->srcpad,
513 : : gst_event_new_new_segment (update, s->rate, GST_FORMAT_TIME,
514 : : start_time, stop_time, start_time - diff));
515 : :
516 : 2 : return ret;
517 : : }
518 : :
519 : : static gboolean
520 : 0 : gst_wavpack_parse_handle_seek_event (GstWavpackParse * wvparse,
521 : : GstEvent * event)
522 : : {
523 : : GstSeekFlags seek_flags;
524 : :
525 : : GstSeekType start_type;
526 : :
527 : : GstSeekType stop_type;
528 : :
529 : : GstSegment segment;
530 : :
531 : : GstFormat format;
532 : :
533 : : gboolean only_update;
534 : :
535 : : gboolean flush, ret;
536 : :
537 : : gdouble speed;
538 : :
539 : : gint64 stop;
540 : :
541 : : gint64 start; /* sample we want to seek to */
542 : :
543 : : gint64 byte_offset; /* byte offset the chunk we seek to starts at */
544 : :
545 : : gint64 chunk_start; /* first sample in chunk we seek to */
546 : :
547 : : guint rate;
548 : :
549 : : gint64 last_stop;
550 : :
551 [ # # ]: 0 : if (wvparse->adapter) {
552 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "seeking in streaming mode not implemented yet");
553 : 0 : return FALSE;
554 : : }
555 : :
556 : 0 : gst_event_parse_seek (event, &speed, &format, &seek_flags, &start_type,
557 : : &start, &stop_type, &stop);
558 : :
559 [ # # ][ # # ]: 0 : if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME) {
560 [ # # ]: 0 : GST_DEBUG ("seeking is only supported in TIME or DEFAULT format");
561 : 0 : return FALSE;
562 : : }
563 : :
564 [ # # ]: 0 : if (speed < 0.0) {
565 [ # # ]: 0 : GST_DEBUG ("only forward playback supported, rate %f not allowed", speed);
566 : 0 : return FALSE;
567 : : }
568 : :
569 : 0 : GST_OBJECT_LOCK (wvparse);
570 : :
571 : 0 : rate = wvparse->samplerate;
572 [ # # ]: 0 : if (rate == 0) {
573 : 0 : GST_OBJECT_UNLOCK (wvparse);
574 [ # # ]: 0 : GST_DEBUG ("haven't read header yet");
575 : 0 : return FALSE;
576 : : }
577 : :
578 : : /* figure out the last position we need to play. If it's configured (stop !=
579 : : * -1), use that, else we play until the total duration of the file */
580 [ # # ]: 0 : if (stop == -1)
581 : 0 : stop = wvparse->segment.duration;
582 : :
583 : : /* convert from time to samples if necessary */
584 [ # # ]: 0 : if (format == GST_FORMAT_TIME) {
585 [ # # ]: 0 : if (start_type != GST_SEEK_TYPE_NONE)
586 : 0 : start = gst_util_uint64_scale_int (start, rate, GST_SECOND);
587 [ # # ]: 0 : if (stop_type != GST_SEEK_TYPE_NONE)
588 : 0 : stop = gst_util_uint64_scale_int (stop, rate, GST_SECOND);
589 : : }
590 : :
591 [ # # ]: 0 : if (start < 0) {
592 : 0 : GST_OBJECT_UNLOCK (wvparse);
593 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "Invalid start sample %" G_GINT64_FORMAT, start);
594 : 0 : return FALSE;
595 : : }
596 : :
597 : 0 : flush = ((seek_flags & GST_SEEK_FLAG_FLUSH) != 0);
598 : :
599 : : /* operate on segment copy until we know the seek worked */
600 : 0 : segment = wvparse->segment;
601 : :
602 : 0 : gst_segment_set_seek (&segment, speed, GST_FORMAT_DEFAULT,
603 : : seek_flags, start_type, start, stop_type, stop, &only_update);
604 : :
605 : : #if 0
606 : : if (only_update) {
607 : : wvparse->segment = segment;
608 : : gst_wavpack_parse_send_newsegment (wvparse, TRUE);
609 : : goto done;
610 : : }
611 : : #endif
612 : :
613 : 0 : gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_start ());
614 : :
615 [ # # ]: 0 : if (flush) {
616 : 0 : gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_start ());
617 : : } else {
618 : 0 : gst_pad_pause_task (wvparse->sinkpad);
619 : : }
620 : :
621 : 0 : GST_PAD_STREAM_LOCK (wvparse->sinkpad);
622 : :
623 : : /* Save current position */
624 : 0 : last_stop = wvparse->segment.last_stop;
625 : :
626 : 0 : gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_stop ());
627 : :
628 [ # # ]: 0 : if (flush) {
629 : 0 : gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_stop ());
630 : : }
631 : :
632 [ # # ][ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "Performing seek to %" GST_TIME_FORMAT " sample %"
[ # # ][ # # ]
[ # # ]
633 : : G_GINT64_FORMAT, GST_TIME_ARGS (segment.start * GST_SECOND / rate),
634 : : start);
635 : :
636 : 0 : ret = gst_wavpack_parse_scan_to_find_sample (wvparse, segment.start,
637 : : &byte_offset, &chunk_start);
638 : :
639 [ # # ]: 0 : if (ret) {
640 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "new offset: %" G_GINT64_FORMAT, byte_offset);
641 : 0 : wvparse->current_offset = byte_offset;
642 : : /* we want to send a newsegment event with the actual seek position
643 : : * as start, even though our first buffer might start before the
644 : : * configured segment. We leave it up to the decoder or sink to crop
645 : : * the output buffers accordingly */
646 : 0 : wvparse->segment = segment;
647 : 0 : wvparse->segment.last_stop = chunk_start;
648 : 0 : wvparse->need_newsegment = TRUE;
649 : 0 : wvparse->discont = (last_stop != chunk_start) ? TRUE : FALSE;
650 : :
651 : : /* if we're doing a segment seek, post a SEGMENT_START message */
652 [ # # ]: 0 : if (wvparse->segment.flags & GST_SEEK_FLAG_SEGMENT) {
653 : 0 : gst_element_post_message (GST_ELEMENT_CAST (wvparse),
654 : : gst_message_new_segment_start (GST_OBJECT_CAST (wvparse),
655 : : wvparse->segment.format, wvparse->segment.last_stop));
656 : : }
657 : : } else {
658 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "seek failed: don't know where to seek to");
659 : : }
660 : :
661 : 0 : GST_PAD_STREAM_UNLOCK (wvparse->sinkpad);
662 : 0 : GST_OBJECT_UNLOCK (wvparse);
663 : :
664 : 0 : gst_pad_start_task (wvparse->sinkpad,
665 : : (GstTaskFunction) gst_wavpack_parse_loop, wvparse);
666 : :
667 : 0 : return ret;
668 : : }
669 : :
670 : : static gboolean
671 : 0 : gst_wavpack_parse_sink_event (GstPad * pad, GstEvent * event)
672 : : {
673 : : GstWavpackParse *parse;
674 : :
675 : 0 : gboolean ret = TRUE;
676 : :
677 : 0 : parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
678 : :
679 [ # # # # ]: 0 : switch (GST_EVENT_TYPE (event)) {
680 : : case GST_EVENT_FLUSH_STOP:{
681 [ # # ]: 0 : if (parse->adapter) {
682 : 0 : gst_adapter_clear (parse->adapter);
683 : : }
684 [ # # ]: 0 : if (parse->pending_buffer) {
685 : 0 : gst_buffer_unref (parse->pending_buffer);
686 : 0 : parse->pending_buffer = NULL;
687 : 0 : parse->pending_offset = 0;
688 : : }
689 : 0 : ret = gst_pad_push_event (parse->srcpad, event);
690 : 0 : break;
691 : : }
692 : : case GST_EVENT_NEWSEGMENT:{
693 : 0 : parse->need_newsegment = TRUE;
694 : 0 : gst_event_unref (event);
695 : 0 : ret = TRUE;
696 : 0 : break;
697 : : }
698 : : case GST_EVENT_EOS:{
699 [ # # ]: 0 : if (parse->adapter) {
700 : : /* remove all bytes that are left in the adapter after EOS. They can't
701 : : * be a complete Wavpack block and we can't do anything with them */
702 : 0 : gst_adapter_clear (parse->adapter);
703 : : }
704 [ # # ]: 0 : if (parse->pending_buffer) {
705 : 0 : gst_buffer_unref (parse->pending_buffer);
706 : 0 : parse->pending_buffer = NULL;
707 : 0 : parse->pending_offset = 0;
708 : : }
709 : 0 : ret = gst_pad_push_event (parse->srcpad, event);
710 : 0 : break;
711 : : }
712 : : default:{
713 : : /* stream lock is recursive, should be fine for all events */
714 : 0 : GST_PAD_STREAM_LOCK (pad);
715 [ # # ]: 0 : if (parse->srcpad == NULL) {
716 : 0 : parse->queued_events = g_list_append (parse->queued_events, event);
717 : : } else {
718 : 0 : ret = gst_pad_push_event (parse->srcpad, event);
719 : : }
720 : 0 : GST_PAD_STREAM_UNLOCK (pad);
721 : : }
722 : : }
723 : :
724 : :
725 : 0 : gst_object_unref (parse);
726 : 0 : return ret;
727 : : }
728 : :
729 : : static gboolean
730 : 0 : gst_wavpack_parse_src_event (GstPad * pad, GstEvent * event)
731 : : {
732 : : GstWavpackParse *parse;
733 : :
734 : : gboolean ret;
735 : :
736 : 0 : parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
737 : :
738 [ # # ]: 0 : switch (GST_EVENT_TYPE (event)) {
739 : : case GST_EVENT_SEEK:
740 : 0 : ret = gst_wavpack_parse_handle_seek_event (parse, event);
741 : 0 : break;
742 : : default:
743 : 0 : ret = gst_pad_event_default (pad, event);
744 : 0 : break;
745 : : }
746 : :
747 : 0 : gst_object_unref (parse);
748 : 0 : return ret;
749 : : }
750 : :
751 : : static void
752 : 5 : gst_wavpack_parse_init (GstWavpackParse * parse, GstWavpackParseClass * gclass)
753 : : {
754 : 5 : GstElementClass *klass = GST_ELEMENT_GET_CLASS (parse);
755 : :
756 : : GstPadTemplate *tmpl;
757 : :
758 : 5 : tmpl = gst_element_class_get_pad_template (klass, "sink");
759 : 5 : parse->sinkpad = gst_pad_new_from_template (tmpl, "sink");
760 : :
761 : 5 : gst_pad_set_activate_function (parse->sinkpad,
762 : 5 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_activate));
763 : 5 : gst_pad_set_activatepull_function (parse->sinkpad,
764 : 5 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_activate_pull));
765 : 5 : gst_pad_set_event_function (parse->sinkpad,
766 : 5 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_event));
767 : 5 : gst_pad_set_chain_function (parse->sinkpad,
768 : 5 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_chain));
769 : :
770 : 5 : gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
771 : :
772 : 5 : parse->srcpad = NULL;
773 : 5 : gst_wavpack_parse_reset (parse);
774 : 5 : }
775 : :
776 : : static gint64
777 : 0 : gst_wavpack_parse_get_upstream_length (GstWavpackParse * parse)
778 : : {
779 : 0 : gint64 length = -1;
780 : :
781 : 0 : GstFormat format = GST_FORMAT_BYTES;
782 : :
783 [ # # ]: 0 : if (!gst_pad_query_peer_duration (parse->sinkpad, &format, &length)) {
784 : 0 : length = -1;
785 : : } else {
786 [ # # ]: 0 : GST_DEBUG ("upstream length: %" G_GINT64_FORMAT, length);
787 : : }
788 : 0 : return length;
789 : : }
790 : :
791 : : static GstBuffer *
792 : 0 : gst_wavpack_parse_pull_buffer (GstWavpackParse * wvparse, gint64 offset,
793 : : guint size, GstFlowReturn * flow)
794 : : {
795 : : GstFlowReturn flow_ret;
796 : :
797 : 0 : GstBuffer *buf = NULL;
798 : :
799 [ # # ]: 0 : if (offset + size > wvparse->upstream_length) {
800 : 0 : wvparse->upstream_length = gst_wavpack_parse_get_upstream_length (wvparse);
801 [ # # ]: 0 : if (offset + size > wvparse->upstream_length) {
802 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "EOS: %" G_GINT64_FORMAT " + %u > %"
803 : : G_GINT64_FORMAT, offset, size, wvparse->upstream_length);
804 : 0 : flow_ret = GST_FLOW_UNEXPECTED;
805 : 0 : goto done;
806 : : }
807 : : }
808 : :
809 : 0 : flow_ret = gst_pad_pull_range (wvparse->sinkpad, offset, size, &buf);
810 : :
811 [ # # ]: 0 : if (flow_ret != GST_FLOW_OK) {
812 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "pull_range (%" G_GINT64_FORMAT ", %u) "
813 : : "failed, flow: %s", offset, size, gst_flow_get_name (flow_ret));
814 : 0 : buf = NULL;
815 : 0 : goto done;
816 : : }
817 : :
818 [ # # ]: 0 : if (GST_BUFFER_SIZE (buf) < size) {
819 [ # # ]: 0 : GST_DEBUG_OBJECT (wvparse, "Short read at offset %" G_GINT64_FORMAT
820 : : ", got only %u of %u bytes", offset, GST_BUFFER_SIZE (buf), size);
821 : 0 : gst_buffer_unref (buf);
822 : 0 : buf = NULL;
823 : 0 : flow_ret = GST_FLOW_UNEXPECTED;
824 : : }
825 : :
826 : : done:
827 [ # # ]: 0 : if (flow)
828 : 0 : *flow = flow_ret;
829 : 0 : return buf;
830 : : }
831 : :
832 : : static gboolean
833 : 2 : gst_wavpack_parse_create_src_pad (GstWavpackParse * wvparse, GstBuffer * buf,
834 : : WavpackHeader * header)
835 : : {
836 : : GstWavpackMetadata meta;
837 : :
838 : 2 : GstCaps *caps = NULL;
839 : :
840 : : guchar *bufptr;
841 : :
842 [ - + ]: 2 : g_assert (wvparse->srcpad == NULL);
843 : :
844 : 2 : bufptr = GST_BUFFER_DATA (buf) + sizeof (WavpackHeader);
845 : :
846 [ + - ]: 12 : while (gst_wavpack_read_metadata (&meta, GST_BUFFER_DATA (buf), &bufptr)) {
847 [ - + + ]: 12 : switch (meta.id) {
848 : : case ID_WVC_BITSTREAM:{
849 : 0 : caps = gst_caps_new_simple ("audio/x-wavpack-correction",
850 : : "framed", G_TYPE_BOOLEAN, TRUE, NULL);
851 : 0 : wvparse->srcpad =
852 : 0 : gst_pad_new_from_template (gst_element_class_get_pad_template
853 : 0 : (GST_ELEMENT_GET_CLASS (wvparse), "wvcsrc"), "wvcsrc");
854 : 0 : break;
855 : : }
856 : : case ID_WV_BITSTREAM:
857 : : case ID_WVX_BITSTREAM:{
858 : 2 : WavpackStreamReader *stream_reader = gst_wavpack_stream_reader_new ();
859 : :
860 : : WavpackContext *wpc;
861 : :
862 : : gchar error_msg[80];
863 : :
864 : : read_id rid;
865 : :
866 : : gint channel_mask;
867 : :
868 : 2 : rid.buffer = GST_BUFFER_DATA (buf);
869 : 2 : rid.length = GST_BUFFER_SIZE (buf);
870 : 2 : rid.position = 0;
871 : :
872 : 2 : wpc =
873 : : WavpackOpenFileInputEx (stream_reader, &rid, NULL, error_msg, 0, 0);
874 : :
875 [ - + ]: 2 : if (!wpc)
876 : 0 : return FALSE;
877 : :
878 : 2 : wvparse->samplerate = WavpackGetSampleRate (wpc);
879 : 2 : wvparse->channels = WavpackGetNumChannels (wpc);
880 : 2 : wvparse->total_samples =
881 : 2 : (header->total_samples ==
882 [ + + ]: 2 : 0xffffffff) ? G_GINT64_CONSTANT (-1) : header->total_samples;
883 : :
884 : 2 : caps = gst_caps_new_simple ("audio/x-wavpack",
885 : : "width", G_TYPE_INT, WavpackGetBitsPerSample (wpc),
886 : : "channels", G_TYPE_INT, wvparse->channels,
887 : : "rate", G_TYPE_INT, wvparse->samplerate,
888 : : "framed", G_TYPE_BOOLEAN, TRUE, NULL);
889 : : #ifdef WAVPACK_OLD_API
890 : : channel_mask = wpc->config.channel_mask;
891 : : #else
892 : 2 : channel_mask = WavpackGetChannelMask (wpc);
893 : : #endif
894 [ - + ]: 2 : if (channel_mask == 0)
895 : 0 : channel_mask =
896 : 0 : gst_wavpack_get_default_channel_mask (wvparse->channels);
897 : :
898 [ + - ]: 2 : if (channel_mask != 0) {
899 [ - + ]: 2 : if (!gst_wavpack_set_channel_layout (caps, channel_mask)) {
900 [ # # ]: 0 : GST_WARNING_OBJECT (wvparse, "Failed to set channel layout");
901 : 0 : gst_caps_unref (caps);
902 : 0 : caps = NULL;
903 : 0 : WavpackCloseFile (wpc);
904 : 0 : g_free (stream_reader);
905 : 0 : break;
906 : : }
907 : : }
908 : :
909 : 2 : wvparse->srcpad =
910 : 2 : gst_pad_new_from_template (gst_element_class_get_pad_template
911 : 2 : (GST_ELEMENT_GET_CLASS (wvparse), "src"), "src");
912 : 2 : WavpackCloseFile (wpc);
913 : 2 : g_free (stream_reader);
914 : 2 : break;
915 : : }
916 : : default:{
917 [ - + ]: 10 : GST_LOG_OBJECT (wvparse, "unhandled ID: 0x%02x", meta.id);
918 : 10 : break;
919 : : }
920 : : }
921 [ + + ]: 12 : if (caps != NULL)
922 : 2 : break;
923 : : }
924 : :
925 [ + - ][ - + ]: 2 : if (caps == NULL || wvparse->srcpad == NULL)
926 : 0 : return FALSE;
927 : :
928 [ - + ]: 2 : GST_DEBUG_OBJECT (wvparse, "Added src pad with caps %" GST_PTR_FORMAT, caps);
929 : :
930 : 2 : gst_pad_set_query_function (wvparse->srcpad,
931 : 2 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_src_query));
932 : 2 : gst_pad_set_query_type_function (wvparse->srcpad,
933 : 2 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_get_src_query_types));
934 : 2 : gst_pad_set_event_function (wvparse->srcpad,
935 : 2 : GST_DEBUG_FUNCPTR (gst_wavpack_parse_src_event));
936 : :
937 : 2 : gst_pad_set_caps (wvparse->srcpad, caps);
938 : 2 : gst_caps_unref (caps);
939 : 2 : gst_pad_use_fixed_caps (wvparse->srcpad);
940 : :
941 : 2 : gst_object_ref (wvparse->srcpad);
942 : 2 : gst_pad_set_active (wvparse->srcpad, TRUE);
943 : 2 : gst_element_add_pad (GST_ELEMENT (wvparse), wvparse->srcpad);
944 : 2 : gst_element_no_more_pads (GST_ELEMENT (wvparse));
945 : :
946 : 2 : return TRUE;
947 : : }
948 : :
949 : : static GstFlowReturn
950 : 3 : gst_wavpack_parse_push_buffer (GstWavpackParse * wvparse, GstBuffer * buf,
951 : : WavpackHeader * header)
952 : : {
953 : : GstFlowReturn ret;
954 : 3 : wvparse->current_offset += header->ckSize + 8;
955 : :
956 : 3 : wvparse->segment.last_stop = header->block_index;
957 : :
958 [ + + ]: 3 : if (wvparse->need_newsegment) {
959 [ + - ]: 2 : if (gst_wavpack_parse_send_newsegment (wvparse, FALSE))
960 : 2 : wvparse->need_newsegment = FALSE;
961 : : }
962 : :
963 : : /* send any queued events */
964 [ - + ]: 3 : if (wvparse->queued_events) {
965 : : GList *l;
966 : :
967 [ # # ]: 0 : for (l = wvparse->queued_events; l != NULL; l = l->next) {
968 : 0 : gst_pad_push_event (wvparse->srcpad, GST_EVENT (l->data));
969 : : }
970 : 0 : g_list_free (wvparse->queued_events);
971 : 0 : wvparse->queued_events = NULL;
972 : : }
973 : :
974 [ + - ]: 3 : if (wvparse->pending_buffer == NULL) {
975 : 3 : wvparse->pending_buffer = buf;
976 : 3 : wvparse->pending_offset = header->block_index;
977 [ # # ]: 0 : } else if (wvparse->pending_offset == header->block_index) {
978 : 0 : wvparse->pending_buffer = gst_buffer_join (wvparse->pending_buffer, buf);
979 : : } else {
980 [ # # ]: 0 : GST_ERROR ("Got incomplete block, dropping");
981 : 0 : gst_buffer_unref (wvparse->pending_buffer);
982 : 0 : wvparse->pending_buffer = buf;
983 : 0 : wvparse->pending_offset = header->block_index;
984 : : }
985 : :
986 [ - + ]: 3 : if (!(header->flags & FINAL_BLOCK))
987 : 0 : return GST_FLOW_OK;
988 : :
989 : 3 : buf = wvparse->pending_buffer;
990 : 3 : wvparse->pending_buffer = NULL;
991 : :
992 : 3 : GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (header->block_index,
993 : 3 : GST_SECOND, wvparse->samplerate);
994 : 3 : GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (header->block_samples,
995 : 3 : GST_SECOND, wvparse->samplerate);
996 : 3 : GST_BUFFER_OFFSET (buf) = header->block_index;
997 : 3 : GST_BUFFER_OFFSET_END (buf) = header->block_index + header->block_samples;
998 : :
999 [ + + ][ - + ]: 3 : if (wvparse->discont || wvparse->next_block_index != header->block_index) {
1000 : 2 : GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
1001 : 2 : wvparse->discont = FALSE;
1002 : : }
1003 : :
1004 : 3 : wvparse->next_block_index = header->block_index + header->block_samples;
1005 : :
1006 : 3 : gst_buffer_set_caps (buf, GST_PAD_CAPS (wvparse->srcpad));
1007 : :
1008 [ - + ][ # # ]: 3 : GST_LOG_OBJECT (wvparse, "Pushing buffer with time %" GST_TIME_FORMAT,
[ # # ][ # # ]
[ # # ]
1009 : : GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1010 : :
1011 : 3 : ret = gst_pad_push (wvparse->srcpad, buf);
1012 : :
1013 : 3 : wvparse->segment.last_stop = wvparse->next_block_index;
1014 : :
1015 : 3 : return ret;
1016 : : }
1017 : :
1018 : : static guint8 *
1019 : 1 : gst_wavpack_parse_find_marker (guint8 * buf, guint size)
1020 : : {
1021 : : int i;
1022 : :
1023 : 1 : guint8 *ret = NULL;
1024 : :
1025 [ - + ]: 1 : if (G_UNLIKELY (size < 4))
1026 : 0 : return NULL;
1027 : :
1028 [ + - ]: 107 : for (i = 0; i < size - 4; i++) {
1029 [ + + ]: 107 : if (memcmp (buf + i, "wvpk", 4) == 0) {
1030 : 1 : ret = buf + i;
1031 : 1 : break;
1032 : : }
1033 : : }
1034 : 1 : return ret;
1035 : : }
1036 : :
1037 : : static GstFlowReturn
1038 : 0 : gst_wavpack_parse_resync_loop (GstWavpackParse * parse, WavpackHeader * header)
1039 : : {
1040 : 0 : GstFlowReturn flow_ret = GST_FLOW_UNEXPECTED;
1041 : :
1042 : 0 : GstBuffer *buf = NULL;
1043 : :
1044 : : /* loop until we have a frame header or reach the end of the stream */
1045 : : while (1) {
1046 : : guint8 *data, *marker;
1047 : :
1048 : : guint len, size;
1049 : :
1050 [ # # ]: 0 : if (buf) {
1051 : 0 : gst_buffer_unref (buf);
1052 : 0 : buf = NULL;
1053 : : }
1054 : :
1055 [ # # ][ # # ]: 0 : if (parse->upstream_length == 0 ||
1056 : 0 : parse->upstream_length <= parse->current_offset) {
1057 : 0 : parse->upstream_length = gst_wavpack_parse_get_upstream_length (parse);
1058 [ # # ][ # # ]: 0 : if (parse->upstream_length == 0 ||
1059 : 0 : parse->upstream_length <= parse->current_offset) {
1060 : : break;
1061 : : }
1062 : : }
1063 : :
1064 : 0 : len = MIN (parse->upstream_length - parse->current_offset, 2048);
1065 : :
1066 [ # # ]: 0 : GST_LOG_OBJECT (parse, "offset: %" G_GINT64_FORMAT, parse->current_offset);
1067 : :
1068 : 0 : buf = gst_wavpack_parse_pull_buffer (parse, parse->current_offset,
1069 : : len, &flow_ret);
1070 : :
1071 : : /* whatever the problem is, there's nothing more for us to do for now */
1072 [ # # ]: 0 : if (flow_ret != GST_FLOW_OK)
1073 : 0 : break;
1074 : :
1075 : 0 : data = GST_BUFFER_DATA (buf);
1076 : 0 : size = GST_BUFFER_SIZE (buf);
1077 : :
1078 : : /* not enough data for a header? */
1079 [ # # ]: 0 : if (size < sizeof (WavpackHeader))
1080 : 0 : break;
1081 : :
1082 : : /* got a header right where we are at now? */
1083 [ # # ]: 0 : if (gst_wavpack_read_header (header, data))
1084 : 0 : break;
1085 : :
1086 : : /* nope, let's see if we can find one */
1087 : 0 : marker = gst_wavpack_parse_find_marker (data + 1, size - 1);
1088 : :
1089 [ # # ]: 0 : if (marker) {
1090 : 0 : parse->current_offset += marker - data;
1091 : : /* do one more loop iteration to make sure we pull enough
1092 : : * data for a full header, we'll bail out then */
1093 : : } else {
1094 : 0 : parse->current_offset += len - 4;
1095 : : }
1096 : 0 : }
1097 : :
1098 [ # # ]: 0 : if (buf)
1099 : 0 : gst_buffer_unref (buf);
1100 : :
1101 : 0 : return flow_ret;
1102 : : }
1103 : :
1104 : : static void
1105 : 0 : gst_wavpack_parse_loop (GstElement * element)
1106 : : {
1107 : 0 : GstWavpackParse *parse = GST_WAVPACK_PARSE (element);
1108 : :
1109 : : GstFlowReturn flow_ret;
1110 : 0 : WavpackHeader header = { {0,}, 0, };
1111 : 0 : GstBuffer *buf = NULL;
1112 : :
1113 : 0 : flow_ret = gst_wavpack_parse_resync_loop (parse, &header);
1114 : :
1115 [ # # ]: 0 : if (flow_ret != GST_FLOW_OK)
1116 : 0 : goto pause;
1117 : :
1118 [ # # ]: 0 : GST_LOG_OBJECT (parse, "Read header at offset %" G_GINT64_FORMAT
1119 : : ": chunk size = %u+8", parse->current_offset, header.ckSize);
1120 : :
1121 : 0 : buf = gst_wavpack_parse_pull_buffer (parse, parse->current_offset,
1122 : 0 : header.ckSize + 8, &flow_ret);
1123 : :
1124 [ # # ]: 0 : if (flow_ret != GST_FLOW_OK)
1125 : 0 : goto pause;
1126 : :
1127 [ # # ]: 0 : if (parse->srcpad == NULL) {
1128 [ # # ]: 0 : if (!gst_wavpack_parse_create_src_pad (parse, buf, &header)) {
1129 [ # # ]: 0 : GST_ERROR_OBJECT (parse, "Failed to create src pad");
1130 : 0 : flow_ret = GST_FLOW_ERROR;
1131 : 0 : goto pause;
1132 : : }
1133 : : }
1134 [ # # ]: 0 : if (header.flags & INITIAL_BLOCK)
1135 : 0 : gst_wavpack_parse_index_append_entry (parse, parse->current_offset,
1136 : 0 : header.block_index, header.block_samples);
1137 : :
1138 : 0 : flow_ret = gst_wavpack_parse_push_buffer (parse, buf, &header);
1139 [ # # ]: 0 : if (flow_ret != GST_FLOW_OK)
1140 : 0 : goto pause;
1141 : :
1142 : 0 : return;
1143 : :
1144 : : pause:
1145 : : {
1146 : 0 : const gchar *reason = gst_flow_get_name (flow_ret);
1147 : :
1148 [ # # ]: 0 : GST_LOG_OBJECT (parse, "pausing task, reason %s", reason);
1149 : 0 : gst_pad_pause_task (parse->sinkpad);
1150 : :
1151 [ # # ][ # # ]: 0 : if (flow_ret == GST_FLOW_UNEXPECTED && parse->srcpad) {
1152 [ # # ]: 0 : if (parse->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1153 : : GstClockTime stop;
1154 : :
1155 [ # # ]: 0 : GST_LOG_OBJECT (parse, "Sending segment done");
1156 : :
1157 [ # # ]: 0 : if ((stop = parse->segment.stop) == -1)
1158 : 0 : stop = parse->segment.duration;
1159 : :
1160 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1161 : : gst_message_new_segment_done (GST_OBJECT_CAST (parse),
1162 : : parse->segment.format, stop));
1163 : : } else {
1164 [ # # ]: 0 : GST_LOG_OBJECT (parse, "Sending EOS, at end of stream");
1165 : 0 : gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
1166 : : }
1167 [ # # ]: 0 : } else if (flow_ret == GST_FLOW_NOT_LINKED
1168 [ # # ]: 0 : || flow_ret < GST_FLOW_UNEXPECTED) {
1169 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (parse, STREAM, FAILED,
[ # # ][ # # ]
1170 : : (_("Internal data stream error.")), ("stream stopped, reason %s",
1171 : : reason));
1172 [ # # ]: 0 : if (parse->srcpad)
1173 : 0 : gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
1174 : : }
1175 : 0 : return;
1176 : : }
1177 : : }
1178 : :
1179 : : static gboolean
1180 : 3 : gst_wavpack_parse_resync_adapter (GstAdapter * adapter)
1181 : : {
1182 : : const guint8 *buf, *marker;
1183 : :
1184 : 3 : guint avail = gst_adapter_available (adapter);
1185 : :
1186 [ - + ]: 3 : if (avail < 4)
1187 : 0 : return FALSE;
1188 : :
1189 : : /* if the marker is at the beginning don't do the expensive search */
1190 : 3 : buf = gst_adapter_peek (adapter, 4);
1191 [ + + ]: 3 : if (memcmp (buf, "wvpk", 4) == 0)
1192 : 2 : return TRUE;
1193 : :
1194 [ - + ]: 1 : if (avail == 4)
1195 : 0 : return FALSE;
1196 : :
1197 : : /* search for the marker in the complete content of the adapter */
1198 : 1 : buf = gst_adapter_peek (adapter, avail);
1199 [ + - ][ + - ]: 1 : if (buf && (marker = gst_wavpack_parse_find_marker ((guint8 *) buf, avail))) {
1200 : 1 : gst_adapter_flush (adapter, marker - buf);
1201 : 1 : return TRUE;
1202 : : }
1203 : :
1204 : : /* flush everything except the last 4 bytes. they could contain
1205 : : * the start of a new marker */
1206 : 0 : gst_adapter_flush (adapter, avail - 4);
1207 : :
1208 : 3 : return FALSE;
1209 : : }
1210 : :
1211 : : static GstFlowReturn
1212 : 2 : gst_wavpack_parse_chain (GstPad * pad, GstBuffer * buf)
1213 : : {
1214 : 2 : GstWavpackParse *wvparse = GST_WAVPACK_PARSE (GST_PAD_PARENT (pad));
1215 : :
1216 : 2 : GstFlowReturn ret = GST_FLOW_OK;
1217 : :
1218 : : WavpackHeader wph;
1219 : :
1220 : : const guint8 *tmp_buf;
1221 : :
1222 [ + - ]: 2 : if (!wvparse->adapter) {
1223 : 2 : wvparse->adapter = gst_adapter_new ();
1224 : : }
1225 : :
1226 [ - + ]: 2 : if (GST_BUFFER_IS_DISCONT (buf)) {
1227 : 0 : gst_adapter_clear (wvparse->adapter);
1228 : 0 : wvparse->discont = TRUE;
1229 : : }
1230 : :
1231 : 2 : gst_adapter_push (wvparse->adapter, buf);
1232 : :
1233 [ - + ]: 2 : if (gst_adapter_available (wvparse->adapter) < sizeof (WavpackHeader))
1234 : 0 : return ret;
1235 : :
1236 [ - + ]: 2 : if (!gst_wavpack_parse_resync_adapter (wvparse->adapter))
1237 : 0 : return ret;
1238 : :
1239 : 2 : tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader));
1240 : 2 : gst_wavpack_read_header (&wph, (guint8 *) tmp_buf);
1241 : :
1242 [ + + ]: 5 : while (gst_adapter_available (wvparse->adapter) >= wph.ckSize + 4 * 1 + 4) {
1243 : 3 : GstBuffer *outbuf =
1244 : 3 : gst_adapter_take_buffer (wvparse->adapter, wph.ckSize + 4 * 1 + 4);
1245 : :
1246 [ - + ]: 3 : if (!outbuf)
1247 : 0 : return GST_FLOW_ERROR;
1248 : :
1249 [ + + ]: 3 : if (wvparse->srcpad == NULL) {
1250 [ - + ]: 2 : if (!gst_wavpack_parse_create_src_pad (wvparse, outbuf, &wph)) {
1251 [ # # ]: 0 : GST_ERROR_OBJECT (wvparse, "Failed to create src pad");
1252 : 0 : ret = GST_FLOW_ERROR;
1253 : 0 : break;
1254 : : }
1255 : : }
1256 : :
1257 : 3 : ret = gst_wavpack_parse_push_buffer (wvparse, outbuf, &wph);
1258 : :
1259 [ - + ]: 3 : if (ret != GST_FLOW_OK)
1260 : 0 : break;
1261 : :
1262 [ + + ]: 3 : if (gst_adapter_available (wvparse->adapter) >= sizeof (WavpackHeader)) {
1263 : 1 : tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader));
1264 : :
1265 [ - + ]: 1 : if (!gst_wavpack_parse_resync_adapter (wvparse->adapter))
1266 : 0 : break;
1267 : :
1268 : 1 : gst_wavpack_read_header (&wph, (guint8 *) tmp_buf);
1269 : : }
1270 : : }
1271 : :
1272 : 2 : return ret;
1273 : : }
1274 : :
1275 : : static GstStateChangeReturn
1276 : 46 : gst_wavpack_parse_change_state (GstElement * element, GstStateChange transition)
1277 : : {
1278 : 46 : GstWavpackParse *wvparse = GST_WAVPACK_PARSE (element);
1279 : :
1280 : 46 : GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1281 : :
1282 [ + + ]: 46 : switch (transition) {
1283 : : case GST_STATE_CHANGE_READY_TO_PAUSED:
1284 : 9 : gst_segment_init (&wvparse->segment, GST_FORMAT_DEFAULT);
1285 : 9 : wvparse->segment.last_stop = 0;
1286 : : default:
1287 : 46 : break;
1288 : : }
1289 : :
1290 [ + - ]: 46 : if (GST_ELEMENT_CLASS (parent_class)->change_state)
1291 : 46 : ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1292 : :
1293 [ + + ]: 46 : switch (transition) {
1294 : : case GST_STATE_CHANGE_PAUSED_TO_READY:
1295 : 9 : gst_wavpack_parse_reset (wvparse);
1296 : 9 : break;
1297 : : default:
1298 : 37 : break;
1299 : : }
1300 : :
1301 : 46 : return ret;
1302 : : }
1303 : :
1304 : : static gboolean
1305 : 9 : gst_wavpack_parse_sink_activate (GstPad * sinkpad)
1306 : : {
1307 [ - + ]: 9 : if (gst_pad_check_pull_range (sinkpad)) {
1308 : 0 : return gst_pad_activate_pull (sinkpad, TRUE);
1309 : : } else {
1310 : 9 : return gst_pad_activate_push (sinkpad, TRUE);
1311 : : }
1312 : : }
1313 : :
1314 : : static gboolean
1315 : 0 : gst_wavpack_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
1316 : : {
1317 : : gboolean result;
1318 : :
1319 [ # # ]: 0 : if (active) {
1320 : 0 : result = gst_pad_start_task (sinkpad,
1321 : 0 : (GstTaskFunction) gst_wavpack_parse_loop, GST_PAD_PARENT (sinkpad));
1322 : : } else {
1323 : 0 : result = gst_pad_stop_task (sinkpad);
1324 : : }
1325 : :
1326 : 0 : return result;
1327 : : }
1328 : :
1329 : : gboolean
1330 : 11 : gst_wavpack_parse_plugin_init (GstPlugin * plugin)
1331 : : {
1332 [ - + ]: 11 : if (!gst_element_register (plugin, "wavpackparse",
1333 : : GST_RANK_PRIMARY, GST_TYPE_WAVPACK_PARSE)) {
1334 : 0 : return FALSE;
1335 : : }
1336 : :
1337 [ + - ]: 11 : GST_DEBUG_CATEGORY_INIT (gst_wavpack_parse_debug, "wavpack_parse", 0,
1338 : : "Wavpack file parser");
1339 : :
1340 : 11 : return TRUE;
1341 : : }
|