Backed out changeset 78424c3ea6cb (bug 883731)
authorEd Morley <emorley@mozilla.com>
Wed, 19 Jun 2013 09:51:05 +0100
changeset 147059 5199b67ba0297db108386fc9d4d7c045b2587654
parent 147058 abe93cefa9cfde1c3d7400df9638286d74688b61
child 147060 b22438ffe2680871844af938a6e7832a18871b6d
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs883731
milestone24.0a1
backs out78424c3ea6cb266dafe113455682ecc0703d4364
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
Backed out changeset 78424c3ea6cb (bug 883731)
content/html/content/public/HTMLMediaElement.h
content/html/content/src/HTMLMediaElement.cpp
content/media/MediaDecoder.cpp
content/media/MediaDecoder.h
content/media/MediaDecoderOwner.h
content/media/MediaResource.cpp
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -149,17 +149,23 @@ public:
   virtual void MetadataLoaded(int aChannels,
                               int aRate,
                               bool aHasAudio,
                               bool aHasVideo,
                               const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
 
   // Called by the video decoder object, on the main thread,
   // when it has read the first frame of the video
-  virtual void FirstFrameLoaded() MOZ_FINAL MOZ_OVERRIDE;
+  // aResourceFullyLoaded should be true if the resource has been
+  // fully loaded and the caller will call ResourceLoaded next.
+  virtual void FirstFrameLoaded(bool aResourceFullyLoaded) MOZ_FINAL MOZ_OVERRIDE;
+
+  // Called by the video decoder object, on the main thread,
+  // when the resource has completed downloading.
+  virtual void ResourceLoaded() MOZ_FINAL MOZ_OVERRIDE;
 
   // Called by the video decoder object, on the main thread,
   // when the resource has a network error during loading.
   virtual void NetworkError() MOZ_FINAL MOZ_OVERRIDE;
 
   // Called by the video decoder object, on the main thread, when the
   // resource has a decode error during metadata loading or decoding.
   virtual void DecodeError() MOZ_FINAL MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -2576,17 +2576,17 @@ public:
     if (mElement && mHaveCurrentData) {
       mElement->FireTimeUpdate(true);
     }
   }
   void DoNotifyHaveCurrentData()
   {
     mHaveCurrentData = true;
     if (mElement) {
-      mElement->FirstFrameLoaded();
+      mElement->FirstFrameLoaded(false);
     }
     UpdateReadyStateForData();
     DoNotifyOutput();
   }
 
   // These notifications run on the media graph thread so we need to
   // dispatch events to the main thread.
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked)
@@ -2666,17 +2666,18 @@ void HTMLMediaElement::SetupSrcMediaStre
   GetSrcMediaStream()->AddAudioOutput(this);
   GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
     GetSrcMediaStream()->AddVideoOutput(container);
   }
 
   AddRemoveSelfReference();
-  // FirstFrameLoaded() will be called when the stream has current data.
+  // FirstFrameLoaded(false) will be called when the stream has current data,
+  // to complete the setup by entering the HAVE_CURRENT_DATA state.
 }
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
   if (stream) {
     stream->RemoveListener(mSrcStreamListener);
   }
@@ -2738,24 +2739,25 @@ void HTMLMediaElement::MetadataLoaded(in
   // If this element had a video track, but consists only of an audio track now,
   // delete the VideoFrameContainer. This happens when the src is changed to an
   // audio only file.
   if (!aHasVideo) {
     mVideoFrameContainer = nullptr;
   }
 }
 
-void HTMLMediaElement::FirstFrameLoaded()
+void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
 {
   ChangeDelayLoadStatus(false);
   UpdateReadyStateForData(NEXT_FRAME_UNAVAILABLE);
 
   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
 
   if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
+      !aResourceFullyLoaded &&
       !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mSuspendedAfterFirstFrame = true;
     mDecoder->Suspend();
   } else if (mLoadedFirstFrame &&
              mDownloadSuspendedByCache &&
              mDecoder &&
              !mDecoder->IsEnded()) {
@@ -2767,16 +2769,36 @@ void HTMLMediaElement::FirstFrameLoaded(
     // that the download was suspended before the first frame was loaded.
     // Don't force this transition if the decoder is in ended state; the
     // readyState should remain at HAVE_CURRENT_DATA in this case.
     ChangeReadyState(HAVE_ENOUGH_DATA);
     return;
   }
 }
 
+void HTMLMediaElement::ResourceLoaded()
+{
+  NS_ASSERTION(!mSrcStream, "Don't call this for streams");
+
+  mBegun = false;
+  mNetworkState = NETWORK_IDLE;
+  AddRemoveSelfReference();
+  if (mReadyState >= HAVE_METADATA) {
+    // MediaStream sources are put into HAVE_CURRENT_DATA state here on setup. If the
+    // stream is not blocked, we will receive a notification that will put it
+    // into HAVE_ENOUGH_DATA state.
+    ChangeReadyState(mSrcStream ? HAVE_CURRENT_DATA
+                     : HAVE_ENOUGH_DATA);
+  }
+  // Ensure a progress event is dispatched at the end of download.
+  DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
+  // The download has stopped.
+  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+}
+
 void HTMLMediaElement::NetworkError()
 {
   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
 }
 
 void HTMLMediaElement::DecodeError()
 {
   nsAutoString src;
@@ -2911,35 +2933,33 @@ bool HTMLMediaElement::ShouldCheckAllowO
 }
 
 void HTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
 {
   mLastNextFrameStatus = aNextFrame;
 
   if (mReadyState < HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
-    // on its own thread before MetadataLoaded gets
+    // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
   if (mReadyState > HAVE_METADATA &&
       mDownloadSuspendedByCache &&
       mDecoder &&
       !mDecoder->IsEnded()) {
     // The decoder has signalled that the download has been suspended by the
     // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
     // script waiting for a "canplaythrough" event; without this forced
     // transition, we will never fire the "canplaythrough" event if the
     // media cache is too small, and scripts are bound to fail. Don't force
     // this transition if the decoder is in ended state; the readyState
     // should remain at HAVE_CURRENT_DATA in this case.
-    // Note that this state transition includes the case where we finished
-    // downloaded the whole data stream.
     ChangeReadyState(HAVE_ENOUGH_DATA);
     return;
   }
 
   if (mReadyState < HAVE_CURRENT_DATA && mHasVideo) {
     VideoFrameContainer* container = GetVideoFrameContainer();
     if (container && mMediaSize == nsIntSize(-1,-1)) {
       // No frame has been set yet. Don't advance.
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -367,16 +367,17 @@ MediaDecoder::MediaDecoder() :
   mRequestedSeekTime(-1.0),
   mDuration(-1),
   mTransportSeekable(true),
   mMediaSeekable(true),
   mReentrantMonitor("media.decoder"),
   mIsDormant(false),
   mPlayState(PLAY_STATE_PAUSED),
   mNextState(PLAY_STATE_PAUSED),
+  mCalledResourceLoaded(false),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mTriggerPlaybackEndedWhenSourceStreamFinishes(false),
   mOwner(nullptr),
   mFrameBufferLength(0),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
@@ -737,23 +738,31 @@ void MediaDecoder::MetadataLoaded(int aC
 
   if (mOwner) {
     // Make sure the element and the frame (if any) are told about
     // our new size.
     Invalidate();
     mOwner->MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags);
   }
 
-  StartProgress();
+  if (!mCalledResourceLoaded) {
+    StartProgress();
+  } else if (mOwner) {
+    // Resource was loaded during metadata loading, when progress
+    // events are being ignored. Fire the final progress event.
+    mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
+  }
 
   // Only inform the element of FirstFrameLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  bool notifyResourceIsLoaded = !mCalledResourceLoaded &&
+                                IsDataCachedToEndOfResource();
   if (mOwner) {
-    mOwner->FirstFrameLoaded();
+    mOwner->FirstFrameLoaded(notifyResourceIsLoaded);
   }
 
   // This can run cache callbacks.
   mResource->EnsureCacheUpToDate();
 
   // The element can run javascript via events
   // before reaching here, so only change the
   // state if we're still set to the original
@@ -762,21 +771,55 @@ void MediaDecoder::MetadataLoaded(int aC
     if (mRequestedSeekTime >= 0.0) {
       ChangeState(PLAY_STATE_SEEKING);
     }
     else {
       ChangeState(mNextState);
     }
   }
 
+  if (notifyResourceIsLoaded) {
+    ResourceLoaded();
+  }
+
   // Run NotifySuspendedStatusChanged now to give us a chance to notice
   // that autoplay should run.
   NotifySuspendedStatusChanged();
 }
 
+void MediaDecoder::ResourceLoaded()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Don't handle ResourceLoaded if we are shutting down, or if
+  // we need to ignore progress data due to seeking (in the case
+  // that the seek results in reaching end of file, we get a bogus call
+  // to ResourceLoaded).
+  if (mShuttingDown)
+    return;
+
+  {
+    // If we are seeking or loading then the resource loaded notification we get
+    // should be ignored, since it represents the end of the seek request.
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING)
+      return;
+
+    Progress(false);
+
+    mCalledResourceLoaded = true;
+    StopProgress();
+  }
+
+  // Ensure the final progress event gets fired
+  if (mOwner) {
+    mOwner->ResourceLoaded();
+  }
+}
+
 void MediaDecoder::NetworkError()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mShuttingDown)
     return;
 
   if (mOwner)
     mOwner->NetworkError();
@@ -972,22 +1015,22 @@ void MediaDecoder::NotifyDownloadEnded(n
   }
 
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     UpdatePlaybackRate();
   }
 
   if (NS_SUCCEEDED(aStatus)) {
-    UpdateReadyStateForData();
-    // A final progress event will be fired by the MediaResource calling
-    // DownloadSuspended on the element.
-  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
+    ResourceLoaded();
+  }
+  else if (aStatus != NS_BASE_STREAM_CLOSED) {
     NetworkError();
   }
+  UpdateReadyStateForData();
 }
 
 void MediaDecoder::NotifyPrincipalChanged()
 {
   if (mOwner) {
     mOwner->NotifyDecoderPrincipalChanged();
   }
 }
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -283,16 +283,19 @@ public:
   virtual nsresult Load(MediaResource* aResource,
                         nsIStreamListener** aListener,
                         MediaDecoder* aCloneDonor);
 
   // Called in |Load| to open the media resource.
   nsresult OpenResource(MediaResource* aResource,
                         nsIStreamListener** aStreamListener);
 
+  // Called when the video file has completed downloading.
+  virtual void ResourceLoaded();
+
   // Called if the media file encounters a network error.
   virtual void NetworkError();
 
   // Get the current MediaResource being used. Its URI will be returned
   // by currentSrc. Returns what was passed to Load(), if Load() has been called.
   // Note: The MediaResource is refcounted, but it outlives the MediaDecoder,
   // so it's OK to use the reference returned by this function without
   // refcounting, *unless* you need to store and use the reference after the
@@ -1012,16 +1015,21 @@ public:
   // The state to change to after a seek or load operation.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
   // Any change to the state must call NotifyAll on the monitor.
   // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.
   PlayState mNextState;
 
+  // True when we have fully loaded the resource and reported that
+  // to the element (i.e. reached NETWORK_LOADED state).
+  // Accessed on the main thread only.
+  bool mCalledResourceLoaded;
+
   // True when seeking or otherwise moving the play position around in
   // such a manner that progress event data is inaccurate. This is set
   // during seek and duration operations to prevent the progress indicator
   // from jumping around. Read/Write from any thread. Must have decode monitor
   // locked before accessing.
   bool mIgnoreProgressData;
 
   // True if the stream is infinite (e.g. a webradio).
--- a/content/media/MediaDecoderOwner.h
+++ b/content/media/MediaDecoderOwner.h
@@ -60,17 +60,21 @@ public:
                               bool aHasAudio,
                               bool aHasVideo,
                               const MetadataTags* aTags) = 0;
 
   // Called by the video decoder object, on the main thread,
   // when it has read the first frame of the video
   // aResourceFullyLoaded should be true if the resource has been
   // fully loaded and the caller will call ResourceLoaded next.
-  virtual void FirstFrameLoaded() = 0;
+  virtual void FirstFrameLoaded(bool aResourceFullyLoaded) = 0;
+
+  // Called by the video decoder object, on the main thread,
+  // when the resource has completed downloading.
+  virtual void ResourceLoaded() = 0;
 
   // Called by the video decoder object, on the main thread,
   // when the resource has a network error during loading.
   virtual void NetworkError() = 0;
 
   // Called by the video decoder object, on the main thread, when the
   // resource has a decode error during metadata loading or decoding.
   virtual void DecodeError() = 0;
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -979,28 +979,21 @@ ChannelMediaResource::CacheClientNotifyD
 }
 
 class DataEnded : public nsRunnable {
 public:
   DataEnded(MediaDecoder* aDecoder, nsresult aStatus) :
     mDecoder(aDecoder), mStatus(aStatus) {}
   NS_IMETHOD Run() {
     mDecoder->NotifyDownloadEnded(mStatus);
-    MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
-    if (owner) {
-      HTMLMediaElement* element = owner->GetMediaElement();
-      if (element) {
-        element->DownloadSuspended();
-      }
-    }
     return NS_OK;
   }
 private:
   nsRefPtr<MediaDecoder> mDecoder;
-  nsresult               mStatus;
+  nsresult                 mStatus;
 };
 
 void
 ChannelMediaResource::CacheClientNotifyDataEnded(nsresult aStatus)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
   // NOTE: this can be called with the media cache lock held, so don't
   // block or do anything which might try to acquire a lock!
@@ -1343,17 +1336,17 @@ public:
   }
   virtual int64_t GetCachedDataEnd(int64_t aOffset) {
     MutexAutoLock lock(mLock);
 
     EnsureSizeInitialized();
     return std::max(aOffset, mSize);
   }
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
-  virtual bool    IsSuspendedByCache() { return true; }
+  virtual bool    IsSuspendedByCache() { return false; }
   virtual bool    IsSuspended() { return false; }
   virtual bool    IsTransportSeekable() MOZ_OVERRIDE { return true; }
 
   nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
 private:
   // Ensures mSize is initialized, if it can be.
   // mLock must be held when this is called, and mInput must be non-null.
@@ -1393,28 +1386,17 @@ public:
     MOZ_COUNT_CTOR(LoadedEvent);
   }
   ~LoadedEvent()
   {
     MOZ_COUNT_DTOR(LoadedEvent);
   }
 
   NS_IMETHOD Run() {
-    // NotifySuspendedStatusChanged will tell the element that download
-    // has been suspended "by the cache", which is true since we never downloaded
-    // anything. The element can then transition to HAVE_ENOUGH_DATA.
-    mDecoder->NotifySuspendedStatusChanged();
     mDecoder->NotifyDownloadEnded(NS_OK);
-    MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
-    if (owner) {
-      HTMLMediaElement* element = owner->GetMediaElement();
-      if (element) {
-        element->DownloadSuspended();
-      }
-    }
     return NS_OK;
   }
 
 private:
   nsRefPtr<MediaDecoder> mDecoder;
 };
 
 void FileMediaResource::EnsureSizeInitialized()