1 /*
2 Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3 Organisation (CSIRO) Australia
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 - Neither the name of CSIRO Australia nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * oggz_seek.c
35 *
36 * Conrad Parker <conrad@annodex.net>
37 */
38
39 #include "config.h"
40
41 #if OGGZ_CONFIG_READ
42
43 #include <assert.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #include <fcntl.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <time.h>
57
58 #include <ogg/ogg.h>
59
60 #include "oggz_compat.h"
61 #include "oggz_private.h"
62
63 /*#define DEBUG*/
64 /*#define DEBUG_VERBOSE*/
65
66 #define CHUNKSIZE 4096
67
68 /*
69 * The typical usage is:
70 *
71 * oggz_set_data_start (oggz, oggz_tell (oggz));
72 */
73 int
oggz_set_data_start(OGGZ * oggz,oggz_off_t offset)74 oggz_set_data_start (OGGZ * oggz, oggz_off_t offset)
75 {
76 if (oggz == NULL) return -1;
77
78 if (offset < 0) return -1;
79
80 oggz->offset_data_begin = offset;
81
82 return 0;
83 }
84
85 static oggz_off_t
oggz_tell_raw(OGGZ * oggz)86 oggz_tell_raw (OGGZ * oggz)
87 {
88 oggz_off_t offset_at;
89
90 offset_at = oggz_io_tell (oggz);
91
92 return offset_at;
93 }
94
95 /*
96 * seeks and syncs
97 */
98
99 int
oggz_seek_reset_stream(void * data)100 oggz_seek_reset_stream(void *data) {
101 ((oggz_stream_t *)data)->last_granulepos = -1L;
102 return 0;
103 }
104
105 static oggz_off_t
oggz_seek_raw(OGGZ * oggz,oggz_off_t offset,int whence)106 oggz_seek_raw (OGGZ * oggz, oggz_off_t offset, int whence)
107 {
108 OggzReader * reader = &oggz->x.reader;
109 oggz_off_t offset_at;
110
111 if (oggz_io_seek (oggz, offset, whence) == -1) {
112 return -1;
113 }
114
115 offset_at = oggz_io_tell (oggz);
116
117 oggz->offset = offset_at;
118
119 ogg_sync_reset (&reader->ogg_sync);
120
121 oggz_vector_foreach(oggz->streams, oggz_seek_reset_stream);
122
123 return offset_at;
124 }
125
126 static int
oggz_stream_reset(void * data)127 oggz_stream_reset (void * data)
128 {
129 oggz_stream_t * stream = (oggz_stream_t *) data;
130
131 if (stream->ogg_stream.serialno != -1) {
132 ogg_stream_reset (&stream->ogg_stream);
133 }
134
135 return 0;
136 }
137
138 static void
oggz_reset_streams(OGGZ * oggz)139 oggz_reset_streams (OGGZ * oggz)
140 {
141 oggz_vector_foreach (oggz->streams, oggz_stream_reset);
142 }
143
144 static long
oggz_reset_seek(OGGZ * oggz,oggz_off_t offset,ogg_int64_t unit,int whence)145 oggz_reset_seek (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence)
146 {
147 OggzReader * reader = &oggz->x.reader;
148
149 oggz_off_t offset_at;
150
151 offset_at = oggz_seek_raw (oggz, offset, whence);
152 if (offset_at == -1) return -1;
153
154 oggz->offset = offset_at;
155
156 #ifdef DEBUG
157 printf ("reset to %" PRI_OGGZ_OFF_T "d\n", offset_at);
158 #endif
159
160 if (unit != -1) reader->current_unit = unit;
161
162 return offset_at;
163 }
164
165 static long
oggz_reset(OGGZ * oggz,oggz_off_t offset,ogg_int64_t unit,int whence)166 oggz_reset (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence)
167 {
168 oggz_reset_streams (oggz);
169 return oggz_reset_seek (oggz, offset, unit, whence);
170 }
171
172 int
oggz_purge(OGGZ * oggz)173 oggz_purge (OGGZ * oggz)
174 {
175 if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
176
177 if (oggz->flags & OGGZ_WRITE) {
178 return OGGZ_ERR_INVALID;
179 }
180
181 oggz_reset_streams (oggz);
182
183 if (oggz->file && oggz_reset (oggz, oggz->offset, -1, SEEK_SET) < 0) {
184 return OGGZ_ERR_SYSTEM;
185 }
186
187 return 0;
188 }
189
190 /*
191 * oggz_get_next_page (oggz, og, do_read)
192 *
193 * retrieves the next page.
194 * returns >= 0 if found; return value is offset of page start
195 * returns -1 on error
196 * returns -2 if EOF was encountered
197 */
198 static oggz_off_t
oggz_get_next_page(OGGZ * oggz,ogg_page * og)199 oggz_get_next_page (OGGZ * oggz, ogg_page * og)
200 {
201 OggzReader * reader = &oggz->x.reader;
202 char * buffer;
203 long bytes = 0, more;
204 oggz_off_t page_offset = 0, ret;
205 int found = 0;
206
207 do {
208 more = ogg_sync_pageseek (&reader->ogg_sync, og);
209
210 if (more == 0) {
211 page_offset = 0;
212
213 buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE);
214 if ((bytes = (long) oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) {
215 if (oggz->file && feof (oggz->file)) {
216 #ifdef DEBUG_VERBOSE
217 printf ("get_next_page: feof (oggz->file), returning -2\n");
218 #endif
219 clearerr (oggz->file);
220 return -2;
221 }
222 }
223 if (bytes == OGGZ_ERR_SYSTEM) {
224 /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
225 return -1;
226 }
227
228 if (bytes == 0) {
229 #ifdef DEBUG_VERBOSE
230 printf ("get_next_page: bytes == 0, returning -2\n");
231 #endif
232 return -2;
233 }
234
235 ogg_sync_wrote(&reader->ogg_sync, bytes);
236
237 } else if (more < 0) {
238 #ifdef DEBUG_VERBOSE
239 printf ("get_next_page: skipped %ld bytes\n", -more);
240 #endif
241 page_offset -= more;
242 } else {
243 #ifdef DEBUG_VERBOSE
244 printf ("get_next_page: page has %ld bytes\n", more);
245 #endif
246 found = 1;
247 }
248
249 } while (!found);
250
251 /* Calculate the byte offset of the page which was found */
252 if (bytes > 0) {
253 oggz->offset = oggz_tell_raw (oggz) - bytes + page_offset;
254 } else {
255 /* didn't need to do any reading -- accumulate the page_offset */
256 oggz->offset += page_offset;
257 }
258
259 ret = oggz->offset + more;
260
261 return ret;
262 }
263
264 static oggz_off_t
oggz_get_next_start_page(OGGZ * oggz,ogg_page * og)265 oggz_get_next_start_page (OGGZ * oggz, ogg_page * og)
266 {
267 oggz_off_t page_offset;
268 int found = 0;
269
270 do {
271 page_offset = oggz_get_next_page (oggz, og);
272
273 /* Return this value if one of the following conditions is met:
274 *
275 * page_offset < 0 : error or EOF
276 * page_offset == 0 : start of stream
277 * !ogg_page_continued : page contains start of a packet
278 * ogg_page_packets > 1: page contains start of a packet
279 */
280 /*if (page_offset <= 0 || !ogg_page_continued (og) ||
281 ogg_page_packets (og) > 1)*/
282 if (page_offset <= 0 || ogg_page_granulepos(og) > -1)
283 found = 1;
284 }
285 while (!found);
286
287 return page_offset;
288 }
289
290 static oggz_off_t
oggz_get_prev_start_page(OGGZ * oggz,ogg_page * og,ogg_int64_t * granule,long * serialno)291 oggz_get_prev_start_page (OGGZ * oggz, ogg_page * og,
292 ogg_int64_t * granule, long * serialno)
293 {
294 oggz_off_t offset_at, offset_start;
295 oggz_off_t page_offset, found_offset = 0;
296 ogg_int64_t unit_at;
297 ogg_int64_t granule_at = -1;
298
299 #if 0
300 offset_at = oggz_tell_raw (oggz);
301 if (offset_at == -1) return -1;
302 #else
303 offset_at = oggz->offset;
304 #endif
305
306 offset_start = offset_at;
307
308 do {
309
310 offset_start = offset_start - CHUNKSIZE;
311 if (offset_start < 0) offset_start = 0;
312
313 offset_start = oggz_seek_raw (oggz, offset_start, SEEK_SET);
314 if (offset_start == -1) return -1;
315
316 #ifdef DEBUG
317
318 printf ("get_prev_start_page: [A] offset_at: @%" PRI_OGGZ_OFF_T "d\toffset_start: @%" PRI_OGGZ_OFF_T "d\n",
319 offset_at, offset_start);
320
321 printf ("get_prev_start_page: seeked to %" PRI_OGGZ_OFF_T "d\n", offset_start);
322 #endif
323
324 page_offset = 0;
325
326 do {
327 page_offset = oggz_get_next_start_page (oggz, og);
328 if (page_offset == -1) {
329 #ifdef DEBUG
330 printf ("get_prev_start_page: page_offset = -1\n");
331 #endif
332 return -1;
333 }
334 if (page_offset == -2) {
335 #ifdef DEBUG
336 printf ("*** get_prev_start_page: page_offset = -2\n");
337 #endif
338 break;
339 }
340
341 granule_at = ogg_page_granulepos (og);
342
343 #ifdef DEBUG_VERBOSE
344 printf ("get_prev_start_page: GOT page (%lld) @%" PRI_OGGZ_OFF_T "d\tat @%" PRI_OGGZ_OFF_T "d\n",
345 granule_at, page_offset, offset_at);
346 #endif
347
348 /* Need to stash the granule and serialno of this page because og
349 * will be overwritten by the time we realise this was the desired
350 * prev page */
351 if (page_offset >= 0 && page_offset < offset_at) {
352 found_offset = page_offset;
353 *granule = granule_at;
354 *serialno = ogg_page_serialno (og);
355 }
356
357 } while (page_offset >= 0 && page_offset < offset_at);
358
359 #ifdef DEBUG
360 printf ("get_prev_start_page: [B] offset_at: @%" PRI_OGGZ_OFF_T "d\toffset_start: @%" PRI_OGGZ_OFF_T "d\n"
361 "found_offset: @%" PRI_OGGZ_OFF_T "d\tpage_offset: @%" PRI_OGGZ_OFF_T "d\n",
362 offset_at, offset_start, found_offset, page_offset);
363 #endif
364 /* reset the file offset */
365 /*offset_at = offset_start;*/
366
367 } while (found_offset == 0 && offset_start > 0);
368
369 unit_at = oggz_get_unit (oggz, *serialno, *granule);
370 offset_at = oggz_reset (oggz, found_offset, unit_at, SEEK_SET);
371
372 #ifdef DEBUG
373 printf ("get_prev_start_page: [C] offset_at: @%" PRI_OGGZ_OFF_T "d\t"
374 "found_offset: @%" PRI_OGGZ_OFF_T "d\tunit_at: %lld\n",
375 offset_at, found_offset, unit_at);
376 #endif
377
378 if (offset_at == -1) return -1;
379
380 if (offset_at >= 0)
381 return found_offset;
382 else
383 return -1;
384 }
385
386 static oggz_off_t
oggz_scan_for_page(OGGZ * oggz,ogg_page * og,ogg_int64_t unit_target,oggz_off_t offset_begin,oggz_off_t offset_end)387 oggz_scan_for_page (OGGZ * oggz, ogg_page * og, ogg_int64_t unit_target,
388 oggz_off_t offset_begin, oggz_off_t offset_end)
389 {
390 oggz_off_t offset_at, offset_next;
391 oggz_off_t offset_prev = -1;
392 ogg_int64_t granule_at;
393 ogg_int64_t unit_at;
394 long serialno;
395
396 #ifdef DEBUG
397 printf (" SCANNING from %" PRI_OGGZ_OFF_T "d...", offset_begin);
398 #endif
399
400 for ( ; ; ) {
401 offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET);
402 if (offset_at == -1) return -1;
403
404 #ifdef DEBUG
405 printf (" scan @%" PRI_OGGZ_OFF_T "d\n", offset_at);
406 #endif
407
408 offset_next = oggz_get_next_start_page (oggz, og);
409
410 if (offset_next < 0) {
411 return offset_next;
412 }
413
414 if (offset_next == 0 && offset_begin != 0) {
415 #ifdef DEBUG
416 printf (" ... scanned past EOF\n");
417 #endif
418 return -1;
419 }
420 if (offset_next > offset_end) {
421 #ifdef DEBUG
422 printf (" ... scanned to page %ld\n", (long)ogg_page_granulepos (og));
423 #endif
424
425 #if 0
426 if (offset_prev != -1) {
427 offset_at = oggz_seek_raw (oggz, offset_prev, SEEK_SET);
428 if (offset_at == -1) return -1;
429
430 offset_next = oggz_get_next_start_page (oggz, og);
431 if (offset_next < 0) return offset_next;
432
433 serialno = ogg_page_serialno (og);
434 granule_at = ogg_page_granulepos (og);
435 unit_at = oggz_get_unit (oggz, serialno, granule_at);
436
437 return offset_at;
438 } else {
439 return -1;
440 }
441 #else
442 serialno = ogg_page_serialno (og);
443 granule_at = ogg_page_granulepos (og);
444 unit_at = oggz_get_unit (oggz, serialno, granule_at);
445
446 return offset_at;
447 #endif
448 }
449
450 offset_at = offset_next;
451
452 serialno = ogg_page_serialno (og);
453 granule_at = ogg_page_granulepos (og);
454 unit_at = oggz_get_unit (oggz, serialno, granule_at);
455
456 if (unit_at < unit_target) {
457 #ifdef DEBUG
458 printf (" scan: (%lld) < (%lld)\n", unit_at, unit_target);
459 #endif
460 offset_prev = offset_next;
461 offset_begin = offset_next+1;
462 } else if (unit_at > unit_target) {
463 #ifdef DEBUG
464 printf (" scan: (%lld) > (%lld)\n", unit_at, unit_target);
465 #endif
466 #if 0
467 /* hole ? */
468 offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET);
469 if (offset_at == -1) return -1;
470
471 offset_next = oggz_get_next_start_page (oggz, og);
472 if (offset_next < 0) return offset_next;
473
474 serialno = ogg_page_serialno (og);
475 granule_at = ogg_page_granulepos (og);
476 unit_at = oggz_get_unit (oggz, serialno, granule_at);
477
478 break;
479 #else
480 do {
481 offset_at = oggz_get_prev_start_page(oggz, og, &granule_at, &serialno);
482 if (offset_at < 0)
483 break;
484 unit_at = oggz_get_unit(oggz, serialno, granule_at);
485 } while (unit_at > unit_target);
486 return offset_at;
487 #endif
488 } else if (unit_at == unit_target) {
489 #ifdef DEBUG
490 printf (" scan: (%lld) == (%lld)\n", unit_at, unit_target);
491 #endif
492 break;
493 }
494 }
495
496 return offset_at;
497 }
498
499 #define GUESS_MULTIPLIER (1<<16)
500
501 static oggz_off_t
guess(ogg_int64_t unit_at,ogg_int64_t unit_target,ogg_int64_t unit_begin,ogg_int64_t unit_end,oggz_off_t offset_begin,oggz_off_t offset_end)502 guess (ogg_int64_t unit_at, ogg_int64_t unit_target,
503 ogg_int64_t unit_begin, ogg_int64_t unit_end,
504 oggz_off_t offset_begin, oggz_off_t offset_end)
505 {
506 ogg_int64_t guess_ratio;
507 oggz_off_t offset_guess;
508
509 if (unit_at == unit_begin) return offset_begin;
510
511 if (unit_end != -1) {
512 guess_ratio =
513 GUESS_MULTIPLIER * (unit_target - unit_begin) /
514 (unit_end - unit_begin);
515 } else {
516 guess_ratio =
517 GUESS_MULTIPLIER * (unit_target - unit_begin) /
518 (unit_at - unit_begin);
519 }
520
521 #ifdef DEBUG
522 printf ("oggz_seek::guess: guess_ratio %lld = (%lld - %lld) / (%lld - %lld)\n",
523 guess_ratio, unit_target, unit_begin, unit_at, unit_begin);
524 #endif
525
526 offset_guess = offset_begin +
527 (oggz_off_t)(((offset_end - offset_begin) * guess_ratio) /
528 GUESS_MULTIPLIER);
529
530 return offset_guess;
531 }
532
533 static oggz_off_t
oggz_seek_guess(ogg_int64_t unit_at,ogg_int64_t unit_target,ogg_int64_t unit_begin,ogg_int64_t unit_end,oggz_off_t offset_at,oggz_off_t offset_begin,oggz_off_t offset_end)534 oggz_seek_guess (ogg_int64_t unit_at, ogg_int64_t unit_target,
535 ogg_int64_t unit_begin, ogg_int64_t unit_end,
536 oggz_off_t offset_at,
537 oggz_off_t offset_begin, oggz_off_t offset_end)
538 {
539 oggz_off_t offset_guess;
540
541 if (unit_at == unit_begin) {
542 offset_guess = offset_begin + (offset_end - offset_begin)/2;
543 } else if (unit_end == -1) {
544 offset_guess = guess (unit_at, unit_target, unit_begin, unit_end,
545 offset_begin, offset_at);
546 } else if (unit_end <= unit_begin) {
547 #ifdef DEBUG
548 printf ("oggz_seek_guess: unit_end <= unit_begin (ERROR)\n");
549 #endif
550 offset_guess = -1;
551 } else {
552 offset_guess = guess (unit_at, unit_target, unit_begin, unit_end,
553 offset_begin, offset_end);
554 }
555
556 #ifdef DEBUG
557 printf ("oggz_seek_guess: guessed %" PRI_OGGZ_OFF_T "d\n", offset_guess);
558 #endif
559
560 return offset_guess;
561 }
562
563 static oggz_off_t
oggz_offset_end(OGGZ * oggz)564 oggz_offset_end (OGGZ * oggz)
565 {
566 int fd;
567 struct stat statbuf;
568 oggz_off_t offset_end = -1;
569
570 if (oggz->file != NULL) {
571 if ((fd = fileno (oggz->file)) == -1) {
572 /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
573 return -1;
574 }
575
576 if (fstat (fd, &statbuf) == -1) {
577 /*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
578 return -1;
579 }
580
581 if (oggz_stat_regular (statbuf.st_mode)) {
582 offset_end = statbuf.st_size;
583 #ifdef DEBUG
584 printf ("oggz_offset_end: stat size %" PRI_OGGZ_OFF_T "d\n", offset_end);
585 #endif
586 } else {
587 /*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/
588
589 /* XXX: should be able to just carry on and guess, as per io */
590 /*return -1;*/
591 }
592 } else {
593 oggz_off_t offset_save;
594
595 if (oggz->io == NULL || oggz->io->seek == NULL) {
596 /* No file, and no io seek method */
597 return -1;
598 }
599
600 /* Get the offset of the end by querying the io seek method */
601 offset_save = oggz_io_tell (oggz);
602 if (oggz_io_seek (oggz, 0, SEEK_END) == -1) {
603 return -1;
604 }
605 offset_end = oggz_io_tell (oggz);
606 if (oggz_io_seek (oggz, offset_save, SEEK_SET) == -1) {
607 return -1; /* fubar */
608 }
609 }
610
611 return offset_end;
612 }
613
614 ogg_int64_t
oggz_bounded_seek_set(OGGZ * oggz,ogg_int64_t unit_target,ogg_int64_t offset_begin,ogg_int64_t offset_end)615 oggz_bounded_seek_set (OGGZ * oggz,
616 ogg_int64_t unit_target,
617 ogg_int64_t offset_begin,
618 ogg_int64_t offset_end)
619 {
620 OggzReader * reader;
621 oggz_off_t offset_orig, offset_at, offset_guess;
622 oggz_off_t offset_next;
623 ogg_int64_t granule_at;
624 ogg_int64_t unit_at, unit_begin = -1, unit_end = -1, unit_last_iter = -1;
625 long serialno;
626 ogg_page * og;
627 int hit_eof = 0;
628
629 if (oggz == NULL) {
630 return -1;
631 }
632
633 if (unit_target > 0 && !oggz_has_metrics (oggz)) {
634 #ifdef DEBUG
635 printf ("oggz_bounded_seek_set: No metric defined, FAIL\n");
636 #endif
637 return -1;
638 }
639
640 if (offset_end == -1 && (offset_end = oggz_offset_end (oggz)) == -1) {
641 #ifdef DEBUG
642 printf ("oggz_bounded_seek_set: oggz_offset_end == -1, FAIL\n");
643 #endif
644 return -1;
645 }
646
647 reader = &oggz->x.reader;
648
649 if (unit_target == reader->current_unit) {
650 #ifdef DEBUG
651 printf ("oggz_bounded_seek_set: unit_target == reader->current_unit, SKIP\n");
652 #endif
653 return (long)reader->current_unit;
654 }
655
656 if (unit_target == 0) {
657 offset_at = oggz_reset (oggz, oggz->offset_data_begin, 0, SEEK_SET);
658 if (offset_at == -1) return -1;
659 return 0;
660 }
661
662 offset_at = oggz_tell_raw (oggz);
663 if (offset_at == -1) return -1;
664
665 offset_orig = oggz->offset;
666
667 unit_at = reader->current_unit;
668
669 og = &oggz->current_page;
670
671 if (unit_end == -1 && oggz_seek_raw (oggz, offset_end, SEEK_SET) >= 0) {
672 ogg_int64_t granulepos;
673
674 if (oggz_get_prev_start_page (oggz, og, &granulepos, &serialno) >= 0) {
675 unit_end = oggz_get_unit (oggz, serialno, granulepos);
676 }
677 }
678
679 if (unit_begin == -1 && oggz_seek_raw (oggz, offset_begin, SEEK_SET) >= 0) {
680 ogg_int64_t granulepos;
681 if (oggz_get_next_start_page (oggz, og) >= 0) {
682 serialno = ogg_page_serialno (og);
683 granulepos = ogg_page_granulepos (og);
684 unit_begin = oggz_get_unit (oggz, serialno, granulepos);
685 }
686 }
687
688 /* Fail if target isn't in specified range. */
689 if (unit_target < unit_begin || unit_target > unit_end)
690 return -1;
691
692 /* Reduce the search range if possible using read cursor position. */
693 if (unit_at > unit_begin && unit_at < unit_end) {
694 if (unit_target < unit_at) {
695 unit_end = unit_at;
696 offset_end = offset_at;
697 } else {
698 unit_begin = unit_at;
699 offset_begin = offset_at;
700 }
701 }
702
703 og = &oggz->current_page;
704
705 for ( ; ; ) {
706
707 unit_last_iter = unit_at;
708 hit_eof = 0;
709
710 #ifdef DEBUG
711 printf ("oggz_bounded_seek_set: [A] want u%lld: (u%lld - u%lld) [@%" PRI_OGGZ_OFF_T "d - @%" PRI_OGGZ_OFF_T "d]\n",
712 unit_target, unit_begin, unit_end, offset_begin, offset_end);
713 #endif
714
715 offset_guess = oggz_seek_guess (unit_at, unit_target,
716 unit_begin, unit_end,
717 offset_at,
718 offset_begin, offset_end);
719 if (offset_guess == -1) break;
720
721 if (offset_guess == offset_at) {
722 /* Already there, looping */
723 break;
724 }
725
726 if (offset_guess > offset_end) {
727 offset_guess = offset_end;
728 offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET);
729 offset_next = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
730 } else {
731 offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET);
732 offset_next = oggz_get_next_start_page (oggz, og);
733 serialno = ogg_page_serialno (og);
734 granule_at = ogg_page_granulepos (og);
735 }
736
737 unit_at = oggz_get_unit (oggz, serialno, granule_at);
738
739 #ifdef DEBUG
740 printf ("oggz_bounded_seek_set: offset_next %" PRI_OGGZ_OFF_T "d\n", offset_next);
741 #endif
742 if (unit_at == unit_last_iter) break;
743
744 #ifdef DEBUG
745 printf ("oggz_bounded_seek_set: [D] want u%lld, got page u%lld @%" PRI_OGGZ_OFF_T "d g%lld\n",
746 unit_target, unit_at, offset_at, granule_at);
747 #endif
748
749 if (unit_at < unit_target) {
750 offset_begin = offset_at;
751 unit_begin = unit_at;
752 if (unit_end == unit_begin) break;
753 } else if (unit_at > unit_target) {
754 offset_end = offset_at-1;
755 unit_end = unit_at;
756 if (unit_end == unit_begin) break;
757 } else {
758 break;
759 }
760 }
761
762 do {
763 offset_at = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
764 unit_at = oggz_get_unit (oggz, serialno, granule_at);
765 } while (unit_at > unit_target);
766
767 if (offset_at < 0) {
768 oggz_reset (oggz, offset_orig, -1, SEEK_SET);
769 return -1;
770 }
771
772 offset_at = oggz_reset (oggz, offset_at, unit_at, SEEK_SET);
773 if (offset_at == -1) return -1;
774
775 #ifdef DEBUG
776 printf ("oggz_bounded_seek_set: FOUND (%lld)\n", unit_at);
777 #endif
778
779 return (long)reader->current_unit;
780 }
781
782 static ogg_int64_t
oggz_seek_end(OGGZ * oggz,ogg_int64_t unit_offset)783 oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset)
784 {
785 oggz_off_t offset_orig, offset_at, offset_end;
786 ogg_int64_t granulepos;
787 ogg_int64_t unit_end;
788 long serialno;
789 ogg_page * og;
790
791 og = &oggz->current_page;
792
793 offset_orig = oggz->offset;
794
795 offset_at = oggz_seek_raw (oggz, 0, SEEK_END);
796 if (offset_at == -1) return -1;
797
798 offset_end = oggz_get_prev_start_page (oggz, og, &granulepos, &serialno);
799
800 if (offset_end < 0) {
801 oggz_reset (oggz, offset_orig, -1, SEEK_SET);
802 return -1;
803 }
804
805 unit_end = oggz_get_unit (oggz, serialno, granulepos);
806
807 #ifdef DEBUG
808 printf ("*** oggz_seek_end: found packet (%lld) at @%" PRI_OGGZ_OFF_T "d [%lld]\n",
809 unit_end, offset_end, granulepos);
810 #endif
811
812 return oggz_bounded_seek_set (oggz, unit_end + unit_offset, 0, -1);
813 }
814
815 off_t
oggz_seek(OGGZ * oggz,oggz_off_t offset,int whence)816 oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence)
817 {
818 OggzReader * reader;
819 ogg_int64_t units = -1;
820
821 if (oggz == NULL) return -1;
822
823 if (oggz->flags & OGGZ_WRITE) {
824 return -1;
825 }
826
827 if (offset == 0 && whence == SEEK_SET) units = 0;
828
829 reader = &oggz->x.reader;
830
831 if (!(offset == 0 && whence == SEEK_CUR)) {
832 /* Invalidate current_unit */
833 reader->current_unit = -1;
834 }
835
836 return (off_t)oggz_reset (oggz, offset, units, whence);
837 }
838
839 ogg_int64_t
oggz_seek_units(OGGZ * oggz,ogg_int64_t units,int whence)840 oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence)
841 {
842 OggzReader * reader;
843
844 ogg_int64_t r;
845
846 if (oggz == NULL) {
847 #ifdef DEBUG
848 printf ("oggz_seek_units: oggz NULL, FAIL\n");
849 #endif
850 return -1;
851 }
852
853 if (oggz->flags & OGGZ_WRITE) {
854 #ifdef DEBUG
855 printf ("oggz_seek_units: is OGGZ_WRITE, FAIL\n");
856 #endif
857 return -1;
858 }
859
860 if (!oggz_has_metrics (oggz)) {
861 #ifdef DEBUG
862 printf ("oggz_seek_units: !has_metrics, FAIL\n");
863 #endif
864 return -1;
865 }
866
867 reader = &oggz->x.reader;
868
869 switch (whence) {
870 case SEEK_SET:
871 r = oggz_bounded_seek_set (oggz, units, 0, -1);
872 break;
873 case SEEK_CUR:
874 units += reader->current_unit;
875 r = oggz_bounded_seek_set (oggz, units, 0, -1);
876 break;
877 case SEEK_END:
878 r = oggz_seek_end (oggz, units);
879 break;
880 default:
881 /*oggz_set_error (oggz, OGGZ_EINVALID);*/
882 r = -1;
883 break;
884 }
885
886 reader->current_granulepos = -1;
887 return r;
888 }
889
890 long
oggz_seek_byorder(OGGZ * oggz,void * target)891 oggz_seek_byorder (OGGZ * oggz, void * target)
892 {
893 return -1;
894 }
895
896 long
oggz_seek_packets(OGGZ * oggz,long serialno,long packets,int whence)897 oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence)
898 {
899 return -1;
900 }
901
902 #else
903
904 #include <ogg/ogg.h>
905 #include "oggz_private.h"
906
907 off_t
oggz_seek(OGGZ * oggz,oggz_off_t offset,int whence)908 oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence)
909 {
910 return OGGZ_ERR_DISABLED;
911 }
912
913 long
oggz_seek_units(OGGZ * oggz,ogg_int64_t units,int whence)914 oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence)
915 {
916 return OGGZ_ERR_DISABLED;
917 }
918
919 long
oggz_seek_byorder(OGGZ * oggz,void * target)920 oggz_seek_byorder (OGGZ * oggz, void * target)
921 {
922 return OGGZ_ERR_DISABLED;
923 }
924
925 long
oggz_seek_packets(OGGZ * oggz,long serialno,long packets,int whence)926 oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence)
927 {
928 return OGGZ_ERR_DISABLED;
929 }
930
931 #endif
932