mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 469408. Make seeking in bufferred ranges fast by trying a 'bounded seek' in each buffered data range before falling back to a seek over the whole resource. r=doublec,sr=roc
This commit is contained in:
parent
c8a063a105
commit
0719504584
@ -296,7 +296,18 @@ public:
|
||||
// Returns the end of the bytes starting at the given offset
|
||||
// which are in cache.
|
||||
PRInt64 GetCachedDataEnd(PRInt64 aOffset);
|
||||
// XXX we may need to add GetUncachedDataEnd at some point.
|
||||
// Returns the offset of the first byte of cached data at or after aOffset,
|
||||
// or -1 if there is no such cached data.
|
||||
PRInt64 GetNextCachedData(PRInt64 aOffset);
|
||||
|
||||
// Reads from buffered data only. Will fail if not all data to be read is
|
||||
// in the cache. Will not mark blocks as read. Can be called from the main
|
||||
// thread. It's the caller's responsibility to wrap the call in a pin/unpin,
|
||||
// and also to check that the range they want is cached before calling this.
|
||||
nsresult ReadFromCache(char* aBuffer,
|
||||
PRInt64 aOffset,
|
||||
PRInt64 aCount);
|
||||
|
||||
// IsDataCachedToEndOfStream returns true if all the data from
|
||||
// aOffset to the end of the stream (the server-reported end, if the
|
||||
// real end is not known) is in cache. If we know nothing about the
|
||||
@ -371,6 +382,11 @@ private:
|
||||
// This method assumes that the cache monitor is held and can be called on
|
||||
// any thread.
|
||||
PRInt64 GetCachedDataEndInternal(PRInt64 aOffset);
|
||||
// Returns the offset of the first byte of cached data at or after aOffset,
|
||||
// or -1 if there is no such cached data.
|
||||
// This method assumes that the cache monitor is held and can be called on
|
||||
// any thread.
|
||||
PRInt64 GetNextCachedDataInternal(PRInt64 aOffset);
|
||||
// A helper function to do the work of closing the stream. Assumes
|
||||
// that the cache monitor is held. Main thread only.
|
||||
// aMonitor is the nsAutoMonitor wrapper holding the cache monitor.
|
||||
|
@ -228,6 +228,9 @@ public:
|
||||
// header and give us more or less data than it reported. We will adjust
|
||||
// the result of GetLength to reflect the data that's actually arriving.
|
||||
virtual PRInt64 GetLength() = 0;
|
||||
// Returns the offset of the first byte of cached data at or after aOffset,
|
||||
// or -1 if there is no such cached data.
|
||||
virtual PRInt64 GetNextCachedData(PRInt64 aOffset) = 0;
|
||||
// Returns the end of the bytes starting at the given offset
|
||||
// which are in cache.
|
||||
virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset) = 0;
|
||||
@ -335,6 +338,7 @@ public:
|
||||
virtual void Unpin();
|
||||
virtual double GetDownloadRate(PRPackedBool* aIsReliable);
|
||||
virtual PRInt64 GetLength();
|
||||
virtual PRInt64 GetNextCachedData(PRInt64 aOffset);
|
||||
virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset);
|
||||
virtual PRBool IsDataCachedToEndOfStream(PRInt64 aOffset);
|
||||
virtual PRBool IsSuspendedByCache();
|
||||
|
@ -43,6 +43,12 @@
|
||||
#include "nsChannelReader.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
||||
// The minimum size buffered byte range inside which we'll consider
|
||||
// trying a bounded-seek. When we seek, we first try to seek inside all
|
||||
// buffered ranges larger than this, and if they all fail we fall back to
|
||||
// an unbounded seek over the whole media. 64K is approximately 16 pages.
|
||||
#define MIN_BOUNDED_SEEK_SIZE (64 * 1024)
|
||||
|
||||
OggPlayErrorCode nsChannelReader::initialise(int aBlock)
|
||||
{
|
||||
return E_OGGPLAY_OK;
|
||||
@ -131,6 +137,70 @@ static ogg_int64_t oggplay_channel_reader_duration(struct _OggPlayReader *aReade
|
||||
return me->duration();
|
||||
}
|
||||
|
||||
class ByteRange {
|
||||
public:
|
||||
ByteRange() : mStart(-1), mEnd(-1) {}
|
||||
ByteRange(PRInt64 aStart, PRInt64 aEnd) : mStart(aStart), mEnd(aEnd) {}
|
||||
PRInt64 mStart, mEnd;
|
||||
};
|
||||
|
||||
static void GetBufferedBytes(nsMediaStream* aStream, nsTArray<ByteRange>& aRanges)
|
||||
{
|
||||
PRInt64 startOffset = 0;
|
||||
while (PR_TRUE) {
|
||||
PRInt64 endOffset = aStream->GetCachedDataEnd(startOffset);
|
||||
if (endOffset == startOffset) {
|
||||
// Uncached at startOffset.
|
||||
endOffset = aStream->GetNextCachedData(startOffset);
|
||||
if (endOffset == -1) {
|
||||
// Uncached at startOffset until endOffset of stream, or we're at
|
||||
// the end of stream.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Bytes [startOffset..endOffset] are cached.
|
||||
PRInt64 cachedLength = endOffset - startOffset;
|
||||
// Only bother trying to seek inside ranges greater than
|
||||
// MIN_BOUNDED_SEEK_SIZE, so that the bounded seek is unlikely to
|
||||
// read outside of the range when finding Ogg page boundaries.
|
||||
if (cachedLength > MIN_BOUNDED_SEEK_SIZE) {
|
||||
aRanges.AppendElement(ByteRange(startOffset, endOffset));
|
||||
}
|
||||
}
|
||||
startOffset = endOffset;
|
||||
}
|
||||
}
|
||||
|
||||
OggPlayErrorCode oggplay_channel_reader_seek(struct _OggPlayReader *me,
|
||||
OGGZ *oggz,
|
||||
ogg_int64_t aTargetMs)
|
||||
{
|
||||
nsChannelReader* reader = static_cast<nsChannelReader*>(me);
|
||||
nsMediaStream* stream = reader->Stream();
|
||||
nsAutoTArray<ByteRange, 16> ranges;
|
||||
stream->Pin();
|
||||
GetBufferedBytes(stream, ranges);
|
||||
PRInt64 rv = -1;
|
||||
for (PRUint32 i = 0; rv == -1 && i < ranges.Length(); i++) {
|
||||
rv = oggz_bounded_seek_set(oggz,
|
||||
aTargetMs,
|
||||
ranges[i].mStart,
|
||||
ranges[i].mEnd);
|
||||
}
|
||||
stream->Unpin();
|
||||
|
||||
if (rv == -1) {
|
||||
// Could not seek in a buffered range, fall back to seeking over the
|
||||
// entire media.
|
||||
rv = oggz_bounded_seek_set(oggz,
|
||||
aTargetMs,
|
||||
0,
|
||||
stream->GetLength());
|
||||
}
|
||||
return (rv == -1) ? E_OGGPLAY_CANT_SEEK : E_OGGPLAY_OK;
|
||||
|
||||
}
|
||||
|
||||
nsresult nsChannelReader::Init(nsMediaDecoder* aDecoder, nsIURI* aURI,
|
||||
nsIChannel* aChannel,
|
||||
nsIStreamListener** aStreamListener)
|
||||
@ -151,7 +221,7 @@ nsChannelReader::nsChannelReader() :
|
||||
OggPlayReader* reader = this;
|
||||
reader->initialise = &oggplay_channel_reader_initialise;
|
||||
reader->destroy = &oggplay_channel_reader_destroy;
|
||||
reader->seek = nsnull;
|
||||
reader->seek = &oggplay_channel_reader_seek;
|
||||
reader->io_read = &oggplay_channel_reader_io_read;
|
||||
reader->io_seek = &oggplay_channel_reader_io_seek;
|
||||
reader->io_tell = &oggplay_channel_reader_io_tell;
|
||||
|
@ -1588,6 +1588,13 @@ nsMediaCacheStream::GetLength()
|
||||
return mStreamLength;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsMediaCacheStream::GetNextCachedData(PRInt64 aOffset)
|
||||
{
|
||||
nsAutoMonitor mon(gMediaCache->Monitor());
|
||||
return GetNextCachedDataInternal(aOffset);
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsMediaCacheStream::GetCachedDataEnd(PRInt64 aOffset)
|
||||
{
|
||||
@ -1607,6 +1614,7 @@ nsMediaCacheStream::IsDataCachedToEndOfStream(PRInt64 aOffset)
|
||||
PRInt64
|
||||
nsMediaCacheStream::GetCachedDataEndInternal(PRInt64 aOffset)
|
||||
{
|
||||
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(gMediaCache->Monitor());
|
||||
PRUint32 startBlockIndex = aOffset/BLOCK_SIZE;
|
||||
PRUint32 blockIndex = startBlockIndex;
|
||||
while (blockIndex < mBlocks.Length() && mBlocks[blockIndex] != -1) {
|
||||
@ -1626,6 +1634,53 @@ nsMediaCacheStream::GetCachedDataEndInternal(PRInt64 aOffset)
|
||||
return PR_MAX(result, aOffset);
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsMediaCacheStream::GetNextCachedDataInternal(PRInt64 aOffset)
|
||||
{
|
||||
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(gMediaCache->Monitor());
|
||||
if (aOffset == mStreamLength)
|
||||
return -1;
|
||||
|
||||
PRUint32 startBlockIndex = aOffset/BLOCK_SIZE;
|
||||
PRUint32 channelBlockIndex = mChannelOffset/BLOCK_SIZE;
|
||||
|
||||
if (startBlockIndex == channelBlockIndex &&
|
||||
aOffset < mChannelOffset) {
|
||||
// The block containing mChannelOffset is partially read, but not
|
||||
// yet committed to the main cache. aOffset lies in the partially
|
||||
// read portion, thus it is effectively cached.
|
||||
return aOffset;
|
||||
}
|
||||
|
||||
if (startBlockIndex >= mBlocks.Length())
|
||||
return -1;
|
||||
|
||||
// Is the current block cached?
|
||||
if (mBlocks[startBlockIndex] != -1)
|
||||
return aOffset;
|
||||
|
||||
// Count the number of uncached blocks
|
||||
PRBool hasPartialBlock = (mChannelOffset % BLOCK_SIZE) != 0;
|
||||
PRUint32 blockIndex = startBlockIndex + 1;
|
||||
while (PR_TRUE) {
|
||||
if ((hasPartialBlock && blockIndex == channelBlockIndex) ||
|
||||
(blockIndex < mBlocks.Length() && mBlocks[blockIndex] != -1)) {
|
||||
// We at the incoming channel block, which has has data in it,
|
||||
// or are we at a cached block. Return index of block start.
|
||||
return blockIndex * BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// No more cached blocks?
|
||||
if (blockIndex >= mBlocks.Length())
|
||||
return -1;
|
||||
|
||||
++blockIndex;
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Should return in loop");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaCacheStream::SetReadMode(ReadMode aMode)
|
||||
{
|
||||
@ -1721,7 +1776,7 @@ nsMediaCacheStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
PRUint32 channelBlock = PRUint32(mChannelOffset/BLOCK_SIZE);
|
||||
PRInt32 cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
|
||||
if (channelBlock == streamBlock && mStreamOffset < mChannelOffset) {
|
||||
// We can just use the data in mPartialBuffer. In fact we should
|
||||
// We can just use the data in mPartialBlockBuffer. In fact we should
|
||||
// use it rather than waiting for the block to fill and land in
|
||||
// the cache.
|
||||
bytes = PR_MIN(size, mChannelOffset - mStreamOffset);
|
||||
@ -1775,6 +1830,61 @@ nsMediaCacheStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaCacheStream::ReadFromCache(char* aBuffer,
|
||||
PRInt64 aOffset,
|
||||
PRInt64 aCount)
|
||||
{
|
||||
nsAutoMonitor mon(gMediaCache->Monitor());
|
||||
if (mClosed)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Read one block (or part of a block) at a time
|
||||
PRUint32 count = 0;
|
||||
PRInt64 streamOffset = aOffset;
|
||||
while (count < aCount) {
|
||||
PRUint32 streamBlock = PRUint32(streamOffset/BLOCK_SIZE);
|
||||
PRUint32 offsetInStreamBlock =
|
||||
PRUint32(streamOffset - streamBlock*BLOCK_SIZE);
|
||||
PRInt32 size = PR_MIN(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
|
||||
|
||||
if (mStreamLength >= 0) {
|
||||
// Don't try to read beyond the end of the stream
|
||||
PRInt64 bytesRemaining = mStreamLength - streamOffset;
|
||||
if (bytesRemaining <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
size = PR_MIN(size, PRInt32(bytesRemaining));
|
||||
}
|
||||
|
||||
PRInt32 bytes;
|
||||
PRUint32 channelBlock = PRUint32(mChannelOffset/BLOCK_SIZE);
|
||||
PRInt32 cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
|
||||
if (channelBlock == streamBlock && streamOffset < mChannelOffset) {
|
||||
// We can just use the data in mPartialBlockBuffer. In fact we should
|
||||
// use it rather than waiting for the block to fill and land in
|
||||
// the cache.
|
||||
bytes = PR_MIN(size, mChannelOffset - streamOffset);
|
||||
memcpy(aBuffer + count,
|
||||
reinterpret_cast<char*>(mPartialBlockBuffer) + offsetInStreamBlock, bytes);
|
||||
} else {
|
||||
if (cacheBlock < 0) {
|
||||
// We expect all blocks to be cached! Fail!
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PRInt64 offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
|
||||
nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, size, &bytes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
streamOffset += bytes;
|
||||
count += bytes;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaCacheStream::Init()
|
||||
{
|
||||
|
@ -596,6 +596,12 @@ nsMediaChannelStream::CacheClientResume()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsMediaChannelStream::GetNextCachedData(PRInt64 aOffset)
|
||||
{
|
||||
return mCacheStream.GetNextCachedData(aOffset);
|
||||
}
|
||||
|
||||
PRInt64
|
||||
nsMediaChannelStream::GetCachedDataEnd(PRInt64 aOffset)
|
||||
{
|
||||
@ -693,6 +699,10 @@ public:
|
||||
return 100*1024*1024; // arbitray, use 100MB/s
|
||||
}
|
||||
virtual PRInt64 GetLength() { return mSize; }
|
||||
virtual PRInt64 GetNextCachedData(PRInt64 aOffset)
|
||||
{
|
||||
return (aOffset < mSize) ? aOffset : -1;
|
||||
}
|
||||
virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset) { return PR_MAX(aOffset, mSize); }
|
||||
virtual PRBool IsDataCachedToEndOfStream(PRInt64 aOffset) { return PR_TRUE; }
|
||||
virtual PRBool IsSuspendedByCache() { return PR_FALSE; }
|
||||
|
@ -10,3 +10,5 @@ The wince.patch addresses the lack of posix file IO support on windows ce,
|
||||
see bug 461844 for details.
|
||||
|
||||
endian.patch is applied to fix bug 452698.
|
||||
|
||||
bounded_seek.patch is applied to fix bug 469408.
|
||||
|
332
media/liboggz/bounded_seek.patch
Normal file
332
media/liboggz/bounded_seek.patch
Normal file
@ -0,0 +1,332 @@
|
||||
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
|
||||
@@ -470,9 +470,28 @@ long oggz_seek_byorder (OGGZ * oggz, voi
|
||||
* \param oggz An OGGZ handle previously opened for reading
|
||||
* \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.
|
||||
+ * \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
|
||||
+ * \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);
|
||||
+
|
||||
#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
|
||||
@@ -617,205 +617,182 @@ oggz_offset_end (OGGZ * oggz)
|
||||
if (oggz_io_seek (oggz, offset_save, SEEK_SET) == -1) {
|
||||
return -1; /* fubar */
|
||||
}
|
||||
}
|
||||
|
||||
return offset_end;
|
||||
}
|
||||
|
||||
-static ogg_int64_t
|
||||
-oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
+ogg_int64_t
|
||||
+oggz_bounded_seek_set (OGGZ * oggz,
|
||||
+ ogg_int64_t unit_target,
|
||||
+ ogg_int64_t offset_begin,
|
||||
+ ogg_int64_t offset_end)
|
||||
{
|
||||
OggzReader * reader;
|
||||
oggz_off_t offset_orig, offset_at, offset_guess;
|
||||
- oggz_off_t offset_begin, offset_end = -1, offset_next;
|
||||
+ oggz_off_t offset_next;
|
||||
ogg_int64_t granule_at;
|
||||
- ogg_int64_t unit_at, unit_begin = 0, unit_end = -1, unit_last_iter = -1;
|
||||
+ 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;
|
||||
}
|
||||
|
||||
if (unit_target > 0 && !oggz_has_metrics (oggz)) {
|
||||
#ifdef DEBUG
|
||||
- printf ("oggz_seek_set: No metric defined, FAIL\n");
|
||||
+ printf ("oggz_bounded_seek_set: No metric defined, FAIL\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
-
|
||||
- if ((offset_end = oggz_offset_end (oggz)) == -1) {
|
||||
+
|
||||
+ if (offset_end == -1 && (offset_end = oggz_offset_end (oggz)) == -1) {
|
||||
#ifdef DEBUG
|
||||
- printf ("oggz_seek_set: oggz_offset_end == -1, FAIL\n");
|
||||
+ printf ("oggz_bounded_seek_set: oggz_offset_end == -1, FAIL\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
reader = &oggz->x.reader;
|
||||
|
||||
if (unit_target == reader->current_unit) {
|
||||
#ifdef DEBUG
|
||||
- printf ("oggz_seek_set: unit_target == reader->current_unit, SKIP\n");
|
||||
+ printf ("oggz_bounded_seek_set: unit_target == reader->current_unit, SKIP\n");
|
||||
#endif
|
||||
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;
|
||||
|
||||
og = &oggz->current_page;
|
||||
|
||||
- if (oggz_seek_raw (oggz, 0, SEEK_END) >= 0) {
|
||||
+ if (unit_end == -1 && oggz_seek_raw (oggz, offset_end, SEEK_SET) >= 0) {
|
||||
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) {
|
||||
+ 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)
|
||||
+ 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;
|
||||
+ offset_begin = offset_at;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
og = &oggz->current_page;
|
||||
|
||||
for ( ; ; ) {
|
||||
|
||||
unit_last_iter = unit_at;
|
||||
hit_eof = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
- printf ("oggz_seek_set: [A] want u%lld: (u%lld - u%lld) [@%" PRI_OGGZ_OFF_T "d - @%" PRI_OGGZ_OFF_T "d]\n",
|
||||
+ printf ("oggz_bounded_seek_set: [A] want u%lld: (u%lld - u%lld) [@%" PRI_OGGZ_OFF_T "d - @%" PRI_OGGZ_OFF_T "d]\n",
|
||||
unit_target, unit_begin, unit_end, offset_begin, offset_end);
|
||||
#endif
|
||||
|
||||
offset_guess = oggz_seek_guess (unit_at, unit_target,
|
||||
unit_begin, unit_end,
|
||||
offset_at,
|
||||
offset_begin, offset_end);
|
||||
if (offset_guess == -1) break;
|
||||
|
||||
if (offset_guess == offset_at) {
|
||||
/* Already there, looping */
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset_guess > offset_end) {
|
||||
offset_guess = offset_end;
|
||||
- }
|
||||
-
|
||||
- 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 %" PRI_OGGZ_OFF_T "d\n", offset_next);
|
||||
-#endif
|
||||
-
|
||||
- if (/*unit_end == -1 &&*/ offset_next == -2) { /* reached eof, backtrack */
|
||||
- hit_eof = 1;
|
||||
- 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 @%" PRI_OGGZ_OFF_T "d, 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 if (offset_next >= 0) {
|
||||
+ offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET);
|
||||
+ offset_next = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
|
||||
+ } else {
|
||||
+ 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);
|
||||
}
|
||||
|
||||
- if (offset_next < 0) {
|
||||
- goto notfound;
|
||||
- }
|
||||
-
|
||||
- if (hit_eof || 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;
|
||||
- }
|
||||
-
|
||||
- offset_at = offset_next;
|
||||
-
|
||||
unit_at = oggz_get_unit (oggz, serialno, granule_at);
|
||||
|
||||
+#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_seek_set: [D] want u%lld, got page u%lld @%" PRI_OGGZ_OFF_T "d g%lld\n",
|
||||
+ 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);
|
||||
#endif
|
||||
|
||||
if (unit_at < unit_target) {
|
||||
offset_begin = offset_at;
|
||||
unit_begin = unit_at;
|
||||
if (unit_end == unit_begin) break;
|
||||
} else if (unit_at > unit_target) {
|
||||
offset_end = offset_at-1;
|
||||
unit_end = unit_at;
|
||||
if (unit_end == unit_begin) break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- found:
|
||||
do {
|
||||
offset_at = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
|
||||
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_seek_set: FOUND (%lld)\n", unit_at);
|
||||
+ printf ("oggz_bounded_seek_set: FOUND (%lld)\n", unit_at);
|
||||
#endif
|
||||
|
||||
return (long)reader->current_unit;
|
||||
-
|
||||
- notfound:
|
||||
-#ifdef DEBUG
|
||||
- printf ("oggz_seek_set: NOT FOUND\n");
|
||||
-#endif
|
||||
-
|
||||
- oggz_reset (oggz, offset_orig, -1, SEEK_SET);
|
||||
-
|
||||
- return -1;
|
||||
}
|
||||
|
||||
static ogg_int64_t
|
||||
oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset)
|
||||
{
|
||||
oggz_off_t offset_orig, offset_at, offset_end;
|
||||
ogg_int64_t granulepos;
|
||||
ogg_int64_t unit_end;
|
||||
@@ -838,17 +815,17 @@ oggz_seek_end (OGGZ * oggz, ogg_int64_t
|
||||
return -1;
|
||||
}
|
||||
|
||||
#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_seek_set (oggz, unit_end + unit_offset);
|
||||
+ return oggz_bounded_seek_set (oggz, unit_end + unit_offset, 0, -1);
|
||||
}
|
||||
|
||||
off_t
|
||||
oggz_seek (OGGZ * oggz, oggz_off_t offset, int whence)
|
||||
{
|
||||
OggzReader * reader;
|
||||
ogg_int64_t units = -1;
|
||||
|
||||
@@ -897,21 +874,21 @@ oggz_seek_units (OGGZ * oggz, ogg_int64_
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
reader = &oggz->x.reader;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
- r = oggz_seek_set (oggz, units);
|
||||
+ r = oggz_bounded_seek_set (oggz, units, 0, -1);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
units += reader->current_unit;
|
||||
- r = oggz_seek_set (oggz, units);
|
||||
+ r = oggz_bounded_seek_set (oggz, units, 0, -1);
|
||||
break;
|
||||
case SEEK_END:
|
||||
r = oggz_seek_end (oggz, units);
|
||||
break;
|
||||
default:
|
||||
/*oggz_set_error (oggz, OGGZ_EINVALID);*/
|
||||
r = -1;
|
||||
break;
|
@ -475,4 +475,23 @@ 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.
|
||||
* \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
|
||||
* \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);
|
||||
|
||||
#endif /* __OGGZ_SEEK_H__ */
|
||||
|
@ -622,14 +622,17 @@ oggz_offset_end (OGGZ * oggz)
|
||||
return offset_end;
|
||||
}
|
||||
|
||||
static ogg_int64_t
|
||||
oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
ogg_int64_t
|
||||
oggz_bounded_seek_set (OGGZ * oggz,
|
||||
ogg_int64_t unit_target,
|
||||
ogg_int64_t offset_begin,
|
||||
ogg_int64_t offset_end)
|
||||
{
|
||||
OggzReader * reader;
|
||||
oggz_off_t offset_orig, offset_at, offset_guess;
|
||||
oggz_off_t offset_begin, offset_end = -1, offset_next;
|
||||
oggz_off_t offset_next;
|
||||
ogg_int64_t granule_at;
|
||||
ogg_int64_t unit_at, unit_begin = 0, unit_end = -1, unit_last_iter = -1;
|
||||
ogg_int64_t unit_at, unit_begin = -1, unit_end = -1, unit_last_iter = -1;
|
||||
long serialno;
|
||||
ogg_page * og;
|
||||
int hit_eof = 0;
|
||||
@ -640,14 +643,14 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
|
||||
if (unit_target > 0 && !oggz_has_metrics (oggz)) {
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: No metric defined, FAIL\n");
|
||||
printf ("oggz_bounded_seek_set: No metric defined, FAIL\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((offset_end = oggz_offset_end (oggz)) == -1) {
|
||||
|
||||
if (offset_end == -1 && (offset_end = oggz_offset_end (oggz)) == -1) {
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: oggz_offset_end == -1, FAIL\n");
|
||||
printf ("oggz_bounded_seek_set: oggz_offset_end == -1, FAIL\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
@ -656,7 +659,7 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
|
||||
if (unit_target == reader->current_unit) {
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: unit_target == reader->current_unit, SKIP\n");
|
||||
printf ("oggz_bounded_seek_set: unit_target == reader->current_unit, SKIP\n");
|
||||
#endif
|
||||
return (long)reader->current_unit;
|
||||
}
|
||||
@ -672,14 +675,11 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
|
||||
offset_orig = oggz->offset;
|
||||
|
||||
offset_begin = 0;
|
||||
|
||||
unit_at = reader->current_unit;
|
||||
unit_begin = 0;
|
||||
|
||||
og = &oggz->current_page;
|
||||
|
||||
if (oggz_seek_raw (oggz, 0, SEEK_END) >= 0) {
|
||||
if (unit_end == -1 && oggz_seek_raw (oggz, offset_end, SEEK_SET) >= 0) {
|
||||
ogg_int64_t granulepos;
|
||||
|
||||
if (oggz_get_prev_start_page (oggz, og, &granulepos, &serialno) >= 0) {
|
||||
@ -687,6 +687,30 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
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;
|
||||
offset_begin = offset_at;
|
||||
}
|
||||
}
|
||||
|
||||
og = &oggz->current_page;
|
||||
|
||||
for ( ; ; ) {
|
||||
@ -695,7 +719,7 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
hit_eof = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: [A] want u%lld: (u%lld - u%lld) [@%" PRI_OGGZ_OFF_T "d - @%" PRI_OGGZ_OFF_T "d]\n",
|
||||
printf ("oggz_bounded_seek_set: [A] want u%lld: (u%lld - u%lld) [@%" PRI_OGGZ_OFF_T "d - @%" PRI_OGGZ_OFF_T "d]\n",
|
||||
unit_target, unit_begin, unit_end, offset_begin, offset_end);
|
||||
#endif
|
||||
|
||||
@ -712,61 +736,24 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
|
||||
if (offset_guess > offset_end) {
|
||||
offset_guess = offset_end;
|
||||
}
|
||||
|
||||
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 %" PRI_OGGZ_OFF_T "d\n", offset_next);
|
||||
#endif
|
||||
|
||||
if (/*unit_end == -1 &&*/ offset_next == -2) { /* reached eof, backtrack */
|
||||
hit_eof = 1;
|
||||
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 @%" PRI_OGGZ_OFF_T "d, 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 if (offset_next >= 0) {
|
||||
offset_at = oggz_seek_raw (oggz, offset_guess, SEEK_SET);
|
||||
offset_next = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
if (offset_next < 0) {
|
||||
goto notfound;
|
||||
}
|
||||
|
||||
if (hit_eof || 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;
|
||||
}
|
||||
|
||||
offset_at = offset_next;
|
||||
|
||||
unit_at = oggz_get_unit (oggz, serialno, granule_at);
|
||||
|
||||
#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_seek_set: [D] want u%lld, got page u%lld @%" PRI_OGGZ_OFF_T "d g%lld\n",
|
||||
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);
|
||||
#endif
|
||||
|
||||
@ -783,7 +770,6 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
}
|
||||
}
|
||||
|
||||
found:
|
||||
do {
|
||||
offset_at = oggz_get_prev_start_page (oggz, og, &granule_at, &serialno);
|
||||
unit_at = oggz_get_unit (oggz, serialno, granule_at);
|
||||
@ -798,19 +784,10 @@ oggz_seek_set (OGGZ * oggz, ogg_int64_t unit_target)
|
||||
if (offset_at == -1) return -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: FOUND (%lld)\n", unit_at);
|
||||
printf ("oggz_bounded_seek_set: FOUND (%lld)\n", unit_at);
|
||||
#endif
|
||||
|
||||
return (long)reader->current_unit;
|
||||
|
||||
notfound:
|
||||
#ifdef DEBUG
|
||||
printf ("oggz_seek_set: NOT FOUND\n");
|
||||
#endif
|
||||
|
||||
oggz_reset (oggz, offset_orig, -1, SEEK_SET);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ogg_int64_t
|
||||
@ -843,7 +820,7 @@ oggz_seek_end (OGGZ * oggz, ogg_int64_t unit_offset)
|
||||
unit_end, offset_end, granulepos);
|
||||
#endif
|
||||
|
||||
return oggz_seek_set (oggz, unit_end + unit_offset);
|
||||
return oggz_bounded_seek_set (oggz, unit_end + unit_offset, 0, -1);
|
||||
}
|
||||
|
||||
off_t
|
||||
@ -902,11 +879,11 @@ oggz_seek_units (OGGZ * oggz, ogg_int64_t units, int whence)
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
r = oggz_seek_set (oggz, units);
|
||||
r = oggz_bounded_seek_set (oggz, units, 0, -1);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
units += reader->current_unit;
|
||||
r = oggz_seek_set (oggz, units);
|
||||
r = oggz_bounded_seek_set (oggz, units, 0, -1);
|
||||
break;
|
||||
case SEEK_END:
|
||||
r = oggz_seek_end (oggz, units);
|
||||
|
@ -46,3 +46,4 @@ sed s/\#include\ \"config.h\"/\#ifdef\ WIN32\\n\#include\ \"config_win32.h\"\\n\
|
||||
cp $1/AUTHORS ./AUTHORS
|
||||
patch -p3 <wince.patch
|
||||
patch -p3 <endian.patch
|
||||
patch -p3 <bounded_seek.patch
|
||||
|
Loading…
Reference in New Issue
Block a user