Commit 3ba3cf90 authored by conrad's avatar conrad

cleanup -- move all seeking code out to oggz_seek.c


git-svn-id: http://svn.annodex.net/liboggz/trunk@806 8158c8cd-e7e1-0310-9fa4-c5954c97daef
parent db3d09cf
......@@ -14,6 +14,7 @@ liboggz_la_SOURCES = \
oggz_private.h oggz_byteorder.h oggz_compat.h oggz_macros.h \
oggz_io.c \
oggz_read.c oggz_write.c \
oggz_seek.c \
oggz_auto.c oggz_auto.h \
oggz_stream.c oggz_stream.h \
oggz_table.c \
......
......@@ -505,766 +505,6 @@ oggz_read_input (OGGZ * oggz, unsigned char * buf, long n)
return nread;
}
/* oggz_seek() (and oggz_purge()) related functions from here down */
/*
* The typical usage is:
*
* oggz_set_data_start (oggz, oggz_tell (oggz));
*/
int
oggz_set_data_start (OGGZ * oggz, oggz_off_t offset)
{
if (oggz == NULL) return -1;
if (offset < 0) return -1;
oggz->offset_data_begin = offset;
return 0;
}
static oggz_off_t
oggz_tell_raw (OGGZ * oggz)
{
oggz_off_t offset_at;
offset_at = oggz_io_tell (oggz);
return offset_at;
}
/*
* seeks and syncs
*/
static oggz_off_t
oggz_seek_raw (OGGZ * oggz, oggz_off_t offset, int whence)
{
OggzReader * reader = &oggz->x.reader;
oggz_off_t offset_at;
if (oggz_io_seek (oggz, offset, whence) == -1) {
return -1;
}
offset_at = oggz_io_tell (oggz);
oggz->offset = offset_at;
ogg_sync_reset (&reader->ogg_sync);
return offset_at;
}
static int
oggz_stream_reset (void * data)
{
oggz_stream_t * stream = (oggz_stream_t *) data;
if (stream->ogg_stream.serialno != -1) {
ogg_stream_reset (&stream->ogg_stream);
}
return 0;
}
static void
oggz_reset_streams (OGGZ * oggz)
{
oggz_vector_foreach (oggz->streams, oggz_stream_reset);
}
static long
oggz_reset_seek (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence)
{
OggzReader * reader = &oggz->x.reader;
oggz_off_t offset_at;
offset_at = oggz_seek_raw (oggz, offset, whence);
if (offset_at == -1) return -1;
oggz->offset = offset_at;
#ifdef DEBUG
printf ("reset to %ld\n", offset_at);
#endif
if (unit != -1) reader->current_unit = unit;
return offset_at;
}
static long
oggz_reset (OGGZ * oggz, oggz_off_t offset, ogg_int64_t unit, int whence)
{
oggz_reset_streams (oggz);
return oggz_reset_seek (oggz, offset, unit, whence);
}
int
oggz_purge (OGGZ * oggz)
{
if (oggz == NULL) return OGGZ_ERR_BAD_OGGZ;
if (oggz->flags & OGGZ_WRITE) {
return OGGZ_ERR_INVALID;
}
oggz_reset_streams (oggz);
if (oggz->file && oggz_reset (oggz, oggz->offset, -1, SEEK_SET) < 0) {
return OGGZ_ERR_SYSTEM;
}
return 0;
}
/*
* oggz_get_next_page (oggz, og, do_read)
*
* retrieves the next page.
* returns >= 0 if found; return value is offset of page start
* returns -1 on error
* returns -2 if EOF was encountered
*/
static oggz_off_t
oggz_get_next_page (OGGZ * oggz, ogg_page * og)
{
OggzReader * reader = &oggz->x.reader;
char * buffer;
long bytes = 0, more;
oggz_off_t page_offset = 0, ret;
int found = 0;
do {
more = ogg_sync_pageseek (&reader->ogg_sync, og);
if (more == 0) {
page_offset = 0;
buffer = ogg_sync_buffer (&reader->ogg_sync, CHUNKSIZE);
if ((bytes = (long) oggz_io_read (oggz, buffer, CHUNKSIZE)) == 0) {
if (oggz->file && feof (oggz->file)) {
#ifdef DEBUG_VERBOSE
printf ("get_next_page: feof (oggz->file), returning -2\n");
#endif
clearerr (oggz->file);
return -2;
}
}
if (bytes == OGGZ_ERR_SYSTEM) {
/*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
return -1;
}
if (bytes == 0) {
#ifdef DEBUG_VERBOSE
printf ("get_next_page: bytes == 0, returning -2\n");
#endif
return -2;
#if 0
} else if (oggz->file && feof (oggz->file)) {
#ifdef DEBUG_VERBOSE
printf ("get_next_page: feof (oggz->file), returning -2\n");
#endif
clearerr (oggz->file);
return -2;
#endif
}
ogg_sync_wrote(&reader->ogg_sync, bytes);
} else if (more < 0) {
#ifdef DEBUG_VERBOSE
printf ("get_next_page: skipped %ld bytes\n", -more);
#endif
page_offset -= more;
} else {
#ifdef DEBUG_VERBOSE
printf ("get_next_page: page has %ld bytes\n", more);
#endif
found = 1;
}
} while (!found);
/* Calculate the byte offset of the page which was found */
if (bytes > 0) {
oggz->offset = oggz_tell_raw (oggz) - bytes + page_offset;
ret = oggz->offset;
} else {
/* didn't need to do any reading -- accumulate the page_offset */
ret = oggz->offset + page_offset;
oggz->offset += page_offset + more;
}
return ret;
}
static oggz_off_t
oggz_get_next_start_page (OGGZ * oggz, ogg_page * og)
{
oggz_off_t page_offset;
int found = 0;
do {
page_offset = oggz_get_next_page (oggz, og);
/* Return this value if one of the following conditions is met:
*
* page_offset < 0 : error or EOF
* page_offset == 0 : start of stream
* !ogg_page_continued : page contains start of a packet
* ogg_page_packets > 1: page contains start of a packet
*/
if (page_offset <= 0 || !ogg_page_continued (og) ||
ogg_page_packets (og) > 1)
found = 1;
}
while (!found);
return page_offset;
}
static oggz_off_t
oggz_get_prev_start_page (OGGZ * oggz, ogg_page * og,
ogg_int64_t * granule, long * serialno)
{
oggz_off_t offset_at, offset_start;
oggz_off_t page_offset, prev_offset = 0;
ogg_int64_t unit_at;
long granule_at = -1;
#if 0
offset_at = oggz_tell_raw (oggz);
if (offset_at == -1) return -1;
#else
offset_at = oggz->offset;
#endif
offset_start = offset_at;
do {
offset_start = offset_at - CHUNKSIZE;
if (offset_start < 0) offset_start = 0;
offset_start = oggz_seek_raw (oggz, offset_start, SEEK_SET);
if (offset_start == -1) return -1;
#ifdef DEBUG
printf ("get_prev_start_page: [A] offset_at: @%ld\toffset_start: @%ld\n",
offset_at, offset_start);
printf ("get_prev_start_page: seeked to %ld\n", offset_start);
#endif
page_offset = 0;
do {
prev_offset = page_offset;
page_offset = oggz_get_next_start_page (oggz, og);
if (page_offset == -1) return -1;
if (page_offset == -2) {
#ifdef DEBUG
printf ("*** get_prev_start_page: page_offset = -2\n");
#endif
break;
}
granule_at = (long)ogg_page_granulepos (og);
#ifdef DEBUG_VERBOSE
printf ("get_prev_start_page: GOT page (%ld) @%ld\tat @%ld\n",
granule_at, page_offset, offset_at);
#endif
/* Need to stash the granule and serialno of this page because og
* will be overwritten by the time we realise this was the desired
* prev page */
if (page_offset >= 0 && page_offset < offset_at) {
*granule = granule_at;
*serialno = ogg_page_serialno (og);
}
} while (page_offset >= 0 && page_offset < offset_at);
#ifdef DEBUG
printf ("get_prev_start_page: [B] offset_at: @%ld\toffset_start: @%ld\n"
"prev_offset: @%ld\tpage_offset: @%ld\n",
offset_at, offset_start, prev_offset, page_offset);
#endif
/* reset the file offset */
offset_at = offset_start;
} while (offset_at > 0 && prev_offset == 0);
unit_at = oggz_get_unit (oggz, *serialno, *granule);
offset_at = oggz_reset (oggz, prev_offset, unit_at, SEEK_SET);
#ifdef DEBUG
printf ("get_prev_start_page: [C] offset_at: @%ld\t"
"prev_offset: @%ld\tunit_at: %lld\n",
offset_at, prev_offset, unit_at);
#endif
if (offset_at == -1) return -1;
if (offset_at > 0)
return prev_offset;
else
return -1;
}
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)
{
oggz_off_t offset_at, offset_next;
oggz_off_t offset_prev = -1;
ogg_int64_t granule_at;
ogg_int64_t unit_at;
long serialno;
#ifdef DEBUG
printf (" SCANNING from %ld...", offset_begin);
#endif
for ( ; ; ) {
offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET);
if (offset_at == -1) return -1;
#ifdef DEBUG
printf (" scan @%ld\n", offset_at);
#endif
offset_next = oggz_get_next_start_page (oggz, og);
if (offset_next < 0) {
return offset_next;
}
if (offset_next == 0 && offset_begin != 0) {
#ifdef DEBUG
printf (" ... scanned past EOF\n");
#endif
return -1;
}
if (offset_next > offset_end) {
#ifdef DEBUG
printf (" ... scanned to page %ld\n", (long)ogg_page_granulepos (og));
#endif
if (offset_prev != -1) {
offset_at = oggz_seek_raw (oggz, offset_prev, SEEK_SET);
if (offset_at == -1) return -1;
offset_next = oggz_get_next_start_page (oggz, og);
if (offset_next < 0) return offset_next;
serialno = ogg_page_serialno (og);
granule_at = ogg_page_granulepos (og);
unit_at = oggz_get_unit (oggz, serialno, granule_at);
return offset_at;
} else {
return -1;
}
}
offset_at = offset_next;
serialno = ogg_page_serialno (og);
granule_at = ogg_page_granulepos (og);
unit_at = oggz_get_unit (oggz, serialno, granule_at);
if (unit_at < unit_target) {
#ifdef DEBUG
printf (" scan: (%lld) < (%lld)\n", unit_at, unit_target);
#endif
offset_prev = offset_next;
offset_begin = offset_next+1;
} else if (unit_at > unit_target) {
#ifdef DEBUG
printf (" scan: (%lld) > (%lld)\n", unit_at, unit_target);
#endif
#if 0
/* hole ? */
offset_at = oggz_seek_raw (oggz, offset_begin, SEEK_SET);
if (offset_at == -1) return -1;
offset_next = oggz_get_next_start_page (oggz, og);
if (offset_next < 0) return offset_next;
serialno = ogg_page_serialno (og);
granule_at = ogg_page_granulepos (og);
unit_at = oggz_get_unit (oggz, serialno, granule_at);
break;
#else
return offset_at;
#endif
} else if (unit_at == unit_target) {
#ifdef DEBUG
printf (" scan: (%lld) == (%lld)\n", unit_at, unit_target);
#endif
break;
}
}
return offset_at;
}
#define GUESS_MULTIPLIER (1<<16)
static ogg_int64_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)
{
ogg_int64_t guess_ratio;
oggz_off_t offset_guess;
if (unit_at == unit_begin) return offset_begin;
guess_ratio =
GUESS_MULTIPLIER * (unit_target - unit_begin) /
(unit_at - unit_begin);
#ifdef DEBUG
printf ("oggz_seek::guess: guess_ratio %lld = (%lld - %lld) / (%lld - %lld)\n",
guess_ratio, unit_target, unit_begin, unit_at, unit_begin);
#endif
offset_guess = offset_begin +
(oggz_off_t)(((offset_end - offset_begin) * guess_ratio) /
GUESS_MULTIPLIER);
return offset_guess;
}
static ogg_int64_t
oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
{
OggzReader * reader = &oggz->x.reader;
int fd;
struct stat statbuf;
oggz_off_t offset_orig, offset_at, offset_guess;
oggz_off_t offset_begin, offset_end = -1, offset_next;
ogg_int64_t granule_at;
ogg_int64_t unit_at, unit_begin = 0, unit_end = -1, unit_last_iter = -1;
long serialno;
ogg_page * og;
int looping = 0;
if (oggz == NULL) {
return -1;
}
if (unit_target > 0 && !oggz_has_metrics (oggz)) {
#ifdef DEBUG
printf ("oggz_seek_set: No metric defined, FAIL\n");
#endif
return -1;
}
if (oggz->file != NULL) {
if ((fd = fileno (oggz->file)) == -1) {
/*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
return -1;
}
if (fstat (fd, &statbuf) == -1) {
/*oggz_set_error (oggz, OGGZ_ERR_SYSTEM);*/
return -1;
}
if (oggz_stat_regular (statbuf.st_mode)) {
offset_end = statbuf.st_size;
#ifdef DEBUG
printf ("oggz_seek_set: stat size %ld\n", offset_end);
#endif
} else {
/*oggz_set_error (oggz, OGGZ_ERR_NOSEEK);*/
/* XXX: should be able to just carry on and guess, as per io */
/*return -1;*/
}
} else {
oggz_off_t offset_save;
if (oggz->io == NULL || oggz->io->seek == NULL) {
/* No file, and no io seek method */
return -1;
}
/* Get the offset of the end by querying the io seek method */
offset_save = oggz_io_tell (oggz);
if (oggz_io_seek (oggz, 0, SEEK_END) == -1) {
return -1;
}
offset_end = oggz_io_tell (oggz);
if (oggz_io_seek (oggz, offset_save, SEEK_SET) == -1) {
return -1; /* fubar */
}
}
if (unit_target == reader->current_unit) {
return (long)reader->current_unit;
}
if (unit_target == 0) {
offset_at = oggz_reset (oggz, oggz->offset_data_begin, 0, SEEK_SET);
if (offset_at == -1) return -1;
return 0;
}
offset_at = oggz_tell_raw (oggz);
if (offset_at == -1) return -1;
offset_orig = oggz->offset;
offset_begin = 0;
unit_at = reader->current_unit;
unit_begin = 0;
unit_end = -1;
og = &oggz->current_page;
for ( ; ; ) {
unit_last_iter = unit_at;
#ifdef DEBUG
printf ("oggz_seek_set: [A] want u%lld: (u%lld - u%lld) [@%ld - @%ld]\n",
unit_target, unit_begin, unit_end, offset_begin, offset_end);
#endif
if (unit_end == -1) {
if (unit_at == unit_begin) {
offset_guess = offset_begin + (offset_end - offset_begin)/2;
} else {
offset_guess = guess (unit_at, unit_target, unit_begin, unit_end,
offset_begin, offset_at);
}
} else if (unit_end == unit_begin) {
#ifdef DEBUG
printf ("oggz_seek_set: unit_end == unit_begin (FOUND)\n");
#endif
goto found;
} else if (unit_end <= unit_begin) {
#ifdef DEBUG
printf ("oggz_seek_set: unit_end <= unit_begin (ERROR)\n");
#endif
break;
} else {
offset_guess = guess (unit_at, unit_target, unit_begin, unit_end,
offset_begin, offset_end);
}
#ifdef DEBUG
printf ("oggz_seek_set: guessed %ld\n", offset_guess);
#endif
if (offset_guess == offset_at) {
/* Already there, looping */
looping = 1;
}
offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET);
if (offset_at == -1) {
goto notfound;
}
offset_next = oggz_get_next_start_page (oggz, og);
#ifdef DEBUG
printf ("oggz_seek_set: offset_next %ld\n", offset_next);
#endif
if (unit_end == -1 && offset_next == -2) { /* reached eof, backtrack */
offset_next = oggz_get_prev_start_page (oggz, og, &granule_at,
&serialno);
unit_end = oggz_get_unit (oggz, serialno, granule_at);
#ifdef DEBUG
printf ("oggz_seek_set: [C] offset_next @%ld, g%lld, (s%ld)\n",
offset_next, granule_at, serialno);
printf ("oggz_seek_set: [c] u%lld\n",
oggz_get_unit (oggz, serialno, granule_at));
#endif
} else {
serialno = ogg_page_serialno (og);
granule_at = ogg_page_granulepos (og);
}
if (offset_next < 0) {
goto notfound;
}
if (offset_next > offset_end) {
offset_next =
oggz_scan_for_page (oggz, og, unit_target, offset_begin, offset_end);
if (offset_next < 0) goto notfound;
offset_at = offset_next;
serialno = ogg_page_serialno (og);
granule_at = ogg_page_granulepos (og);
unit_at = oggz_get_unit (oggz, serialno, granule_at);
goto found;
}