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