Bug 513144. Make nsMediaChannelStream::CloneData clone the underlying cache stream, which adds the new stream as co-owner of all the nsMediaCache blocks currently cached for the original stream. Also, use NS_NEW_RUNNABLE_METHOD instead of the SuspendedStatusChanged class. r=doublec
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -154,16 +154,19 @@ public:
// Notify the cache that a block has been read from. This is used
// to update last-use times. The block may not actually have a
// cache entry yet since Read can read data from a stream's
// 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(nsMediaCacheStream* aStream, PRInt32 aBlockIndex,
nsMediaCacheStream::ReadMode aMode, TimeStamp aNow);
+ // Mark aStream as having the block, adding it as an owner.
+ void AddBlockOwnerAsReadahead(PRInt32 aBlockIndex, nsMediaCacheStream* aStream,
+ PRInt32 aStreamBlockIndex);
// This queues a call to Update() on the main thread.
void QueueUpdate();
// Updates the cache state asynchronously on the main thread:
// -- try to trim the cache back to its desired size, if necessary
// -- suspend channels that are going to read data that's lower priority
// than anything currently cached
@@ -836,16 +839,33 @@ nsMediaCache::RemoveBlockOwner(PRInt32 a
mFreeBlocks.AddFirstBlock(aBlockIndex);
}
return;
}
}
}
void
+nsMediaCache::AddBlockOwnerAsReadahead(PRInt32 aBlockIndex,
+ nsMediaCacheStream* aStream,
+ PRInt32 aStreamBlockIndex)
+{
+ Block* block = &mIndex[aBlockIndex];
+ if (block->mOwners.IsEmpty()) {
+ mFreeBlocks.RemoveBlock(aBlockIndex);
+ }
+ BlockOwner* bo = block->mOwners.AppendElement();
+ bo->mStream = aStream;
+ bo->mStreamBlock = aStreamBlockIndex;
+ aStream->mBlocks[aStreamBlockIndex] = aBlockIndex;
+ bo->mClass = READAHEAD_BLOCK;
+ InsertReadaheadBlock(bo, aBlockIndex);
+}
+
+void
nsMediaCache::FreeBlock(PRInt32 aBlock)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
Block* block = &mIndex[aBlock];
if (block->mOwners.IsEmpty()) {
// already free
return;
@@ -1991,14 +2011,49 @@ nsMediaCacheStream::ReadFromCache(char*
return NS_OK;
}
nsresult
nsMediaCacheStream::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+ if (mInitialized)
+ return NS_OK;
+
InitMediaCache();
if (!gMediaCache)
return NS_ERROR_FAILURE;
gMediaCache->OpenStream(this);
+ mInitialized = PR_TRUE;
return NS_OK;
}
+
+nsresult
+nsMediaCacheStream::InitAsClone(nsMediaCacheStream* aOriginal)
+{
+ if (mInitialized)
+ return NS_OK;
+
+ nsresult rv = Init();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Grab cache blocks from aOriginal as readahead blocks for our stream
+ nsAutoMonitor mon(gMediaCache->Monitor());
+
+ mPrincipal = aOriginal->mPrincipal;
+ mStreamLength = aOriginal->mStreamLength;
+ mIsSeekable = aOriginal->mIsSeekable;
+
+ for (PRUint32 i = 0; i < aOriginal->mBlocks.Length(); ++i) {
+ PRInt32 cacheBlockIndex = aOriginal->mBlocks[i];
+ if (cacheBlockIndex < 0)
+ continue;
+
+ while (i >= mBlocks.Length()) {
+ mBlocks.AppendElement(-1);
+ }
+ gMediaCache->AddBlockOwnerAsReadahead(cacheBlockIndex, this, i);
+ }
+
+ return NS_OK;
+}
--- a/content/media/nsMediaCache.h
+++ b/content/media/nsMediaCache.h
@@ -217,27 +217,34 @@ public:
MODE_PLAYBACK
};
// aClient provides the underlying transport that cache will use to read
// data for this stream.
nsMediaCacheStream(nsMediaChannelStream* aClient)
: mClient(aClient), mChannelOffset(0),
mStreamOffset(0), mStreamLength(-1), mPlaybackBytesPerSecond(10000),
- mPinCount(0), mCurrentMode(MODE_PLAYBACK), mClosed(PR_FALSE),
+ mPinCount(0), mCurrentMode(MODE_PLAYBACK),
+ mInitialized(PR_FALSE), mClosed(PR_FALSE),
mIsSeekable(PR_FALSE), mCacheSuspended(PR_FALSE),
mMetadataInPartialBlockBuffer(PR_FALSE),
mUsingNullPrincipal(PR_FALSE) {}
~nsMediaCacheStream();
- // Set up this stream with the cache. Can fail on OOM. Must be called
- // before other methods on this object; no other methods may be called
- // if this fails.
+ // Set up this stream with the cache. Can fail on OOM. One
+ // of InitAsClone or Init must be called before any other method on
+ // this class. Does nothing if already initialized.
nsresult Init();
+ // Set up this stream with the cache, assuming it's for the same data
+ // as the aOriginal stream. Can fail on OOM. Exactly one
+ // of InitAsClone or Init must be called before any other method on
+ // this class. Does nothing if already initialized.
+ nsresult InitAsClone(nsMediaCacheStream* aOriginal);
+
// These are called on the main thread.
// Tell us whether the stream is seekable or not. Non-seekable streams
// will always pass 0 for aOffset to CacheClientSeek. This should only
// be called while the stream is at channel offset 0. Seekability can
// change during the lifetime of the nsMediaCacheStream --- every time
// we do an HTTP load the seekability may be different (and sometimes
// is, in practice, due to the effects of caching proxies).
void SetSeekable(PRBool aIsSeekable);
@@ -442,16 +449,18 @@ private:
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
// corresponding Unpin
PRUint32 mPinCount;
// The last reported read mode
ReadMode mCurrentMode;
+ // Set to true when Init or InitAsClone has been called
+ PRPackedBool mInitialized;
// Set to true when the stream has been closed either explicitly or
// due to an internal cache error
PRPackedBool mClosed;
// The last reported seekability state for the underlying channel
PRPackedBool mIsSeekable;
// true if the cache has suspended our channel because the cache is
// full and the priority of the data that would be received is lower
// than the priority of the data already in the cache
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/nsMediaStream.cpp
@@ -447,17 +447,17 @@ already_AddRefed<nsIPrincipal> nsMediaCh
nsMediaStream* nsMediaChannelStream::CloneData(nsMediaDecoder* aDecoder)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsMediaChannelStream* stream = new nsMediaChannelStream(aDecoder, nsnull, mURI);
if (stream) {
stream->RecreateChannel();
- // XXXroc need to clone mCacheStream's data here
+ stream->mCacheStream.InitAsClone(&mCacheStream);
}
return stream;
}
void nsMediaChannelStream::CloseChannel()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
@@ -609,68 +609,48 @@ nsMediaChannelStream::CacheClientSeek(PR
nsresult rv = RecreateChannel();
if (NS_FAILED(rv))
return rv;
mOffset = aOffset;
return OpenChannel(nsnull);
}
-class SuspendedStatusChanged : public nsRunnable
-{
-public:
- SuspendedStatusChanged(nsMediaDecoder* aDecoder) :
- mDecoder(aDecoder)
- {
- MOZ_COUNT_CTOR(SuspendedStatusChanged);
- }
- ~SuspendedStatusChanged()
- {
- MOZ_COUNT_DTOR(SuspendedStatusChanged);
- }
-
- NS_IMETHOD Run() {
- mDecoder->NotifySuspendedStatusChanged();
- return NS_OK;
- }
-
-private:
- nsRefPtr<nsMediaDecoder> mDecoder;
-};
-
nsresult
nsMediaChannelStream::CacheClientSuspend()
{
{
nsAutoLock lock(mLock);
++mCacheSuspendCount;
}
Suspend(PR_FALSE);
// We have to spawn an event here since we're being called back from
// a sensitive place in nsMediaCache, which doesn't want us to reenter
// the decoder and cause deadlocks or other unpleasantness
- nsCOMPtr<nsIRunnable> event = new SuspendedStatusChanged(mDecoder);
+ nsCOMPtr<nsIRunnable> event =
+ NS_NEW_RUNNABLE_METHOD(nsMediaDecoder, mDecoder, NotifySuspendedStatusChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
nsresult
nsMediaChannelStream::CacheClientResume()
{
Resume();
{
nsAutoLock lock(mLock);
--mCacheSuspendCount;
}
// We have to spawn an event here since we're being called back from
// a sensitive place in nsMediaCache, which doesn't want us to reenter
// the decoder and cause deadlocks or other unpleasantness
- nsCOMPtr<nsIRunnable> event = new SuspendedStatusChanged(mDecoder);
+ nsCOMPtr<nsIRunnable> event =
+ NS_NEW_RUNNABLE_METHOD(nsMediaDecoder, mDecoder, NotifySuspendedStatusChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
PRInt64
nsMediaChannelStream::GetNextCachedData(PRInt64 aOffset)
{
return mCacheStream.GetNextCachedData(aOffset);