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
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 15 Sep 2009 14:30:44 +1200
changeset 32904 aea350f323885028993916ee73b7ae7e2e3e6d0c
parent 32903 a4ef15912d218af253eefa53cba30e0376a64349
child 32905 643b222c2d83749c495bee8c2f5343fd11dc9586
push idunknown
push userunknown
push dateunknown
reviewersdoublec
bugs513144
milestone1.9.3a1pre
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
content/media/nsMediaCache.cpp
content/media/nsMediaCache.h
content/media/nsMediaStream.cpp
--- 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);