Bug 513144. Allow nsMediaCache blocks to be owned by multiple streams at the same time. r=doublec

--HG--
extra : rebase_source : 8788764ff6c883d2da5347ead39660626deb9d7c
This commit is contained in:
Robert O'Callahan 2009-09-15 14:30:44 +12:00
parent 93031ccc14
commit e6abe07b1b
2 changed files with 248 additions and 184 deletions

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -157,8 +157,8 @@ public:
// in-memory mPartialBlockBuffer while the block is only partly full,
// and thus hasn't yet been committed to the cache. The caller will
// call QueueUpdate().
void NoteBlockUsage(PRInt32 aBlockIndex, nsMediaCacheStream::ReadMode aMode,
TimeStamp aNow);
void NoteBlockUsage(nsMediaCacheStream* aStream, PRInt32 aBlockIndex,
nsMediaCacheStream::ReadMode aMode, TimeStamp aNow);
// This queues a call to Update() on the main thread.
void QueueUpdate();
@ -197,6 +197,7 @@ protected:
nsMediaCacheStream* aForStream,
PRInt32 aForStreamBlock,
PRInt32 aMaxSearchBlockIndex);
PRBool BlockIsReusable(PRInt32 aBlockIndex);
// Given a list of blocks sorted with the most reusable blocks at the
// end, find the last block whose stream is not pinned (if any)
// and whose cache entry index is less than aBlockIndexLimit
@ -206,8 +207,6 @@ protected:
PRInt32 aBlockIndexLimit);
enum BlockClass {
// block belongs to mFreeBlockList because it's free
FREE_BLOCK,
// block belongs to mMetadataBlockList because data has been consumed
// from it in "metadata mode" --- in particular blocks read during
// Ogg seeks go into this class. These blocks may have played data
@ -222,8 +221,8 @@ protected:
READAHEAD_BLOCK
};
struct Block {
Block() : mStream(nsnull), mClass(FREE_BLOCK) {}
struct BlockOwner {
BlockOwner() : mStream(nsnull), mClass(READAHEAD_BLOCK) {}
// The stream that owns this block, or null if the block is free.
nsMediaCacheStream* mStream;
@ -232,22 +231,35 @@ protected:
// Time at which this block was last used. Valid only if
// mClass is METADATA_BLOCK or PLAYED_BLOCK.
TimeStamp mLastUseTime;
// The class is FREE_BLOCK if and only if mStream is null
BlockClass mClass;
};
struct Block {
// Free blocks have an empty mOwners array
nsTArray<BlockOwner> mOwners;
};
// Get the BlockList that the block should belong to given its
// current mClass and mStream
BlockList* GetListForBlock(Block* aBlock);
// Add the block to the free list, mark it FREE_BLOCK, and mark
// its stream (if any) as not having the block in cache
// current owner
BlockList* GetListForBlock(BlockOwner* aBlock);
// Get the BlockOwner for the given block index and owning stream
// (returns null if the stream does not own the block)
BlockOwner* GetBlockOwner(PRInt32 aBlockIndex, nsMediaCacheStream* aStream);
// Returns true iff the block is free
PRBool IsBlockFree(PRInt32 aBlockIndex)
{ return mIndex[aBlockIndex].mOwners.IsEmpty(); }
// Add the block to the free list and mark its streams as not having
// the block in cache
void FreeBlock(PRInt32 aBlock);
// Mark aStream as not having the block, removing it as an owner. If
// the block has no more owners it's added to the free list.
void RemoveBlockOwner(PRInt32 aBlockIndex, nsMediaCacheStream* aStream);
// Swap all metadata associated with the two blocks. The caller
// is responsible for swapping up any cache file state.
void SwapBlocks(PRInt32 aBlockIndex1, PRInt32 aBlockIndex2);
// Insert the block into the readahead block list for its stream
// Insert the block into the readahead block list for the stream
// at the right point in the list.
void InsertReadaheadBlock(PRInt32 aBlockIndex);
void InsertReadaheadBlock(BlockOwner* aBlockOwner, PRInt32 aBlockIndex);
// Guess the duration until block aBlock will be next used
TimeDuration PredictNextUse(TimeStamp aNow, PRInt32 aBlock);
@ -274,10 +286,6 @@ protected:
PRInt64 mFDCurrentPos;
// The list of free blocks; they are not ordered.
BlockList mFreeBlocks;
// The list of metadata blocks; the first block is the most recently used
BlockList mMetadataBlocks;
// The list of played-back blocks; the first block is the most recently used
BlockList mPlayedBlocks;
// True if an event to run Update() has been queued but not processed
PRPackedBool mUpdateQueued;
#ifdef DEBUG
@ -616,7 +624,7 @@ nsMediaCache::FindBlockForIncomingData(TimeStamp aNow,
PRInt32 blockIndex = FindReusableBlock(aNow, aStream,
aStream->mChannelOffset/BLOCK_SIZE, PR_INT32_MAX);
if (blockIndex < 0 || mIndex[blockIndex].mStream) {
if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
// The block returned is already allocated.
// Don't reuse it if a) there's room to expand the cache or
// b) the data we're going to store in the free block is not higher
@ -635,6 +643,20 @@ nsMediaCache::FindBlockForIncomingData(TimeStamp aNow,
return blockIndex;
}
PRBool
nsMediaCache::BlockIsReusable(PRInt32 aBlockIndex)
{
Block* block = &mIndex[aBlockIndex];
for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
nsMediaCacheStream* stream = block->mOwners[i].mStream;
if (stream->mPinCount >= 0 ||
stream->mStreamOffset/BLOCK_SIZE == block->mOwners[i].mStreamBlock) {
return PR_FALSE;
}
}
return PR_TRUE;
}
void
nsMediaCache::AppendMostReusableBlock(BlockList* aBlockList,
nsTArray<PRUint32>* aResult,
@ -647,12 +669,10 @@ nsMediaCache::AppendMostReusableBlock(BlockList* aBlockList,
return;
do {
// Don't consider blocks for pinned streams, or blocks that are
// beyond the specified limit, or the block that contains its stream's
// beyond the specified limit, or a block that contains a stream's
// current read position (such a block contains both played data
// and readahead data)
nsMediaCacheStream* stream = mIndex[blockIndex].mStream;
if (stream->mPinCount == 0 && blockIndex < aBlockIndexLimit &&
stream->mStreamOffset/BLOCK_SIZE != mIndex[blockIndex].mStreamBlock) {
if (blockIndex < aBlockIndexLimit && BlockIsReusable(blockIndex)) {
aResult->AppendElement(blockIndex);
return;
}
@ -677,7 +697,7 @@ nsMediaCache::FindReusableBlock(TimeStamp aNow,
PRUint32 freeBlockScanEnd =
PR_MIN(length, prevCacheBlock + FREE_BLOCK_SCAN_LIMIT);
for (PRUint32 i = prevCacheBlock; i < freeBlockScanEnd; ++i) {
if (mIndex[i].mClass == FREE_BLOCK)
if (IsBlockFree(i))
return i;
}
}
@ -697,25 +717,20 @@ nsMediaCache::FindReusableBlock(TimeStamp aNow,
// linked lists are ordered by increasing time of next use. This is
// actually the whole point of having the linked lists.
nsAutoTArray<PRUint32,8> candidates;
AppendMostReusableBlock(&mMetadataBlocks, &candidates, length);
AppendMostReusableBlock(&mPlayedBlocks, &candidates, length);
for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
nsMediaCacheStream* stream = mStreams[i];
// Don't consider a) blocks for pinned streams or b) blocks in
// non-seekable streams that contain data ahead of the current reader
// position. In the latter case, if we remove the block we won't be
// able to seek back to read it later.
if (!stream->mReadaheadBlocks.IsEmpty() && stream->mIsSeekable &&
stream->mPinCount == 0) {
// Find a readahead block that's in the given limit
PRInt32 blockIndex = stream->mReadaheadBlocks.GetLastBlock();
do {
if (PRUint32(blockIndex) < length) {
candidates.AppendElement(blockIndex);
break;
}
blockIndex = stream->mReadaheadBlocks.GetPrevBlock(blockIndex);
} while (blockIndex >= 0);
if (stream->mPinCount > 0) {
// No point in even looking at this stream's blocks
continue;
}
AppendMostReusableBlock(&stream->mMetadataBlocks, &candidates, length);
AppendMostReusableBlock(&stream->mPlayedBlocks, &candidates, length);
// Don't consider readahead blocks in non-seekable streams. If we
// remove the block we won't be able to seek back to read it later.
if (stream->mIsSeekable) {
AppendMostReusableBlock(&stream->mReadaheadBlocks, &candidates, length);
}
}
@ -729,34 +744,19 @@ nsMediaCache::FindReusableBlock(TimeStamp aNow,
}
}
#ifdef DEBUG
for (PRUint32 blockIndex = 0; blockIndex < length; ++blockIndex) {
Block* block = &mIndex[blockIndex];
nsMediaCacheStream* stream = block->mStream;
NS_ASSERTION(!stream || stream->mPinCount > 0 ||
(!stream->mIsSeekable && block->mClass == READAHEAD_BLOCK) ||
stream->mStreamOffset/BLOCK_SIZE == block->mStreamBlock ||
PredictNextUse(aNow, blockIndex) <= latestUse,
"We missed a block that should be replaced");
}
#endif
return latestUseBlock;
}
nsMediaCache::BlockList*
nsMediaCache::GetListForBlock(Block* aBlock)
nsMediaCache::GetListForBlock(BlockOwner* aBlock)
{
switch (aBlock->mClass) {
case FREE_BLOCK:
NS_ASSERTION(!aBlock->mStream, "Free block has a stream?");
return &mFreeBlocks;
case METADATA_BLOCK:
NS_ASSERTION(aBlock->mStream, "Metadata block has no stream?");
return &mMetadataBlocks;
return &aBlock->mStream->mMetadataBlocks;
case PLAYED_BLOCK:
NS_ASSERTION(aBlock->mStream, "Metadata block has no stream?");
return &mPlayedBlocks;
return &aBlock->mStream->mPlayedBlocks;
case READAHEAD_BLOCK:
NS_ASSERTION(aBlock->mStream, "Readahead block has no stream?");
return &aBlock->mStream->mReadaheadBlocks;
@ -766,6 +766,17 @@ nsMediaCache::GetListForBlock(Block* aBlock)
}
}
nsMediaCache::BlockOwner*
nsMediaCache::GetBlockOwner(PRInt32 aBlockIndex, nsMediaCacheStream* aStream)
{
Block* block = &mIndex[aBlockIndex];
for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
if (block->mOwners[i].mStream == aStream)
return &block->mOwners[i];
}
return nsnull;
}
void
nsMediaCache::SwapBlocks(PRInt32 aBlockIndex1, PRInt32 aBlockIndex2)
{
@ -774,43 +785,80 @@ nsMediaCache::SwapBlocks(PRInt32 aBlockIndex1, PRInt32 aBlockIndex2)
Block* block1 = &mIndex[aBlockIndex1];
Block* block2 = &mIndex[aBlockIndex2];
Block tmp = *block1;
*block1 = *block2;
*block2 = tmp;
block1->mOwners.SwapElements(block2->mOwners);
// Now all references to block1 have to be replaced with block2 and
// vice versa
if (block1->mStream) {
block1->mStream->mBlocks[block1->mStreamBlock] = aBlockIndex1;
}
if (block2->mStream) {
block2->mStream->mBlocks[block2->mStreamBlock] = aBlockIndex2;
// vice versa.
// First update stream references to blocks via mBlocks.
const Block* blocks[] = { block1, block2 };
PRInt32 blockIndices[] = { aBlockIndex1, aBlockIndex2 };
for (PRInt32 i = 0; i < 2; ++i) {
for (PRUint32 j = 0; j < blocks[i]->mOwners.Length(); ++j) {
const BlockOwner* b = &blocks[i]->mOwners[j];
b->mStream->mBlocks[b->mStreamBlock] = blockIndices[i];
}
}
BlockList* list1 = GetListForBlock(block1);
list1->NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
BlockList* list2 = GetListForBlock(block2);
// We have to be careful we don't swap the same reference twice!
if (list1 != list2) {
list2->NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
// Now update references to blocks in block lists.
mFreeBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
nsTHashtable<nsPtrHashKey<nsMediaCacheStream> > visitedStreams;
visitedStreams.Init();
for (PRInt32 i = 0; i < 2; ++i) {
for (PRUint32 j = 0; j < blocks[i]->mOwners.Length(); ++j) {
nsMediaCacheStream* stream = blocks[i]->mOwners[j].mStream;
// Make sure that we don't update the same stream twice --- that
// would result in swapping the block references back again!
if (visitedStreams.GetEntry(stream))
continue;
visitedStreams.PutEntry(stream);
stream->mReadaheadBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
stream->mPlayedBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
stream->mMetadataBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
}
}
Verify();
}
void
nsMediaCache::RemoveBlockOwner(PRInt32 aBlockIndex, nsMediaCacheStream* aStream)
{
Block* block = &mIndex[aBlockIndex];
for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
if (bo->mStream == aStream) {
GetListForBlock(bo)->RemoveBlock(aBlockIndex);
bo->mStream->mBlocks[bo->mStreamBlock] = -1;
block->mOwners.RemoveElementAt(i);
if (block->mOwners.IsEmpty()) {
mFreeBlocks.AddFirstBlock(aBlockIndex);
}
return;
}
}
}
void
nsMediaCache::FreeBlock(PRInt32 aBlock)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
Block* block = &mIndex[aBlock];
GetListForBlock(block)->RemoveBlock(aBlock);
if (block->mStream) {
block->mStream->mBlocks[block->mStreamBlock] = -1;
if (block->mOwners.IsEmpty()) {
// already free
return;
}
block->mStream = nsnull;
block->mClass = FREE_BLOCK;
LOG(PR_LOG_DEBUG, ("Released block %d", aBlock));
for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
GetListForBlock(bo)->RemoveBlock(aBlock);
bo->mStream->mBlocks[bo->mStreamBlock] = -1;
}
block->mOwners.Clear();
mFreeBlocks.AddFirstBlock(aBlock);
Verify();
}
@ -819,36 +867,50 @@ TimeDuration
nsMediaCache::PredictNextUse(TimeStamp aNow, PRInt32 aBlock)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
NS_ASSERTION(!IsBlockFree(aBlock), "aBlock is free");
Block* block = &mIndex[aBlock];
switch (block->mClass) {
case METADATA_BLOCK:
// This block should be managed in LRU mode. For metadata we predict
// that the time until the next use is the time since the last use.
return aNow - block->mLastUseTime;
case PLAYED_BLOCK:
// This block should be managed in LRU mode, and we should impose
// a "replay delay" to reflect the likelihood of replay happening
NS_ASSERTION(PRInt64(block->mStreamBlock)*BLOCK_SIZE <
block->mStream->mStreamOffset,
"Played block after the current stream position?");
return aNow - block->mLastUseTime +
// Blocks can be belong to multiple streams. The predicted next use
// time is the earliest time predicted by any of the streams.
TimeDuration result;
for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
TimeDuration prediction;
switch (bo->mClass) {
case METADATA_BLOCK:
// This block should be managed in LRU mode. For metadata we predict
// that the time until the next use is the time since the last use.
prediction = aNow - bo->mLastUseTime;
break;
case PLAYED_BLOCK:
// This block should be managed in LRU mode, and we should impose
// a "replay delay" to reflect the likelihood of replay happening
NS_ASSERTION(PRInt64(bo->mStreamBlock)*BLOCK_SIZE <
bo->mStream->mStreamOffset,
"Played block after the current stream position?");
prediction = aNow - bo->mLastUseTime +
TimeDuration::FromSeconds(REPLAY_DELAY);
case READAHEAD_BLOCK: {
PRInt64 bytesAhead =
PRInt64(block->mStreamBlock)*BLOCK_SIZE - block->mStream->mStreamOffset;
NS_ASSERTION(bytesAhead >= 0,
"Readahead block before the current stream position?");
PRInt64 millisecondsAhead =
bytesAhead*1000/block->mStream->mPlaybackBytesPerSecond;
return TimeDuration::FromMilliseconds(
PR_MIN(millisecondsAhead, PR_INT32_MAX));
}
default:
NS_ERROR("Invalid class for predicting next use");
return TimeDuration(0);
break;
case READAHEAD_BLOCK: {
PRInt64 bytesAhead =
PRInt64(bo->mStreamBlock)*BLOCK_SIZE - bo->mStream->mStreamOffset;
NS_ASSERTION(bytesAhead >= 0,
"Readahead block before the current stream position?");
PRInt64 millisecondsAhead =
bytesAhead*1000/bo->mStream->mPlaybackBytesPerSecond;
prediction = TimeDuration::FromMilliseconds(
PR_MIN(millisecondsAhead, PR_INT32_MAX));
break;
}
default:
NS_ERROR("Invalid class for predicting next use");
return TimeDuration(0);
}
if (i == 0 || prediction < result) {
result = prediction;
}
}
return result;
}
TimeDuration
@ -900,8 +962,7 @@ nsMediaCache::Update()
TimeDuration latestPredictedUseForOverflow = 0;
for (PRInt32 blockIndex = mIndex.Length() - 1; blockIndex >= maxBlocks;
--blockIndex) {
nsMediaCacheStream* stream = mIndex[blockIndex].mStream;
if (!stream) {
if (IsBlockFree(blockIndex)) {
// Don't count overflowing free blocks in our free block count
--freeBlockCount;
continue;
@ -913,21 +974,23 @@ nsMediaCache::Update()
// Now try to move overflowing blocks to the main part of the cache.
for (PRInt32 blockIndex = mIndex.Length() - 1; blockIndex >= maxBlocks;
--blockIndex) {
Block* block = &mIndex[blockIndex];
nsMediaCacheStream* stream = block->mStream;
if (!stream)
if (IsBlockFree(blockIndex))
continue;
Block* block = &mIndex[blockIndex];
// Try to relocate the block close to other blocks for the first stream.
// There is no point in trying ot make it close to other blocks in
// *all* the streams it might belong to.
PRInt32 destinationBlockIndex =
FindReusableBlock(now, stream, block->mStreamBlock, maxBlocks);
FindReusableBlock(now, block->mOwners[0].mStream,
block->mOwners[0].mStreamBlock, maxBlocks);
if (destinationBlockIndex < 0) {
// Nowhere to place this overflow block. We won't be able to
// place any more overflow blocks.
break;
}
Block* destinationBlock = &mIndex[destinationBlockIndex];
if (destinationBlock->mClass == FREE_BLOCK ||
if (IsBlockFree(destinationBlockIndex) ||
PredictNextUse(now, destinationBlockIndex) > latestPredictedUseForOverflow) {
// Reuse blocks in the main part of the cache that are less useful than
// the least useful overflow blocks
@ -945,19 +1008,14 @@ nsMediaCache::Update()
} else {
// If the write fails we may have corrupted the destination
// block. Free it now.
LOG(PR_LOG_DEBUG, ("Released block %d from stream %p block %d(%lld) (trimming cache)",
destinationBlockIndex, destinationBlock->mStream,
destinationBlock->mStreamBlock,
(long long)destinationBlock->mStreamBlock*BLOCK_SIZE));
LOG(PR_LOG_DEBUG, ("Released block %d (trimming cache)",
destinationBlockIndex));
FreeBlock(destinationBlockIndex);
}
// Free the overflowing block even if the copy failed.
if (block->mClass != FREE_BLOCK) {
LOG(PR_LOG_DEBUG, ("Released block %d from stream %p block %d(%lld) (trimming cache)",
blockIndex, block->mStream, block->mStreamBlock,
(long long)block->mStreamBlock*BLOCK_SIZE));
FreeBlock(blockIndex);
}
LOG(PR_LOG_DEBUG, ("Released block %d (trimming cache)",
blockIndex));
FreeBlock(blockIndex);
}
}
}
@ -1145,51 +1203,53 @@ nsMediaCache::Verify()
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
mFreeBlocks.Verify();
mPlayedBlocks.Verify();
mMetadataBlocks.Verify();
for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
nsMediaCacheStream* stream = mStreams[i];
stream->mReadaheadBlocks.Verify();
if (!stream->mReadaheadBlocks.IsEmpty()) {
// Verify that the readahead blocks are listed in stream block order
PRInt32 block = stream->mReadaheadBlocks.GetFirstBlock();
PRInt32 lastStreamBlock = -1;
do {
NS_ASSERTION(mIndex[block].mStream == stream, "Bad stream");
NS_ASSERTION(lastStreamBlock < PRInt32(mIndex[block].mStreamBlock),
"Blocks not increasing in readahead stream");
lastStreamBlock = PRInt32(mIndex[block].mStreamBlock);
block = stream->mReadaheadBlocks.GetNextBlock(block);
} while (block >= 0);
stream->mPlayedBlocks.Verify();
stream->mMetadataBlocks.Verify();
// Verify that the readahead blocks are listed in stream block order
PRInt32 block = stream->mReadaheadBlocks.GetFirstBlock();
PRInt32 lastStreamBlock = -1;
while (block >= 0) {
PRUint32 j = 0;
while (mIndex[block].mOwners[j].mStream != stream) {
++j;
}
PRInt32 nextStreamBlock =
PRInt32(mIndex[block].mOwners[j].mStreamBlock);
NS_ASSERTION(lastStreamBlock < nextStreamBlock,
"Blocks not increasing in readahead stream");
lastStreamBlock = nextStreamBlock;
block = stream->mReadaheadBlocks.GetNextBlock(block);
}
}
}
#endif
void
nsMediaCache::InsertReadaheadBlock(PRInt32 aBlockIndex)
nsMediaCache::InsertReadaheadBlock(BlockOwner* aBlockOwner,
PRInt32 aBlockIndex)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
Block* block = &mIndex[aBlockIndex];
nsMediaCacheStream* stream = block->mStream;
if (stream->mReadaheadBlocks.IsEmpty()) {
stream->mReadaheadBlocks.AddFirstBlock(aBlockIndex);
return;
}
// Find the last block whose stream block is before aBlockIndex's
// stream block, and insert after it
nsMediaCacheStream* stream = aBlockOwner->mStream;
PRInt32 readaheadIndex = stream->mReadaheadBlocks.GetLastBlock();
do {
if (mIndex[readaheadIndex].mStreamBlock < block->mStreamBlock) {
while (readaheadIndex >= 0) {
BlockOwner* bo = GetBlockOwner(readaheadIndex, stream);
NS_ASSERTION(bo, "stream must own its blocks");
if (bo->mStreamBlock < aBlockOwner->mStreamBlock) {
stream->mReadaheadBlocks.AddAfter(aBlockIndex, readaheadIndex);
return;
}
NS_ASSERTION(mIndex[readaheadIndex].mStreamBlock > block->mStreamBlock,
NS_ASSERTION(bo->mStreamBlock > aBlockOwner->mStreamBlock,
"Duplicated blocks??");
readaheadIndex = stream->mReadaheadBlocks.GetPrevBlock(readaheadIndex);
} while (readaheadIndex >= 0);
}
stream->mReadaheadBlocks.AddFirstBlock(aBlockIndex);
Verify();
}
@ -1206,45 +1266,44 @@ nsMediaCache::AllocateAndWriteBlock(nsMediaCacheStream* aStream, const void* aDa
aStream->mBlocks.AppendElement(-1);
}
if (aStream->mBlocks[streamBlockIndex] >= 0) {
// Release the existing cache entry for this stream block
// We no longer want to own this block
PRInt32 globalBlockIndex = aStream->mBlocks[streamBlockIndex];
LOG(PR_LOG_DEBUG, ("Released block %d from stream %p block %d(%lld)",
globalBlockIndex, aStream, streamBlockIndex, (long long)streamBlockIndex*BLOCK_SIZE));
FreeBlock(globalBlockIndex);
RemoveBlockOwner(globalBlockIndex, aStream);
}
TimeStamp now = TimeStamp::Now();
PRInt32 blockIndex = FindBlockForIncomingData(now, aStream);
if (blockIndex >= 0) {
Block* block = &mIndex[blockIndex];
if (block->mClass != FREE_BLOCK) {
LOG(PR_LOG_DEBUG, ("Released block %d from stream %p block %d(%lld)",
blockIndex, block->mStream, block->mStreamBlock, (long long)block->mStreamBlock*BLOCK_SIZE));
FreeBlock(blockIndex);
}
NS_ASSERTION(block->mClass == FREE_BLOCK, "Block should be free now!");
FreeBlock(blockIndex);
Block* block = &mIndex[blockIndex];
LOG(PR_LOG_DEBUG, ("Allocated block %d to stream %p block %d(%lld)",
blockIndex, aStream, streamBlockIndex, (long long)streamBlockIndex*BLOCK_SIZE));
block->mStream = aStream;
block->mStreamBlock = streamBlockIndex;
block->mLastUseTime = now;
BlockOwner* bo = block->mOwners.AppendElement();
if (!bo)
return;
bo->mStream = aStream;
bo->mStreamBlock = streamBlockIndex;
bo->mLastUseTime = now;
aStream->mBlocks[streamBlockIndex] = blockIndex;
mFreeBlocks.RemoveBlock(blockIndex);
if (streamBlockIndex*BLOCK_SIZE < aStream->mStreamOffset) {
block->mClass = aMode == nsMediaCacheStream::MODE_PLAYBACK
bo->mClass = aMode == nsMediaCacheStream::MODE_PLAYBACK
? PLAYED_BLOCK : METADATA_BLOCK;
// This must be the most-recently-used block, since we
// marked it as used now (which may be slightly bogus, but we'll
// treat it as used for simplicity).
GetListForBlock(block)->AddFirstBlock(blockIndex);
GetListForBlock(bo)->AddFirstBlock(blockIndex);
Verify();
} else {
// This may not be the latest readahead block, although it usually
// will be. We may have to scan for the right place to insert
// the block in the list.
block->mClass = READAHEAD_BLOCK;
InsertReadaheadBlock(blockIndex);
bo->mClass = READAHEAD_BLOCK;
InsertReadaheadBlock(bo, blockIndex);
}
nsresult rv = WriteCacheFile(blockIndex*BLOCK_SIZE, aData, BLOCK_SIZE);
@ -1292,7 +1351,7 @@ nsMediaCache::ReleaseStreamBlocks(nsMediaCacheStream* aStream)
if (blockIndex >= 0) {
LOG(PR_LOG_DEBUG, ("Released block %d from stream %p block %d(%lld)",
blockIndex, aStream, i, (long long)i*BLOCK_SIZE));
FreeBlock(blockIndex);
RemoveBlockOwner(blockIndex, aStream);
}
}
}
@ -1302,7 +1361,7 @@ nsMediaCache::Truncate()
{
PRUint32 end;
for (end = mIndex.Length(); end > 0; --end) {
if (mIndex[end - 1].mStream)
if (!IsBlockFree(end - 1))
break;
mFreeBlocks.RemoveBlock(end - 1);
}
@ -1317,7 +1376,7 @@ nsMediaCache::Truncate()
}
void
nsMediaCache::NoteBlockUsage(PRInt32 aBlockIndex,
nsMediaCache::NoteBlockUsage(nsMediaCacheStream* aStream, PRInt32 aBlockIndex,
nsMediaCacheStream::ReadMode aMode,
TimeStamp aNow)
{
@ -1328,25 +1387,25 @@ nsMediaCache::NoteBlockUsage(PRInt32 aBlockIndex,
return;
}
Block* block = &mIndex[aBlockIndex];
if (block->mClass == FREE_BLOCK) {
BlockOwner* bo = GetBlockOwner(aBlockIndex, aStream);
if (!bo) {
// this block is not in the cache yet
return;
}
// The following check has to be <= because the stream offset has
// not yet been updated for the data read from this block
NS_ASSERTION(block->mStreamBlock*BLOCK_SIZE <= block->mStream->mStreamOffset,
NS_ASSERTION(bo->mStreamBlock*BLOCK_SIZE <= bo->mStream->mStreamOffset,
"Using a block that's behind the read position?");
GetListForBlock(block)->RemoveBlock(aBlockIndex);
block->mClass =
(aMode == nsMediaCacheStream::MODE_METADATA || block->mClass == METADATA_BLOCK)
GetListForBlock(bo)->RemoveBlock(aBlockIndex);
bo->mClass =
(aMode == nsMediaCacheStream::MODE_METADATA || bo->mClass == METADATA_BLOCK)
? METADATA_BLOCK : PLAYED_BLOCK;
// Since this is just being used now, it can definitely be at the front
// of mMetadataBlocks or mPlayedBlocks
GetListForBlock(block)->AddFirstBlock(aBlockIndex);
block->mLastUseTime = aNow;
GetListForBlock(bo)->AddFirstBlock(aBlockIndex);
bo->mLastUseTime = aNow;
Verify();
}
@ -1369,7 +1428,7 @@ nsMediaCache::NoteSeek(nsMediaCacheStream* aStream, PRInt64 aOldOffset)
if (cacheBlockIndex >= 0) {
// Marking the block used may not be exactly what we want but
// it's simple
NoteBlockUsage(cacheBlockIndex, nsMediaCacheStream::MODE_PLAYBACK,
NoteBlockUsage(aStream, cacheBlockIndex, nsMediaCacheStream::MODE_PLAYBACK,
now);
}
++blockIndex;
@ -1386,15 +1445,16 @@ nsMediaCache::NoteSeek(nsMediaCacheStream* aStream, PRInt64 aOldOffset)
while (blockIndex < endIndex) {
PRInt32 cacheBlockIndex = aStream->mBlocks[endIndex - 1];
if (cacheBlockIndex >= 0) {
Block* block = &mIndex[cacheBlockIndex];
if (block->mClass != METADATA_BLOCK) {
mPlayedBlocks.RemoveBlock(cacheBlockIndex);
block->mClass = READAHEAD_BLOCK;
BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
NS_ASSERTION(bo, "Stream doesn't own its blocks?");
if (bo->mClass == PLAYED_BLOCK) {
aStream->mPlayedBlocks.RemoveBlock(cacheBlockIndex);
bo->mClass = READAHEAD_BLOCK;
// Adding this as the first block is sure to be OK since
// this must currently be the earliest readahead block
// (that's why we're proceeding backwards from the end of
// the seeked range to the start)
GetListForBlock(block)->AddFirstBlock(cacheBlockIndex);
aStream->mReadaheadBlocks.AddFirstBlock(cacheBlockIndex);
Verify();
}
}
@ -1831,7 +1891,7 @@ nsMediaCacheStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
if (mCurrentMode == MODE_METADATA) {
mMetadataInPartialBlockBuffer = PR_TRUE;
}
gMediaCache->NoteBlockUsage(cacheBlock, mCurrentMode, TimeStamp::Now());
gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
} else {
if (cacheBlock < 0) {
if (count > 0) {
@ -1850,7 +1910,7 @@ nsMediaCacheStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
continue;
}
gMediaCache->NoteBlockUsage(cacheBlock, mCurrentMode, TimeStamp::Now());
gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
PRInt64 offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, size, &bytes);

View File

@ -436,6 +436,10 @@ private:
// block is the earliest in the stream (so the last block will be the
// least valuable).
BlockList mReadaheadBlocks;
// The list of metadata blocks; the first block is the most recently used
BlockList mMetadataBlocks;
// The list of played-back blocks; the first block is the most recently used
BlockList mPlayedBlocks;
// The last reported estimate of the decoder's playback rate
PRUint32 mPlaybackBytesPerSecond;
// The number of times this stream has been Pinned without a