Bug 639391 - Ensure WebM GetBuffered() is threadsafe. r=kinetik

This commit is contained in:
Chris Pearce 2011-03-24 11:28:58 +13:00
Родитель df9a15ba57
Коммит 6d85c92e19
3 изменённых файлов: 37 добавлений и 21 удалений

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

@ -39,6 +39,9 @@
#include "nsAlgorithm.h"
#include "nsWebMBufferedParser.h"
#include "nsTimeRanges.h"
#include "nsThreadUtils.h"
using mozilla::MonitorAutoEnter;
static const double NS_PER_S = 1e9;
static const double MS_PER_S = 1e3;
@ -63,7 +66,8 @@ VIntLength(unsigned char aFirstByte, PRUint32* aMask)
}
void nsWebMBufferedParser::Append(const unsigned char* aBuffer, PRUint32 aLength,
nsTArray<nsWebMTimeDataOffset>& aMapping)
nsTArray<nsWebMTimeDataOffset>& aMapping,
Monitor& aMonitor)
{
static const unsigned char CLUSTER_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
static const unsigned char TIMECODE_ID = 0xe7;
@ -167,10 +171,13 @@ void nsWebMBufferedParser::Append(const unsigned char* aBuffer, PRUint32 aLength
} else {
// It's possible we've parsed this data before, so avoid inserting
// duplicate nsWebMTimeDataOffset entries.
PRUint32 idx;
if (!aMapping.GreatestIndexLtEq(mBlockOffset, idx)) {
nsWebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
aMapping.InsertElementAt(idx, entry);
{
MonitorAutoEnter mon(aMonitor);
PRUint32 idx;
if (!aMapping.GreatestIndexLtEq(mBlockOffset, idx)) {
nsWebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
aMapping.InsertElementAt(idx, entry);
}
}
// Skip rest of block header and the block's payload.
@ -208,6 +215,8 @@ void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
PRUint64 aTimecodeScale,
PRInt64 aStartTimeOffsetNS)
{
MonitorAutoEnter mon(mMonitor);
// Find the first nsWebMTimeDataOffset at or after aStartOffset.
PRUint32 start;
mTimeMapping.GreatestIndexLtEq(aStartOffset, start);
@ -251,6 +260,7 @@ void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
PRUint32 idx;
if (!mRangeParsers.GreatestIndexLtEq(aOffset, idx)) {
// If the incoming data overlaps an already parsed range, adjust the
@ -274,7 +284,10 @@ void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLengt
}
}
mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer), aLength, mTimeMapping);
mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer),
aLength,
mTimeMapping,
mMonitor);
// Merge parsers with overlapping regions and clean up the remnants.
PRUint32 i = 0;

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

@ -40,8 +40,10 @@
#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "mozilla/Monitor.h"
class nsTimeRanges;
using mozilla::Monitor;
// Stores a stream byte offset and the scaled timecode of the block at
// that offset. The timecode must be scaled by the stream's timecode
@ -77,9 +79,11 @@ struct nsWebMBufferedParser
{}
// Steps the parser through aLength bytes of data. Always consumes
// aLength bytes. Updates mCurrentOffset before returning.
// aLength bytes. Updates mCurrentOffset before returning. Acquires
// aMonitor before using aMapping.
void Append(const unsigned char* aBuffer, PRUint32 aLength,
nsTArray<nsWebMTimeDataOffset>& aMapping);
nsTArray<nsWebMTimeDataOffset>& aMapping,
Monitor& aMonitor);
bool operator==(PRInt64 aOffset) const {
return mCurrentOffset == aOffset;
@ -212,7 +216,7 @@ class nsWebMBufferedState
NS_INLINE_DECL_REFCOUNTING(nsWebMBufferedState)
public:
nsWebMBufferedState() {
nsWebMBufferedState() : mMonitor("nsWebMBufferedState") {
MOZ_COUNT_CTOR(nsWebMBufferedState);
}
@ -227,6 +231,9 @@ public:
PRInt64 aStartTimeOffsetNS);
private:
// Synchronizes access to the mTimeMapping array.
Monitor mMonitor;
// Sorted (by offset) map of data offsets to timecodes. Populated
// on the main thread as data is received and parsed by nsWebMBufferedParsers.
nsTArray<nsWebMTimeDataOffset> mTimeMapping;

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

@ -796,22 +796,18 @@ nsresult nsWebMReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
aBuffered->Add(0, duration / NS_PER_S);
}
} else {
PRInt64 startOffset = stream->GetNextCachedData(0);
PRInt64 startTimeOffsetNS = aStartTime * NS_PER_MS;
while (startOffset >= 0) {
PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
NS_ASSERTION(startOffset < endOffset, "Cached range invalid");
nsMediaStream* stream = mDecoder->GetCurrentStream();
nsTArray<nsByteRange> ranges;
nsresult res = stream->GetCachedRanges(ranges);
NS_ENSURE_SUCCESS(res, res);
PRInt64 startTimeOffsetNS = aStartTime * NS_PER_MS;
for (PRUint32 index = 0; index < ranges.Length(); index++) {
mBufferedState->CalculateBufferedForRange(aBuffered,
startOffset,
endOffset,
ranges[index].mStart,
ranges[index].mEnd,
timecodeScale,
startTimeOffsetNS);
// Advance to the next cached data range.
startOffset = stream->GetNextCachedData(endOffset);
NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
"Next cached range invalid");
}
}