gecko-dev/media/liboggz/faster_seek.patch

482 lines
15 KiB
Diff

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; i<n; i++) {
- if (a[i] == val) {
- return 1;
- }
- }
- return 0;
+// Returns the maximum time in milliseconds by which a key frame could be
+// offset for a given stream. Ogg granulepos encode time as:
+// ((key_frame_number << granule_shift) + frame_offset).
+// Therefore the maximum possible time by which any frame could be offset
+// from a keyframe is the duration of (1 << granule_shift) - 1) frames.
+static ogg_int64_t
+get_keyframe_offset(oggz_stream_t* stream) {
+ ogg_int64_t frame_duration;
+ ogg_int64_t keyframe_diff;
+
+ if (stream->granuleshift == 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; i<n; i++) {
- if (a[i] == val) {
- return i;
- }
+// Returns the maximum possible time by which a keyframe could be offset in
+// milliseconds, for all streams in the media.
+static ogg_int64_t
+get_max_keyframe_offset(OGGZ* oggz) {
+ int i=0, size = 0, max_gshift = 0;
+ ogg_int64_t max = 0, x;
+ oggz_stream_t* stream = 0;
+ size = oggz_vector_size (oggz->streams);
+ 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; i<n; i++) {
- if (a[i] < m) {
- m = a[i];
- }
- }
- return m;
+ return max;
}
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)
{
- oggz_off_t offset_at;
- oggz_off_t offset_next;
- ogg_int64_t granule_at;
ogg_int64_t unit_at;
- ogg_int64_t key_granule_at, key_unit_at;
- long serialno;
- ogg_page * og;
- int granule_shift = 0, idx;
- ogg_int64_t* key_frames = 0;
-
+ ogg_int64_t max_keyframe_offset;
+ const ogg_int64_t fuzz = 500;
+ ogg_int64_t keyframe_unit_target;
+
+ max_keyframe_offset = get_max_keyframe_offset(oggz);
+
+ keyframe_unit_target = MAX(0, unit_target - max_keyframe_offset - fuzz);
+
unit_at = oggz_bounded_seek_set(oggz,
- unit_target,
+ keyframe_unit_target,
offset_begin,
- offset_end);
- // Time isn't in the specified offset range, fail.
- if (unit_at == -1)
- return -1;
-
- // We've seeked to beginning, we're at a key frame.
- if (unit_at == 0)
- return 0;
-
- // Backup this, in case we need to fail.
- offset_at = oggz->offset;
-
- 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;
}