mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-05 12:05:22 +00:00
482 lines
15 KiB
Diff
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;
|
|
}
|