зеркало из https://github.com/mozilla/gecko-dev.git
Bug 463358. Support seeking to keyframes in liboggz. r=doublec
--HG-- extra : rebase_source : a8aa26fe569f1155936b496ecc35317d27a9aae7
This commit is contained in:
Родитель
bd64741ff1
Коммит
dacfca1cb3
|
@ -20,6 +20,8 @@ bug493140: Fix for offsets not being used.
|
|||
|
||||
aspect-ratio: Adds oggplay_get_video_aspect_ratio, used for bug 480058.
|
||||
|
||||
seek_to_key_frame.patch: Adds oggplay_seek_to_keyframe(), as per bug 463358.
|
||||
|
||||
bug488951: Fix for YUV conversion for odd sized frames. Cherrypicked from
|
||||
upstream commits dabde8, 683f23, and 4d7581.
|
||||
|
||||
|
|
|
@ -42,4 +42,15 @@
|
|||
OggPlayErrorCode
|
||||
oggplay_seek(OggPlay *me, ogg_int64_t milliseconds);
|
||||
|
||||
/**
|
||||
* Seeks to key frame before |milliseconds|.
|
||||
*/
|
||||
OggPlayErrorCode
|
||||
oggplay_seek_to_keyframe(OggPlay *me,
|
||||
int* tracks,
|
||||
int num_tracks,
|
||||
ogg_int64_t milliseconds,
|
||||
ogg_int64_t offset_begin,
|
||||
ogg_int64_t offset_end);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
diff --git a/media/liboggplay/include/oggplay/oggplay_seek.h b/media/liboggplay/include/oggplay/oggplay_seek.h
|
||||
--- a/media/liboggplay/include/oggplay/oggplay_seek.h
|
||||
+++ b/media/liboggplay/include/oggplay/oggplay_seek.h
|
||||
@@ -37,9 +37,20 @@
|
||||
*/
|
||||
|
||||
#ifndef __OGGPLAY_SEEK_H__
|
||||
#define __OGGPLAY_SEEK_H__
|
||||
|
||||
OggPlayErrorCode
|
||||
oggplay_seek(OggPlay *me, ogg_int64_t milliseconds);
|
||||
|
||||
+/**
|
||||
+ * Seeks to key frame before |milliseconds|.
|
||||
+ */
|
||||
+OggPlayErrorCode
|
||||
+oggplay_seek_to_keyframe(OggPlay *me,
|
||||
+ int* tracks,
|
||||
+ int num_tracks,
|
||||
+ ogg_int64_t milliseconds,
|
||||
+ ogg_int64_t offset_begin,
|
||||
+ ogg_int64_t offset_end);
|
||||
+
|
||||
#endif
|
||||
diff --git a/media/liboggplay/src/liboggplay/oggplay_seek.c b/media/liboggplay/src/liboggplay/oggplay_seek.c
|
||||
--- a/media/liboggplay/src/liboggplay/oggplay_seek.c
|
||||
+++ b/media/liboggplay/src/liboggplay/oggplay_seek.c
|
||||
@@ -73,16 +73,67 @@ oggplay_seek(OggPlay *me, ogg_int64_t mi
|
||||
}
|
||||
|
||||
oggplay_seek_cleanup(me, milliseconds);
|
||||
|
||||
return E_OGGPLAY_OK;
|
||||
|
||||
}
|
||||
|
||||
+OggPlayErrorCode
|
||||
+oggplay_seek_to_keyframe(OggPlay *me,
|
||||
+ int* tracks,
|
||||
+ int num_tracks,
|
||||
+ ogg_int64_t milliseconds,
|
||||
+ ogg_int64_t offset_begin,
|
||||
+ ogg_int64_t offset_end)
|
||||
+{
|
||||
+ long *serial_nos;
|
||||
+ int i;
|
||||
+ ogg_int64_t eof, time;
|
||||
+
|
||||
+ if (me == NULL) {
|
||||
+ return E_OGGPLAY_BAD_OGGPLAY;
|
||||
+ }
|
||||
+
|
||||
+ if (num_tracks > me->num_tracks || milliseconds < 0)
|
||||
+ return E_OGGPLAY_CANT_SEEK;
|
||||
+
|
||||
+ eof = oggplay_get_duration(me);
|
||||
+ if (eof > -1 && milliseconds > eof) {
|
||||
+ return E_OGGPLAY_CANT_SEEK;
|
||||
+ }
|
||||
+
|
||||
+ // Get the serialnos for the tracks we're seeking.
|
||||
+ serial_nos = (long*)oggplay_malloc(sizeof(long)*num_tracks);
|
||||
+ if (!serial_nos) {
|
||||
+ return E_OGGPLAY_CANT_SEEK;
|
||||
+ }
|
||||
+ for (i=0; i<num_tracks; i++) {
|
||||
+ serial_nos[i] = me->decode_data[tracks[i]]->serialno;
|
||||
+ }
|
||||
+
|
||||
+ time = oggz_keyframe_seek_set(me->oggz,
|
||||
+ serial_nos,
|
||||
+ num_tracks,
|
||||
+ milliseconds,
|
||||
+ offset_begin,
|
||||
+ offset_end);
|
||||
+ oggplay_free(serial_nos);
|
||||
+
|
||||
+ if (time == -1) {
|
||||
+ return E_OGGPLAY_CANT_SEEK;
|
||||
+ }
|
||||
+
|
||||
+ oggplay_seek_cleanup(me, time);
|
||||
+
|
||||
+ return E_OGGPLAY_OK;
|
||||
+
|
||||
+}
|
||||
+
|
||||
void
|
||||
oggplay_seek_cleanup(OggPlay* me, ogg_int64_t milliseconds)
|
||||
{
|
||||
|
||||
OggPlaySeekTrash * trash;
|
||||
OggPlaySeekTrash ** p;
|
||||
OggPlayDataHeader ** end_of_list_p;
|
||||
int i;
|
|
@ -78,6 +78,57 @@ oggplay_seek(OggPlay *me, ogg_int64_t milliseconds) {
|
|||
|
||||
}
|
||||
|
||||
OggPlayErrorCode
|
||||
oggplay_seek_to_keyframe(OggPlay *me,
|
||||
int* tracks,
|
||||
int num_tracks,
|
||||
ogg_int64_t milliseconds,
|
||||
ogg_int64_t offset_begin,
|
||||
ogg_int64_t offset_end)
|
||||
{
|
||||
long *serial_nos;
|
||||
int i;
|
||||
ogg_int64_t eof, time;
|
||||
|
||||
if (me == NULL) {
|
||||
return E_OGGPLAY_BAD_OGGPLAY;
|
||||
}
|
||||
|
||||
if (num_tracks > me->num_tracks || milliseconds < 0)
|
||||
return E_OGGPLAY_CANT_SEEK;
|
||||
|
||||
eof = oggplay_get_duration(me);
|
||||
if (eof > -1 && milliseconds > eof) {
|
||||
return E_OGGPLAY_CANT_SEEK;
|
||||
}
|
||||
|
||||
// Get the serialnos for the tracks we're seeking.
|
||||
serial_nos = (long*)oggplay_malloc(sizeof(long)*num_tracks);
|
||||
if (!serial_nos) {
|
||||
return E_OGGPLAY_CANT_SEEK;
|
||||
}
|
||||
for (i=0; i<num_tracks; i++) {
|
||||
serial_nos[i] = me->decode_data[tracks[i]]->serialno;
|
||||
}
|
||||
|
||||
time = oggz_keyframe_seek_set(me->oggz,
|
||||
serial_nos,
|
||||
num_tracks,
|
||||
milliseconds,
|
||||
offset_begin,
|
||||
offset_end);
|
||||
oggplay_free(serial_nos);
|
||||
|
||||
if (time == -1) {
|
||||
return E_OGGPLAY_CANT_SEEK;
|
||||
}
|
||||
|
||||
oggplay_seek_cleanup(me, time);
|
||||
|
||||
return E_OGGPLAY_OK;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
oggplay_seek_cleanup(OggPlay* me, ogg_int64_t milliseconds)
|
||||
{
|
||||
|
|
|
@ -50,5 +50,7 @@ patch -p3 < bug492436.patch
|
|||
patch -p3 < bug493140.patch
|
||||
patch -p3 < bug481921.patch
|
||||
patch -p3 < aspect_ratio.patch
|
||||
patch -p3 < seek_to_key_frame.patch
|
||||
|
||||
patch -p3 < bug488951.patch
|
||||
patch -p3 < bug488951_yuv_fix.patch
|
||||
|
|
|
@ -12,3 +12,5 @@ see bug 461844 for details.
|
|||
endian.patch is applied to fix bug 452698.
|
||||
|
||||
bounded_seek.patch is applied to fix bug 469408.
|
||||
|
||||
key_frame_seek.patch fixes buf 463358.
|
||||
|
|
|
@ -494,4 +494,17 @@ oggz_bounded_seek_set (OGGZ * oggz,
|
|||
ogg_int64_t offset_begin,
|
||||
ogg_int64_t offset_end);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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__ */
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
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
|
||||
@@ -489,9 +489,22 @@ int oggz_set_data_start (OGGZ * oggz, og
|
||||
* \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);
|
||||
|
||||
+/**
|
||||
+ * 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.
|
||||
+ */
|
||||
+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
|
||||
@@ -935,8 +935,131 @@ 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 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;
|
||||
+ }
|
||||
+ }
|
||||
+ 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;
|
||||
+}
|
||||
+
|
||||
+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;
|
||||
+
|
||||
+ unit_at = oggz_bounded_seek_set(oggz,
|
||||
+ 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);
|
||||
+
|
||||
+ return unit_at;
|
||||
+}
|
|
@ -940,3 +940,126 @@ oggz_seek_packets (OGGZ * oggz, long serialno, long packets, int whence)
|
|||
}
|
||||
|
||||
#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 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
unit_at = oggz_bounded_seek_set(oggz,
|
||||
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);
|
||||
|
||||
return unit_at;
|
||||
}
|
||||
|
|
|
@ -47,3 +47,4 @@ cp $1/AUTHORS ./AUTHORS
|
|||
patch -p3 <wince.patch
|
||||
patch -p3 <endian.patch
|
||||
patch -p3 <bounded_seek.patch
|
||||
patch -p3 <key_frame_seek.patch
|
||||
|
|
Загрузка…
Ссылка в новой задаче