Bug 639391 - Push Ogg specific seeking stuff down into nsOggReader. r=roc

This commit is contained in:
Chris Pearce 2011-03-24 11:28:58 +13:00
Родитель ad1de1d7b7
Коммит a7114ca3c3
4 изменённых файлов: 150 добавлений и 151 удалений

Просмотреть файл

@ -230,84 +230,6 @@ nsresult nsBuiltinDecoderReader::ResetDecode()
return res;
}
nsresult nsBuiltinDecoderReader::GetBufferedBytes(nsTArray<ByteRange>& aRanges)
{
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
mMonitor.AssertCurrentThreadIn();
PRInt64 startOffset = mDataOffset;
nsMediaStream* stream = mDecoder->GetCurrentStream();
while (PR_TRUE) {
PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
if (endOffset == startOffset) {
// Uncached at startOffset.
endOffset = stream->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 startTime = -1;
PRInt64 endTime = -1;
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;
}
FindStartTime(startOffset, startTime);
if (startTime != -1 &&
((endTime = FindEndTime(endOffset)) != -1))
{
NS_ASSERTION(startOffset < endOffset,
"Start offset must be before end offset");
NS_ASSERTION(startTime < endTime,
"Start time must be before end time");
aRanges.AppendElement(ByteRange(startOffset,
endOffset,
startTime,
endTime));
}
}
startOffset = endOffset;
}
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
ByteRange
nsBuiltinDecoderReader::GetSeekRange(const nsTArray<ByteRange>& ranges,
PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
PRBool aExact)
{
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
PRInt64 so = mDataOffset;
PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
PRInt64 st = aStartTime;
PRInt64 et = aEndTime;
for (PRUint32 i = 0; i < ranges.Length(); i++) {
const ByteRange &r = ranges[i];
if (r.mTimeStart < aTarget) {
so = r.mOffsetStart;
st = r.mTimeStart;
}
if (r.mTimeEnd >= aTarget && r.mTimeEnd < et) {
eo = r.mOffsetEnd;
et = r.mTimeEnd;
}
if (r.mTimeStart < aTarget && aTarget <= r.mTimeEnd) {
// Target lies exactly in this range.
return ranges[i];
}
}
return aExact ? ByteRange() : ByteRange(so, eo, st, et);
}
VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64 aOffset,
PRInt64& aOutStartTime)
{

Просмотреть файл

@ -405,39 +405,6 @@ private:
PRBool mEndOfStream;
};
// Represents a section of contiguous media, with a start and end offset,
// and the timestamps of the start and end of that range. Used to denote the
// extremities of a range to seek in.
class ByteRange {
public:
ByteRange()
: mOffsetStart(0),
mOffsetEnd(0),
mTimeStart(0),
mTimeEnd(0)
{}
ByteRange(PRInt64 aOffsetStart,
PRInt64 aOffsetEnd,
PRInt64 aTimeStart,
PRInt64 aTimeEnd)
: mOffsetStart(aOffsetStart),
mOffsetEnd(aOffsetEnd),
mTimeStart(aTimeStart),
mTimeEnd(aTimeEnd)
{}
PRBool IsNull() const {
return mOffsetStart == 0 &&
mOffsetEnd == 0 &&
mTimeStart == 0 &&
mTimeEnd == 0;
}
PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
PRInt64 mTimeStart, mTimeEnd; // in ms.
};
// Encapsulates the decoding and reading of media data. Reading can be done
// on either the state machine thread (when loading and seeking) or on
// the reader thread (when it's reading and decoding). The reader encapsulates
@ -541,26 +508,6 @@ protected:
return DecodeVideoFrame(f, 0);
}
// Fills aRanges with ByteRanges denoting the sections of the media which
// have been downloaded and are stored in the media cache. The reader
// monitor must must be held with exactly one lock count. The nsMediaStream
// must be pinned while calling this.
nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges);
// Returns the range in which you should perform a seek bisection if
// you wish to seek to aTarget ms, given the known (buffered) byte ranges
// in aRanges. If aExact is PR_TRUE, we only return an exact copy of a
// range in which aTarget lies, or a null range if aTarget isn't contained
// in any of the (buffered) ranges. Otherwise, when aExact is PR_FALSE,
// we'll construct the smallest possible range we can, based on the times
// and byte offsets known in aRanges. We can then use this to minimize our
// bisection's search space when the target isn't in a known buffered range.
ByteRange GetSeekRange(const nsTArray<ByteRange>& aRanges,
PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
PRBool aExact);
// The lock which we hold whenever we read or decode. This ensures the thread
// safety of the reader and its data fields.
Monitor mMonitor;

Просмотреть файл

@ -1023,6 +1023,84 @@ PRInt64 nsOggReader::FindEndTime(PRInt64 aStartOffset,
return endTime;
}
nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
{
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
mMonitor.AssertCurrentThreadIn();
PRInt64 startOffset = mDataOffset;
nsMediaStream* stream = mDecoder->GetCurrentStream();
while (PR_TRUE) {
PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
if (endOffset == startOffset) {
// Uncached at startOffset.
endOffset = stream->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 startTime = -1;
PRInt64 endTime = -1;
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;
}
FindStartTime(startOffset, startTime);
if (startTime != -1 &&
((endTime = FindEndTime(endOffset)) != -1))
{
NS_ASSERTION(startOffset < endOffset,
"Start offset must be before end offset");
NS_ASSERTION(startTime < endTime,
"Start time must be before end time");
aRanges.AppendElement(SeekRange(startOffset,
endOffset,
startTime,
endTime));
}
}
startOffset = endOffset;
}
if (NS_FAILED(ResetDecode())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsOggReader::SeekRange
nsOggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
PRBool aExact)
{
NS_ASSERTION(mDecoder->OnStateMachineThread(),
"Should be on state machine thread.");
PRInt64 so = mDataOffset;
PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
PRInt64 st = aStartTime;
PRInt64 et = aEndTime;
for (PRUint32 i = 0; i < ranges.Length(); i++) {
const SeekRange &r = ranges[i];
if (r.mTimeStart < aTarget) {
so = r.mOffsetStart;
st = r.mTimeStart;
}
if (r.mTimeEnd >= aTarget && r.mTimeEnd < et) {
eo = r.mOffsetEnd;
et = r.mTimeEnd;
}
if (r.mTimeStart < aTarget && aTarget <= r.mTimeEnd) {
// Target lies exactly in this range.
return ranges[i];
}
}
return aExact ? SeekRange() : SeekRange(so, eo, st, et);
}
nsOggReader::IndexedSeekResult nsOggReader::RollbackIndexedSeek(PRInt64 aOffset)
{
mSkeletonState->Deactivate();
@ -1118,8 +1196,8 @@ nsOggReader::IndexedSeekResult nsOggReader::SeekToKeyframeUsingIndex(PRInt64 aTa
nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
const nsTArray<ByteRange>& aRanges,
const ByteRange& aRange)
const nsTArray<SeekRange>& aRanges,
const SeekRange& aRange)
{
LOG(PR_LOG_DEBUG, ("%p Seeking in buffered data to %lldms using bisection search", mDecoder, aTarget));
@ -1156,11 +1234,11 @@ nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
PRInt64 keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
SEEK_LOG(PR_LOG_DEBUG, ("Keyframe for %lld is at %lld, seeking back to it",
video->mTime, keyframeTime));
ByteRange k = GetSeekRange(aRanges,
keyframeTime,
aStartTime,
aEndTime,
PR_FALSE);
SeekRange k = SelectSeekRange(aRanges,
keyframeTime,
aStartTime,
aEndTime,
PR_FALSE);
res = SeekBisection(keyframeTime, k, SEEK_FUZZ_MS);
NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
@ -1182,7 +1260,7 @@ PRBool nsOggReader::CanDecodeToTarget(PRInt64 aTarget,
nsresult nsOggReader::SeekInUnbuffered(PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
const nsTArray<ByteRange>& aRanges)
const nsTArray<SeekRange>& aRanges)
{
LOG(PR_LOG_DEBUG, ("%p Seeking in unbuffered data to %lldms using bisection search", mDecoder, aTarget));
@ -1205,7 +1283,7 @@ nsresult nsOggReader::SeekInUnbuffered(PRInt64 aTarget,
PRInt64 seekTarget = NS_MAX(aStartTime, aTarget - keyframeOffsetMs);
// Minimize the bisection search space using the known timestamps from the
// buffered ranges.
ByteRange k = GetSeekRange(aRanges, seekTarget, aStartTime, aEndTime, PR_FALSE);
SeekRange k = SelectSeekRange(aRanges, seekTarget, aStartTime, aEndTime, PR_FALSE);
nsresult res = SeekBisection(seekTarget, k, SEEK_FUZZ_MS);
NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
@ -1251,12 +1329,12 @@ nsresult nsOggReader::Seek(PRInt64 aTarget,
// No index or other non-fatal index-related failure. Try to seek
// using a bisection search. Determine the already downloaded data
// in the media cache, so we can try to seek in the cached data first.
nsAutoTArray<ByteRange, 16> ranges;
res = GetBufferedBytes(ranges);
nsAutoTArray<SeekRange, 16> ranges;
res = GetSeekRanges(ranges);
NS_ENSURE_SUCCESS(res,res);
// Figure out if the seek target lies in a buffered range.
ByteRange r = GetSeekRange(ranges, aTarget, aStartTime, aEndTime, PR_TRUE);
SeekRange r = SelectSeekRange(ranges, aTarget, aStartTime, aEndTime, PR_TRUE);
if (!r.IsNull()) {
// We know the buffered range in which the seek target lies, do a
@ -1345,7 +1423,7 @@ PageSync(nsMediaStream* aStream,
}
nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
const ByteRange& aRange,
const SeekRange& aRange,
PRUint32 aFuzz)
{
NS_ASSERTION(mDecoder->OnStateMachineThread(),
@ -1716,4 +1794,3 @@ PRBool nsOggReader::IsKnownStream(PRUint32 aSerial)
return PR_FALSE;
}

Просмотреть файл

@ -121,27 +121,61 @@ private:
// Rolls back a seek-using-index attempt, returning a failure error code.
IndexedSeekResult RollbackIndexedSeek(PRInt64 aOffset);
// Represents a section of contiguous media, with a start and end offset,
// and the timestamps of the start and end of that range, that is cached.
// Used to denote the extremities of a range in which we can seek quickly
// (because it's cached).
class SeekRange {
public:
SeekRange()
: mOffsetStart(0),
mOffsetEnd(0),
mTimeStart(0),
mTimeEnd(0)
{}
SeekRange(PRInt64 aOffsetStart,
PRInt64 aOffsetEnd,
PRInt64 aTimeStart,
PRInt64 aTimeEnd)
: mOffsetStart(aOffsetStart),
mOffsetEnd(aOffsetEnd),
mTimeStart(aTimeStart),
mTimeEnd(aTimeEnd)
{}
PRBool IsNull() const {
return mOffsetStart == 0 &&
mOffsetEnd == 0 &&
mTimeStart == 0 &&
mTimeEnd == 0;
}
PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
PRInt64 mTimeStart, mTimeEnd; // in ms.
};
// Seeks to aTarget ms in the buffered range aRange using bisection search,
// or to the keyframe prior to aTarget if we have video. aStartTime must be
// the presentation time at the start of media, and aEndTime the time at
// end of media. aRanges must be the time/byte ranges buffered in the media
// cache as per GetBufferedBytes().
// cache as per GetSeekRanges().
nsresult SeekInBufferedRange(PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
const nsTArray<ByteRange>& aRanges,
const ByteRange& aRange);
const nsTArray<SeekRange>& aRanges,
const SeekRange& aRange);
// Seeks to before aTarget ms in media using bisection search. If the media
// has video, this will seek to before the keyframe required to render the
// media at aTarget. Will use aRanges in order to narrow the bisection
// search space. aStartTime must be the presentation time at the start of
// media, and aEndTime the time at end of media. aRanges must be the time/byte
// ranges buffered in the media cache as per GetBufferedBytes().
// ranges buffered in the media cache as per GetSeekRanges().
nsresult SeekInUnbuffered(PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
const nsTArray<ByteRange>& aRanges);
const nsTArray<SeekRange>& aRanges);
// Get the end time of aEndOffset, without reading before aStartOffset.
// This is the playback position we'd reach after playback finished at
@ -179,13 +213,32 @@ private:
// aFuzz is the number of ms of leniency we'll allow; we'll terminate the
// seek when we land in the range (aTime - aFuzz, aTime) ms.
nsresult SeekBisection(PRInt64 aTarget,
const ByteRange& aRange,
const SeekRange& aRange,
PRUint32 aFuzz);
// Returns true if the serial number is for a stream we encountered
// while reading metadata. Call on the main thread only.
PRBool IsKnownStream(PRUint32 aSerial);
// Fills aRanges with SeekRanges denoting the sections of the media which
// have been downloaded and are stored in the media cache. The reader
// monitor must must be held with exactly one lock count. The nsMediaStream
// must be pinned while calling this.
nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
// Returns the range in which you should perform a seek bisection if
// you wish to seek to aTarget ms, given the known (buffered) byte ranges
// in aRanges. If aExact is PR_TRUE, we only return an exact copy of a
// range in which aTarget lies, or a null range if aTarget isn't contained
// in any of the (buffered) ranges. Otherwise, when aExact is PR_FALSE,
// we'll construct the smallest possible range we can, based on the times
// and byte offsets known in aRanges. We can then use this to minimize our
// bisection's search space when the target isn't in a known buffered range.
SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
PRInt64 aTarget,
PRInt64 aStartTime,
PRInt64 aEndTime,
PRBool aExact);
private:
// Maps Ogg serialnos to nsOggStreams.
nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;