Bug 1001317 - reset |MediaCacheStream::mDidNotifyDataEnded| so that it can notify data ended correctly upon 2nd download. r=roc
authorJW Wang <jwwang@mozilla.com>
Sun, 11 May 2014 20:43:00 +0200
changeset 182613 5ceb693e97330c2f341eb8ae36a5fc650c120658
parent 182612 45ae4b13806977b919af3fcaf1273fb99a794d12
child 182614 a170dabfd12bed8f1e3a4df7f05a747ce8f487f3
push id43346
push usercbook@mozilla.com
push dateMon, 12 May 2014 12:02:02 +0000
treeherdermozilla-inbound@2f7a4bba1dac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1001317
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1001317 - reset |MediaCacheStream::mDidNotifyDataEnded| so that it can notify data ended correctly upon 2nd download. r=roc
content/media/MediaCache.cpp
content/media/MediaCache.h
content/media/MediaResource.cpp
content/media/MediaResource.h
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -1854,34 +1854,42 @@ MediaCacheStream::NotifyDataEnded(nsresu
     // Disconnect from other streams sharing our resource, since they
     // should continue trying to load. Our load might have been deliberately
     // canceled and that shouldn't affect other streams.
     mResourceID = gMediaCache->AllocateResourceID();
   }
 
   FlushPartialBlockInternal(true);
 
-  if (!mDidNotifyDataEnded) {
-    MediaCache::ResourceStreamIterator iter(mResourceID);
-    while (MediaCacheStream* stream = iter.Next()) {
-      if (NS_SUCCEEDED(aStatus)) {
-        // We read the whole stream, so remember the true length
-        stream->mStreamLength = mChannelOffset;
-      }
-      NS_ASSERTION(!stream->mDidNotifyDataEnded, "Stream already ended!");
+  MediaCache::ResourceStreamIterator iter(mResourceID);
+  while (MediaCacheStream* stream = iter.Next()) {
+    if (NS_SUCCEEDED(aStatus)) {
+      // We read the whole stream, so remember the true length
+      stream->mStreamLength = mChannelOffset;
+    }
+    if (!stream->mDidNotifyDataEnded) {
       stream->mDidNotifyDataEnded = true;
       stream->mNotifyDataEndedStatus = aStatus;
       stream->mClient->CacheClientNotifyDataEnded(aStatus);
     }
   }
 
   mChannelEnded = true;
   gMediaCache->QueueUpdate();
 }
 
+void
+MediaCacheStream::NotifyChannelRecreated()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
+  mChannelEnded = false;
+  mDidNotifyDataEnded = false;
+}
+
 MediaCacheStream::~MediaCacheStream()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mPinCount, "Unbalanced Pin");
 
   if (gMediaCache) {
     NS_ASSERTION(mClosed, "Stream was not closed");
     gMediaCache->ReleaseStream(this);
--- a/content/media/MediaCache.h
+++ b/content/media/MediaCache.h
@@ -267,16 +267,20 @@ public:
   void NotifyDataReceived(int64_t aSize, const char* aData,
                           nsIPrincipal* aPrincipal);
   // Notifies the cache that the current bytes should be written to disk.
   // Called on the main thread.
   void FlushPartialBlock();
   // Notifies the cache that the channel has closed with the given status.
   void NotifyDataEnded(nsresult aStatus);
 
+  // Notifies the stream that the channel is reopened. The stream should
+  // reset variables such as |mDidNotifyDataEnded|.
+  void NotifyChannelRecreated();
+
   // These methods can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
   // while the stream is pinned.
   void Pin();
   void Unpin();
   // See comments above for NotifyDataLength about how the length
   // can vary over time. Returns -1 if no length is known. Returns the
   // reported length if we haven't got any better information. If
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -482,18 +482,17 @@ ChannelMediaResource::OnStopRequest(nsIR
   return NS_OK;
 }
 
 nsresult
 ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
                                         uint32_t aFlags)
 {
   mChannel = aNew;
-  SetupChannelHeaders();
-  return NS_OK;
+  return SetupChannelHeaders();
 }
 
 struct CopySegmentClosure {
   nsCOMPtr<nsIPrincipal> mPrincipal;
   ChannelMediaResource*  mResource;
 };
 
 NS_METHOD
@@ -604,55 +603,57 @@ nsresult ChannelMediaResource::OpenChann
 
   mListener = new Listener(this);
   NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
 
   if (aStreamListener) {
     *aStreamListener = mListener;
     NS_ADDREF(*aStreamListener);
   } else {
-    mChannel->SetNotificationCallbacks(mListener.get());
+    nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
+    NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStreamListener> listener = mListener.get();
 
     // Ensure that if we're loading cross domain, that the server is sending
     // an authorizing Access-Control header.
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     if (element->ShouldCheckAllowOrigin()) {
       nsRefPtr<nsCORSListenerProxy> crossSiteListener =
         new nsCORSListenerProxy(mListener,
                                 element->NodePrincipal(),
                                 false);
-      nsresult rv = crossSiteListener->Init(mChannel);
+      NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
+      rv = crossSiteListener->Init(mChannel);
+      NS_ENSURE_SUCCESS(rv, rv);
       listener = crossSiteListener;
-      NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
-      NS_ENSURE_SUCCESS(rv, rv);
     } else {
-      nsresult rv = nsContentUtils::GetSecurityManager()->
+      rv = nsContentUtils::GetSecurityManager()->
         CheckLoadURIWithPrincipal(element->NodePrincipal(),
                                   mURI,
                                   nsIScriptSecurityManager::STANDARD);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    SetupChannelHeaders();
+    rv = SetupChannelHeaders();
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsresult rv = mChannel->AsyncOpen(listener, nullptr);
+    rv = mChannel->AsyncOpen(listener, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
     // Tell the media element that we are fetching data from a channel.
     element->DownloadResumed(true);
   }
 
   return NS_OK;
 }
 
-void ChannelMediaResource::SetupChannelHeaders()
+nsresult ChannelMediaResource::SetupChannelHeaders()
 {
   // Always use a byte range request even if we're reading from the start
   // of the resource.
   // This enables us to detect if the stream supports byte range
   // requests, and therefore seeking, early.
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
   if (hc) {
     // Use |mByteRange| for a specific chunk, or |mOffset| if seeking in a
@@ -663,32 +664,31 @@ void ChannelMediaResource::SetupChannelH
       mOffset = mByteRange.mStart;
     } else {
       rangeString.AppendInt(mOffset);
     }
     rangeString.Append("-");
     if (!mByteRange.IsNull()) {
       rangeString.AppendInt(mByteRange.mEnd);
     }
-    hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
+    nsresult rv = hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     // Send Accept header for video and audio types only (Bug 489071)
     NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
-    if (!owner) {
-      return;
-    }
+    NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
-    if (!element) {
-      return;
-    }
+    NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     element->SetRequestHeaders(hc);
   } else {
     NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
+    return NS_ERROR_FAILURE;
   }
+  return NS_OK;
 }
 
 nsresult ChannelMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   mCacheStream.Close();
   CloseChannel();
@@ -941,16 +941,19 @@ ChannelMediaResource::RecreateChannel()
   // We have cached the Content-Type, which should not change. Give a hint to
   // the channel to avoid a sniffing failure, which would be expected because we
   // are probably seeking in the middle of the bitstream, and sniffing relies
   // on the presence of a magic number at the beginning of the stream.
   NS_ASSERTION(!GetContentType().IsEmpty(),
       "When recreating a channel, we should know the Content-Type.");
   mChannel->SetContentType(GetContentType());
 
+  // Tell the cache to reset the download status when the channel is reopened.
+  mCacheStream.NotifyChannelRecreated();
+
   return rv;
 }
 
 void
 ChannelMediaResource::DoNotifyDataReceived()
 {
   mDataReceivedEvent.Revoke();
   mDecoder->NotifyBytesDownloaded();
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -634,17 +634,17 @@ protected:
                            uint32_t aCount);
   nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
 
   // Opens the channel, using an HTTP byte range request to start at mOffset
   // if possible. Main thread only.
   nsresult OpenChannel(nsIStreamListener** aStreamListener);
   nsresult RecreateChannel();
   // Add headers to HTTP request. Main thread only.
-  void SetupChannelHeaders();
+  nsresult SetupChannelHeaders();
   // Closes the channel. Main thread only.
   void CloseChannel();
 
   // Parses 'Content-Range' header and returns results via parameters.
   // Returns error if header is not available, values are not parse-able or
   // values are out of range.
   nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
                                    int64_t& aRangeStart,