зеркало из https://github.com/mozilla/gecko-dev.git
Bug 639391 - Push Ogg specific seeking stuff down into nsOggReader. r=roc
This commit is contained in:
Родитель
ad1de1d7b7
Коммит
a7114ca3c3
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче