diff --git a/media/liboggz/include/oggz/oggz_seek.h b/media/liboggz/include/oggz/oggz_seek.h --- a/media/liboggz/include/oggz/oggz_seek.h +++ b/media/liboggz/include/oggz/oggz_seek.h @@ -471,40 +471,40 @@ long oggz_seek_byorder (OGGZ * oggz, voi * \param offset The offset of the start of data * \returns 0 on success, -1 on failure. */ int oggz_set_data_start (OGGZ * oggz, oggz_off_t offset); /** \} */ /** - * Seeks Oggz to time unit_target, but with the bounds of the offset range - * [offset_begin, offset_end]. This is useful when seeking in network streams - * where only parts of a media are buffered, and retrieving unbuffered - * parts is expensive. + * Seeks to within fuzz_margin milliseconds of time unit_target, within the + * bounds of the offset range [offset_begin, offset_end]. This is useful when + * seeking in network streams where only parts of a media are buffered, and + * retrieving unbuffered parts is expensive. * \param oggz An OGGZ handle previously opened for reading * \param unit_target The seek target, in milliseconds, or custom units * \param offset_begin Start of offset range to seek inside, in bytes * \param offset_end End of offset range to seek inside, in bytes, pass -1 for end of media + * \param fuzz_margin The seek stops when it's within this many milliseconds + of unit_target * \returns The new position, in milliseconds or custom units * \retval -1 on failure (unit_target is not within range) */ ogg_int64_t oggz_bounded_seek_set (OGGZ * oggz, ogg_int64_t unit_target, ogg_int64_t offset_begin, - ogg_int64_t offset_end); + ogg_int64_t offset_end, + int fuzz_margin); /** - * Seeks to the first key frame before unit_target, in the range - * [offset_begin, offset_end]. serial_nos contains an array of size serial_nos - * of serialnos of the streams which need to be seeked. + * Seeks to before the first key frame before unit_target, in the range + * [offset_begin, offset_end]. */ ogg_int64_t oggz_keyframe_seek_set(OGGZ * oggz, - long* serial_nos, - int num_serialno, ogg_int64_t unit_target, ogg_int64_t offset_begin, ogg_int64_t offset_end); #endif /* __OGGZ_SEEK_H__ */ diff --git a/media/liboggz/src/liboggz/oggz_seek.c b/media/liboggz/src/liboggz/oggz_seek.c --- a/media/liboggz/src/liboggz/oggz_seek.c +++ b/media/liboggz/src/liboggz/oggz_seek.c @@ -491,65 +491,61 @@ oggz_scan_for_page (OGGZ * oggz, ogg_pag } } return offset_at; } #define GUESS_MULTIPLIER (1<<16) -static oggz_off_t +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 offset_begin, ogg_int64_t offset_end) { ogg_int64_t guess_ratio; - oggz_off_t offset_guess; - - if (unit_at == unit_begin) return offset_begin; + ogg_int64_t offset_guess; if (unit_end != -1) { guess_ratio = GUESS_MULTIPLIER * (unit_target - unit_begin) / (unit_end - unit_begin); } else { guess_ratio = GUESS_MULTIPLIER * (unit_target - unit_begin) / (unit_at - unit_begin); } + offset_guess = offset_begin + + (((offset_end - offset_begin) * guess_ratio) / + GUESS_MULTIPLIER); + #ifdef DEBUG - printf ("oggz_seek::guess: guess_ratio %lld = (%lld - %lld) / (%lld - %lld)\n", - guess_ratio, unit_target, unit_begin, unit_at, unit_begin); + printf("guess: [o=%lld t=%lld]-[o=%lld t=%lld] guess_ratio=%lf offset_guess=%lu\n", + offset_begin, unit_begin, offset_end, unit_end, + ((double)guess_ratio / (double)GUESS_MULTIPLIER), offset_guess); #endif - - offset_guess = offset_begin + - (oggz_off_t)(((offset_end - offset_begin) * guess_ratio) / - GUESS_MULTIPLIER); - + return offset_guess; } -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) +static ogg_int64_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) { oggz_off_t offset_guess; - - if (unit_at == unit_begin) { - offset_guess = offset_begin + (offset_end - offset_begin)/2; - } else if (unit_end == -1) { + if (unit_end == -1) { 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_guess: unit_end <= unit_begin (ERROR)\n"); -#endif offset_guess = -1; } else { offset_guess = guess (unit_at, unit_target, unit_begin, unit_end, offset_begin, offset_end); } #ifdef DEBUG printf ("oggz_seek_guess: guessed %" PRI_OGGZ_OFF_T "d\n", offset_guess); @@ -617,21 +613,22 @@ oggz_offset_end (OGGZ * oggz) return offset_end; } ogg_int64_t oggz_bounded_seek_set (OGGZ * oggz, ogg_int64_t unit_target, ogg_int64_t offset_begin, - ogg_int64_t offset_end) + ogg_int64_t offset_end, + int fuzz_margin) { OggzReader * reader; - oggz_off_t offset_orig, offset_at, offset_guess; - oggz_off_t offset_next; + ogg_int64_t offset_orig, offset_at, offset_guess; + ogg_int64_t offset_next; ogg_int64_t granule_at; ogg_int64_t unit_at, unit_begin = -1, unit_end = -1, unit_last_iter = -1; long serialno; ogg_page * og; int hit_eof = 0; if (oggz == NULL) { return -1; @@ -679,27 +676,30 @@ oggz_bounded_seek_set (OGGZ * oggz, ogg_int64_t granulepos; if (oggz_get_prev_start_page (oggz, og, &granulepos, &serialno) >= 0) { unit_end = oggz_get_unit (oggz, serialno, granulepos); } } if (unit_begin == -1 && oggz_seek_raw (oggz, offset_begin, SEEK_SET) >= 0) { - ogg_int64_t granulepos; - if (oggz_get_next_start_page (oggz, og) >= 0) { + ogg_int64_t granulepos = 0; + unit_begin = 0; + // Start time needs to be the end time of the first non-header page. + while (oggz_get_next_start_page (oggz, og) >= 0 && unit_begin <= 0) { serialno = ogg_page_serialno (og); granulepos = ogg_page_granulepos (og); unit_begin = oggz_get_unit (oggz, serialno, granulepos); } } /* Fail if target isn't in specified range. */ - if (unit_target < unit_begin || unit_target > unit_end) + if (unit_target < unit_begin || unit_target > unit_end) { return -1; + } /* Reduce the search range if possible using read cursor position. */ if (unit_at > unit_begin && unit_at < unit_end) { if (unit_target < unit_at) { unit_end = unit_at; offset_end = offset_at; } else { unit_begin = unit_at; @@ -738,16 +738,21 @@ oggz_bounded_seek_set (OGGZ * oggz, offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET); offset_next = oggz_get_next_start_page (oggz, og); serialno = ogg_page_serialno (og); granule_at = ogg_page_granulepos (og); } unit_at = oggz_get_unit (oggz, serialno, granule_at); + if (abs(unit_at - unit_target) < fuzz_margin) { + // Within fuzz_margin of target, stop. + break; + } + #ifdef DEBUG printf ("oggz_bounded_seek_set: offset_next %" PRI_OGGZ_OFF_T "d\n", offset_next); #endif if (unit_at == unit_last_iter) break; #ifdef DEBUG printf ("oggz_bounded_seek_set: [D] want u%lld, got page u%lld @%" PRI_OGGZ_OFF_T "d g%lld\n", unit_target, unit_at, offset_at, granule_at); @@ -761,34 +766,17 @@ oggz_bounded_seek_set (OGGZ * oggz, offset_end = offset_at-1; unit_end = unit_at; if (unit_end == unit_begin) break; } else { break; } } - do { - offset_at = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno); - if (offset_at < 0) - break; - unit_at = oggz_get_unit (oggz, serialno, granule_at); - } while (unit_at > unit_target); - - if (offset_at < 0) { - oggz_reset (oggz, offset_orig, -1, SEEK_SET); - return -1; - } - - offset_at = oggz_reset (oggz, offset_at, unit_at, SEEK_SET); - if (offset_at == -1) return -1; - -#ifdef DEBUG - printf ("oggz_bounded_seek_set: FOUND (%lld)\n", unit_at); -#endif + /* Reader is now approximately at the seek target. */ return (long)reader->current_unit; } static ogg_int64_t oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset) { oggz_off_t offset_orig, offset_at, offset_end; @@ -813,17 +801,17 @@ oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_end = oggz_get_unit (oggz, serialno, granulepos); #ifdef DEBUG printf ("*** oggz_seek_end: found packet (%lld) at @%" PRI_OGGZ_OFF_T "d [%lld]\n", unit_end, offset_end, granulepos); #endif - return oggz_bounded_seek_set (oggz, unit_end + unit_offset, 0, -1); + return oggz_bounded_seek_set (oggz, unit_end + unit_offset, 0, -1, 0); } off_t oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence) { OggzReader * reader; ogg_int64_t units = -1; @@ -872,21 +860,21 @@ oggz_seek_units (OGGZ * oggz, ogg_int64_ #endif return -1; } reader = &oggz->x.reader; switch (whence) { case SEEK_SET: - r = oggz_bounded_seek_set (oggz, units, 0, -1); + r = oggz_bounded_seek_set (oggz, units, 0, -1, 0); break; case SEEK_CUR: units += reader->current_unit; - r = oggz_bounded_seek_set (oggz, units, 0, -1); + r = oggz_bounded_seek_set (oggz, units, 0, -1, 0); break; case SEEK_END: r = oggz_seek_end (oggz, units); break; default: /*oggz_set_error (oggz, OGGZ_EINVALID);*/ r = -1; break; @@ -934,130 +922,70 @@ oggz_seek_byorder (OGGZ * oggz, void * t long oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence) { return OGGZ_ERR_DISABLED; } #endif -// Returns 1 if any of the elements of array |a|, which is of length |n|, -// contain the value |val|. Otherwise returns 0. -static int -is_any(ogg_int64_t* a, int n, ogg_int64_t val) -{ - int i; - for (i=0; igranuleshift == 0) + return 0; + + // Max number of frames keyframe could possibly be offset. + keyframe_diff = (1 << stream->granuleshift) - 1; + + // Length of frame in ms. + frame_duration = stream->granulerate_d / stream->granulerate_n; + + return frame_duration * keyframe_diff; } -// Returns the index of the element in array |a|, which is of length |n|, -// which contains the value |val|, or -1 of it's not present. -static int -find(long* a, int n, ogg_int64_t val) -{ - int i; - for (i=0; istreams); + for (i = 0; i < size; i++) { + stream = (oggz_stream_t *)oggz_vector_nth_p (oggz->streams, i); + if (!stream) continue; + x = get_keyframe_offset(stream); + if (x > max) + max = x; } - return -1; -} - -// Returns the element with the smallest value in array |a|, which is -// of length |n|. -static ogg_int64_t -minimum(ogg_int64_t* a, int n) { - ogg_int64_t m = 0x7FFFFFFFFFFFFFFF; - int i; - for (i=0; ioffset; - - key_frames = oggz_malloc(sizeof(ogg_int64_t) * num_serialno); - if (!key_frames) { - // Malloc failure. We can still exit with the seek finishing at a non - // key frame. - return unit_at; - } - memset(key_frames, -1, sizeof(ogg_int64_t) * num_serialno); - - // Find the key frame offset for every stream. - og = &oggz->current_page; - while (is_any(key_frames, num_serialno, -1)) { - do { - offset_next = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno); - if (offset_next <= 0 || granule_at == 0) { - // At beginning of file, or some other failure. Return with - // non-key frame seek if possible. - oggz_free(key_frames); - offset_at = oggz_reset (oggz, offset_at, unit_at, SEEK_SET); - return (offset_at == -1) ? -1 : unit_at; - } - } while (granule_at < 0); - - idx = find(serial_nos, num_serialno, serialno); - if (idx == -1 || key_frames[idx] != -1) - continue; - - granule_shift = oggz_get_granuleshift(oggz, serialno); - key_granule_at = (granule_at >> granule_shift) << granule_shift; - key_unit_at = oggz_get_unit(oggz, serialno, key_granule_at); - - if (key_unit_at < unit_target) - key_frames[idx] = key_unit_at; - } - - // Seek to 100ms before the earliest of all the streams' key frames. - // This is so that after the seek, the decoder will defintately return frames - // at or before get the key frame. Without this, some decoders will return - // frames which start after the specified time - after the key frame. - key_unit_at = minimum(key_frames, num_serialno); - unit_at = oggz_bounded_seek_set(oggz, - MAX((key_unit_at - 100), 0), - offset_begin, - offset_end); - oggz_free(key_frames); - + offset_end, + fuzz); return unit_at; }