Bug 468190 - ended reports true when seeking after playback ended; r=chris.double sr=roc
authorMatthew Gregan <kinetik@flim.org>
Sun, 14 Dec 2008 19:02:54 +0100
changeset 22783 3d8e98242012776223227c405810847b29b2efc4
parent 22782 6539acbc98f8c744b0c5b7f5e88ba6acb49a5641
child 22784 c062fec2a50aa6ca1d4dcea023a4290852057c78
push id4170
push usersgautherie.bz@free.fr
push dateSun, 14 Dec 2008 18:04:17 +0000
treeherdermozilla-central@3d8e98242012 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschris.double, roc
bugs468190
milestone1.9.2a1pre
Bug 468190 - ended reports true when seeking after playback ended; r=chris.double sr=roc
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/video/public/nsMediaDecoder.h
content/media/video/public/nsOggDecoder.h
content/media/video/public/nsWaveDecoder.h
content/media/video/src/nsOggDecoder.cpp
content/media/video/src/nsWaveDecoder.cpp
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -209,19 +209,16 @@ protected:
   // Size of the media. Updated by the decoder on the main thread if
   // it changes. Defaults to a width and height of -1 if not set.
   nsIntSize mMediaSize;
 
   // If true then we have begun downloading the media content.
   // Set to false when completed, or not yet started.
   PRPackedBool mBegun;
 
-  // If truen then the video playback has completed.
-  PRPackedBool mEnded;
-
   // True when the decoder has loaded enough data to display the
   // first frame of the content.
   PRPackedBool mLoadedFirstFrame;
 
   // Indicates whether current playback is a result of user action
   // (ie. calling of the Play method), or automatic playback due to
   // the 'autoplay' attribute being set. A true value indicates the 
   // latter case.
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -107,17 +107,17 @@ NS_IMETHODIMP nsHTMLMediaElement::GetErr
   NS_IF_ADDREF(*aError = mError);
 
   return NS_OK;
 }
 
 /* readonly attribute boolean ended; */
 NS_IMETHODIMP nsHTMLMediaElement::GetEnded(PRBool *aEnded)
 {
-  *aEnded = mEnded;
+  *aEnded = mDecoder ? mDecoder->IsEnded() : PR_FALSE;
 
   return NS_OK;
 }
 
 /* readonly attribute DOMString currentSrc; */
 NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
 {
   nsCAutoString src;
@@ -186,17 +186,16 @@ nsresult nsHTMLMediaElement::LoadWithCha
   if (aChannel) {
     rv = InitializeDecoderForChannel(aChannel, aListener);
   } else {
     rv = PickMediaElement();
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   mBegun = PR_TRUE;
-  mEnded = PR_FALSE;
 
   DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 /* readonly attribute unsigned short readyState; */
 NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(PRUint16 *aReadyState)
@@ -338,17 +337,16 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMut
 
 nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
   : nsGenericHTMLElement(aNodeInfo),
     mNetworkState(nsIDOMHTMLMediaElement::EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::DATA_UNAVAILABLE),
     mMutedVolume(0.0),
     mMediaSize(-1,-1),
     mBegun(PR_FALSE),
-    mEnded(PR_FALSE),
     mLoadedFirstFrame(PR_FALSE),
     mAutoplaying(PR_TRUE),
     mPaused(PR_TRUE),
     mMuted(PR_FALSE),
     mIsDoneAddingChildren(!aFromParser),
     mPlayingBeforeSeek(PR_FALSE)
 {
 }
@@ -369,18 +367,17 @@ nsHTMLMediaElement::Play(void)
 
   nsresult rv;
 
   if (mNetworkState == nsIDOMHTMLMediaElement::EMPTY) {
     rv = Load();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (mEnded) {
-    mEnded = PR_FALSE;
+  if (mDecoder->IsEnded()) {
     SetCurrentTime(0);
   }
 
   // TODO: If the playback has ended, then the user agent must set 
   // currentLoop to zero and seek to the effective start.
   // TODO: The playback rate must be set to the default playback rate.
   rv = mDecoder->Play();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -702,17 +699,16 @@ void nsHTMLMediaElement::FirstFrameLoade
   mLoadedFirstFrame = PR_TRUE;
   DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedfirstframe"));
   DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canshowcurrentframe"));
 }
 
 void nsHTMLMediaElement::ResourceLoaded()
 {
   mBegun = PR_FALSE;
-  mEnded = PR_FALSE;
   mNetworkState = nsIDOMHTMLMediaElement::LOADED;
   ChangeReadyState(nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH);
 
   DispatchProgressEvent(NS_LITERAL_STRING("load"));
 }
 
 void nsHTMLMediaElement::NetworkError()
 {
@@ -720,18 +716,18 @@ void nsHTMLMediaElement::NetworkError()
   mBegun = PR_FALSE;
   DispatchProgressEvent(NS_LITERAL_STRING("error"));
   mNetworkState = nsIDOMHTMLMediaElement::EMPTY;
   DispatchSimpleEvent(NS_LITERAL_STRING("empty"));
 }
 
 void nsHTMLMediaElement::PlaybackEnded()
 {
+  NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
   mBegun = PR_FALSE;
-  mEnded = PR_TRUE;
   mPaused = PR_TRUE;
   DispatchSimpleEvent(NS_LITERAL_STRING("ended"));
 }
 
 void nsHTMLMediaElement::CanPlayThrough()
 {
   ChangeReadyState(nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH);
 }
@@ -866,23 +862,25 @@ PRBool nsHTMLMediaElement::IsActivelyPla
   //   playback has not stopped due to errors, 
   //   and the element has not paused for user interaction
   return 
     !mPaused && 
     (mReadyState == nsIDOMHTMLMediaElement::CAN_PLAY || 
      mReadyState == nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH) &&
     !IsPlaybackEnded();
 }
+
 PRBool nsHTMLMediaElement::IsPlaybackEnded() const
 {
   // TODO:
   //   the current playback position is equal to the effective end of the media resource, 
   //   and the currentLoop attribute is equal to playCount-1. 
   //   See bug 449157.
-  return mNetworkState >= nsIDOMHTMLMediaElement::LOADED_METADATA && mEnded;
+  return mNetworkState >= nsIDOMHTMLMediaElement::LOADED_METADATA &&
+    mDecoder ? mDecoder->IsEnded() : PR_FALSE;
 }
 
 nsIPrincipal*
 nsHTMLMediaElement::GetCurrentPrincipal()
 {
   if (!mDecoder)
     return nsnull;
 
--- a/content/media/video/public/nsMediaDecoder.h
+++ b/content/media/video/public/nsMediaDecoder.h
@@ -129,16 +129,20 @@ class nsMediaDecoder : public nsIObserve
 
   // Called if the media file encounters a network error.
   virtual void NetworkError() = 0;
 
   // Call from any thread safely. Return PR_TRUE if we are currently
   // seeking in the media resource.
   virtual PRBool IsSeeking() const = 0;
 
+  // Return PR_TRUE if the decoder has reached the end of playback.
+  // Call in the main thread only.
+  virtual PRBool IsEnded() const = 0;
+
   // Return the current number of bytes loaded from the video file.
   // This is used for progress events.
   virtual PRUint64 GetBytesLoaded() = 0;
 
   // Return the size of the video file in bytes. Return 0 if the
   // size is unknown or the stream is infinite.
   virtual PRInt64 GetTotalBytes() = 0;
 
--- a/content/media/video/public/nsOggDecoder.h
+++ b/content/media/video/public/nsOggDecoder.h
@@ -334,16 +334,20 @@ class nsOggDecoder : public nsMediaDecod
   // Called if the media file encounters a network error.
   // Call on the main thread only.
   virtual void NetworkError();
 
   // Call from any thread safely. Return PR_TRUE if we are currently
   // seeking in the media resource.
   virtual PRBool IsSeeking() const;
 
+  // Return PR_TRUE if the decoder has reached the end of playback.
+  // Call on the main thread only.
+  virtual PRBool IsEnded() const;
+
   // Get the size of the media file in bytes. Called on the main thread only.
   virtual void SetTotalBytes(PRInt64 aBytes);
 
   // Set a flag indicating whether seeking is supported
   virtual void SetSeekable(PRBool aSeekable);
 
   // Return PR_TRUE if seeking is supported.
   virtual PRBool GetSeekable();
--- a/content/media/video/public/nsWaveDecoder.h
+++ b/content/media/video/public/nsWaveDecoder.h
@@ -168,16 +168,19 @@ class nsWaveDecoder : public nsMediaDeco
   // Set the current time of the media to aTime.  This may cause mStream to
   // create a new channel to fetch data from the appropriate position in the
   // stream.
   virtual nsresult Seek(float aTime);
 
   // Report whether the decoder is currently seeking.
   virtual PRBool IsSeeking() const;
 
+  // Report whether the decoder has reached end of playback.
+  virtual PRBool IsEnded() const;
+
   // Start downloading the media at the specified URI.  The media's metadata
   // will be parsed and made available as the load progresses.
   virtual nsresult Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStreamListener);
 
   // Called by mStream (and possibly the nsChannelToPipeListener used
   // internally by mStream) when the stream has completed loading.
   virtual void ResourceLoaded();
 
@@ -254,21 +257,22 @@ private:
 
   // The media time of the last requested seek.  This has not been validated
   // against the current media, so may be out of bounds.  Set when
   // Seek(float) is called, and passed to the state machine when the
   // SeekStarted event fires to tell it to update its time offset.  The
   // state machine will validate the offset against the current media.
   float mTimeOffset;
 
-  // Copy of the current time and duration when the state machine was
-  // disposed.  Used to respond to time and duration queries with sensible
-  // values after playback has ended.
+  // Copy of the current time, duration, and ended state when the state
+  // machine was disposed.  Used to respond to time and duration queries
+  // with sensible values after the state machine is destroyed.
   float mEndedCurrentTime;
   float mEndedDuration;
+  PRPackedBool mEnded;
 
   // True if we have registered a shutdown observer.
   PRPackedBool mNotifyOnShutdown;
 
   // True if the media resource is seekable.
   PRPackedBool mSeekable;
 };
 
--- a/content/media/video/src/nsOggDecoder.cpp
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -1485,17 +1485,22 @@ void nsOggDecoder::NetworkError()
 
   if (mElement)
     mElement->NetworkError();
   Stop();
 }
 
 PRBool nsOggDecoder::IsSeeking() const
 {
-  return mPlayState == PLAY_STATE_SEEKING;
+  return mPlayState == PLAY_STATE_SEEKING || mNextState == PLAY_STATE_SEEKING;
+}
+
+PRBool nsOggDecoder::IsEnded() const
+{
+  return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN;
 }
 
 void nsOggDecoder::PlaybackEnded()
 {
   if (mShuttingDown || mPlayState == nsOggDecoder::PLAY_STATE_SEEKING)
     return;
 
   Stop();
--- a/content/media/video/src/nsWaveDecoder.cpp
+++ b/content/media/video/src/nsWaveDecoder.cpp
@@ -140,16 +140,19 @@ public:
 
   // Returns the current playback position in the audio stream in seconds.
   // Threadsafe.
   float GetCurrentTime();
 
   // Returns true if the state machine is seeking.  Threadsafe.
   PRBool IsSeeking();
 
+  // Returns true if the state machine has reached the end of playback.  Threadsafe.
+  PRBool IsEnded();
+
   // Called by the decoder to indicate that the media stream has closed.
   void StreamEnded();
 
   // Main state machine loop.  Runs forever, until shutdown state is reached.
   NS_IMETHOD Run();
 
   // Called by the decoder when the SeekStarted event runs.  This ensures
   // the current time offset of the state machine is updated to the new seek
@@ -423,17 +426,24 @@ nsWaveStateMachine::GetCurrentTime()
   }
   return float(time + mTimeOffset);
 }
 
 PRBool
 nsWaveStateMachine::IsSeeking()
 {
   nsAutoMonitor monitor(mMonitor);
-  return mState == STATE_SEEKING;
+  return mState == STATE_SEEKING || mNextState == STATE_SEEKING;
+}
+
+PRBool
+nsWaveStateMachine::IsEnded()
+{
+  nsAutoMonitor monitor(mMonitor);
+  return mState == STATE_ENDED || mState == STATE_SHUTDOWN;
 }
 
 void
 nsWaveStateMachine::StreamEnded()
 {
   nsAutoMonitor monitor(mMonitor);
   mExpectMoreData = PR_FALSE;
 }
@@ -462,17 +472,19 @@ nsWaveStateMachine::Run()
             mMetadataValid = PR_TRUE;
             event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MetadataLoaded);
             newState = mNextState;
           } else {
             event = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, MediaErrorDecode);
             newState = STATE_ERROR;
           }
 
-          NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+          if (event) {
+            NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+          }
           ChangeState(newState);
         }
       }
       break;
 
     case STATE_BUFFERING:
       if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart) < mBufferingWait) &&
           mStream->DownloadRate() >= 0 &&
@@ -947,16 +959,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDeco
 
 nsWaveDecoder::nsWaveDecoder()
   : mBytesDownloaded(0),
     mInitialVolume(1.0),
     mStream(nsnull),
     mTimeOffset(0.0),
     mEndedCurrentTime(0.0),
     mEndedDuration(std::numeric_limits<float>::quiet_NaN()),
+    mEnded(PR_FALSE),
     mNotifyOnShutdown(PR_FALSE),
     mSeekable(PR_TRUE)
 {
   MOZ_COUNT_CTOR(nsWaveDecoder);
 }
 
 nsWaveDecoder::~nsWaveDecoder()
 {
@@ -1070,19 +1083,23 @@ nsWaveDecoder::Stop()
     mPlaybackStateMachine->Shutdown();
   }
 
   if (mStream) {
     mStream->Cancel();
   }
 
   if (mPlaybackThread) {
+    mPlaybackThread->Shutdown();
+  }
+
+  if (mPlaybackStateMachine) {
     mEndedCurrentTime = mPlaybackStateMachine->GetCurrentTime();
     mEndedDuration = mPlaybackStateMachine->GetDuration();
-    mPlaybackThread->Shutdown();
+    mEnded = mPlaybackStateMachine->IsEnded();
   }
 
   mPlaybackThread = nsnull;
   mPlaybackStateMachine = nsnull;
   mStream = nsnull;
 
   UnregisterShutdownObserver();
 }
@@ -1188,16 +1205,25 @@ PRBool
 nsWaveDecoder::IsSeeking() const
 {
   if (mPlaybackStateMachine) {
     return mPlaybackStateMachine->IsSeeking();
   }
   return PR_FALSE;
 }
 
+PRBool
+nsWaveDecoder::IsEnded() const
+{
+  if (mPlaybackStateMachine) {
+    return mPlaybackStateMachine->IsEnded();
+  }
+  return mEnded;
+}
+
 PRUint64
 nsWaveDecoder::GetBytesLoaded()
 {
   return mBytesDownloaded;
 }
 
 PRInt64
 nsWaveDecoder::GetTotalBytes()