Bug 521176. Don't unnecessarily start an HTTP transaction when cloning a media resource. r=doublec
authorRobert O'Callahan <robert@ocallahan.org>
Sat, 10 Oct 2009 00:48:30 +1300
changeset 34505 af45ce6dc57520066bbcd2c92b11d4dacb4a3821
parent 34504 e8635ca5bc94d91c1c6b4ddca6877fb430c187c6
child 34506 2d2a7f2f0a96710202e2ba8e8648bbffa5f5e96e
push idunknown
push userunknown
push dateunknown
reviewersdoublec
bugs521176
milestone1.9.3a1pre
Bug 521176. Don't unnecessarily start an HTTP transaction when cloning a media resource. r=doublec
content/media/nsMediaCache.cpp
content/media/nsMediaStream.cpp
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -2144,21 +2144,27 @@ nsMediaCacheStream::InitAsClone(nsMediaC
 
   // Grab cache blocks from aOriginal as readahead blocks for our stream
   nsAutoMonitor mon(gMediaCache->Monitor());
 
   mPrincipal = aOriginal->mPrincipal;
   mStreamLength = aOriginal->mStreamLength;
   mIsSeekable = aOriginal->mIsSeekable;
 
+  // Cloned streams are initially suspended, since there is no channel open
+  // initially for a clone.
+  mCacheSuspended = PR_TRUE;
+
   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);
     }
+    // Every block is a readahead block for the clone because the clone's initial
+    // stream offset is zero
     gMediaCache->AddBlockOwnerAsReadahead(cacheBlockIndex, this, i);
   }
 
   return NS_OK;
 }
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/nsMediaStream.cpp
@@ -381,16 +381,25 @@ nsresult nsMediaChannelStream::Open(nsIS
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (!mLock)
     return NS_ERROR_OUT_OF_MEMORY;
   nsresult rv = mCacheStream.Init();
   if (NS_FAILED(rv))
     return rv;
   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
+
+  if (!mChannel) {
+    // When we're a clone, the decoder might ask us to Open even though
+    // we haven't established an mChannel (because we might not need one)
+    NS_ASSERTION(!aStreamListener,
+                 "Should have already been given a channel if we're to return a stream listener");
+    return NS_OK;
+  }
+
   return OpenChannel(aStreamListener);
 }
 
 nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
   NS_ASSERTION(!mListener, "Listener should have been removed by now");
@@ -475,17 +484,24 @@ 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();
+    // Initially the clone is treated as suspended by the cache, because
+    // we don't have a channel. If the cache needs to read data from the clone
+    // it will call CacheClientResume (or CacheClientSeek with aResume true)
+    // which will recreate the channel. This way, if all of the media data
+    // is already in the cache we don't create an unneccesary HTTP channel
+    // and perform a useless HTTP transaction.
+    stream->mSuspendCount = 1;
+    stream->mCacheSuspendCount = 1;
     stream->mCacheStream.InitAsClone(&mCacheStream);
   }
   return stream;
 }
 
 void nsMediaChannelStream::CloseChannel()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
@@ -573,16 +589,17 @@ void nsMediaChannelStream::Resume()
   NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
 
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   if (!element) {
     // Shutting down; do nothing.
     return;
   }
 
+  NS_ASSERTION(mSuspendCount > 0, "Resume without previous Suspend!");
   --mSuspendCount;
   if (mSuspendCount == 0) {
     if (mChannel) {
       // Just wake up our existing channel
       {
         nsAutoLock lock(mLock);
         mChannelStatistics.Start(TimeStamp::Now());
       }
@@ -683,16 +700,21 @@ nsMediaChannelStream::CacheClientSeek(PR
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
 
   CloseChannel();
 
   if (aResume) {
     NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     // No need to mess with the channel, since we're making a new one
     --mSuspendCount;
+    {
+      nsAutoLock lock(mLock);
+      NS_ASSERTION(mCacheSuspendCount > 0, "CacheClientSeek(aResume=true) without previous CacheClientSuspend!");
+      --mCacheSuspendCount;
+    }
   }
 
   nsresult rv = RecreateChannel();
   if (NS_FAILED(rv))
     return rv;
 
   mOffset = aOffset;
   return OpenChannel(nsnull);
@@ -712,16 +734,17 @@ nsMediaChannelStream::CacheClientSuspend
 }
 
 nsresult
 nsMediaChannelStream::CacheClientResume()
 {
   Resume();
   {
     nsAutoLock lock(mLock);
+    NS_ASSERTION(mCacheSuspendCount > 0, "CacheClientResume without previous CacheClientSuspend!");
     --mCacheSuspendCount;
   }
 
   mDecoder->NotifySuspendedStatusChanged();
   return NS_OK;
 }
 
 PRInt64