diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h
index 578a2201e553..655ab79921c2 100644
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -214,9 +214,6 @@ protected:
// 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;
diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp
index decc38a27eec..f2d42d5cf3e1 100644
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -112,7 +112,7 @@ NS_IMETHODIMP nsHTMLMediaElement::GetError(nsIDOMHTMLMediaError * *aError)
/* readonly attribute boolean ended; */
NS_IMETHODIMP nsHTMLMediaElement::GetEnded(PRBool *aEnded)
{
- *aEnded = mEnded;
+ *aEnded = mDecoder ? mDecoder->IsEnded() : PR_FALSE;
return NS_OK;
}
@@ -191,7 +191,6 @@ nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
NS_ENSURE_SUCCESS(rv, rv);
mBegun = PR_TRUE;
- mEnded = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
@@ -343,7 +342,6 @@ nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParse
mMutedVolume(0.0),
mMediaSize(-1,-1),
mBegun(PR_FALSE),
- mEnded(PR_FALSE),
mLoadedFirstFrame(PR_FALSE),
mAutoplaying(PR_TRUE),
mPaused(PR_TRUE),
@@ -374,8 +372,7 @@ nsHTMLMediaElement::Play(void)
NS_ENSURE_SUCCESS(rv, rv);
}
- if (mEnded) {
- mEnded = PR_FALSE;
+ if (mDecoder->IsEnded()) {
SetCurrentTime(0);
}
@@ -707,7 +704,6 @@ void nsHTMLMediaElement::FirstFrameLoaded()
void nsHTMLMediaElement::ResourceLoaded()
{
mBegun = PR_FALSE;
- mEnded = PR_FALSE;
mNetworkState = nsIDOMHTMLMediaElement::LOADED;
ChangeReadyState(nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH);
@@ -725,8 +721,8 @@ void nsHTMLMediaElement::NetworkError()
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"));
}
@@ -871,13 +867,15 @@ PRBool nsHTMLMediaElement::IsActivelyPlaying() const
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*
diff --git a/content/media/video/public/nsMediaDecoder.h b/content/media/video/public/nsMediaDecoder.h
index 5e8e03412208..f103ad05ef20 100644
--- a/content/media/video/public/nsMediaDecoder.h
+++ b/content/media/video/public/nsMediaDecoder.h
@@ -134,6 +134,10 @@ class nsMediaDecoder : public nsIObserver
// 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;
diff --git a/content/media/video/public/nsOggDecoder.h b/content/media/video/public/nsOggDecoder.h
index 7dc57469b559..bd7dede3c7f6 100644
--- a/content/media/video/public/nsOggDecoder.h
+++ b/content/media/video/public/nsOggDecoder.h
@@ -339,6 +339,10 @@ class nsOggDecoder : public nsMediaDecoder
// 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);
diff --git a/content/media/video/public/nsWaveDecoder.h b/content/media/video/public/nsWaveDecoder.h
index 29ab1fc40309..70b77a5345fc 100644
--- a/content/media/video/public/nsWaveDecoder.h
+++ b/content/media/video/public/nsWaveDecoder.h
@@ -173,6 +173,9 @@ class nsWaveDecoder : public nsMediaDecoder
// 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);
@@ -259,11 +262,12 @@ private:
// 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;
diff --git a/content/media/video/src/nsOggDecoder.cpp b/content/media/video/src/nsOggDecoder.cpp
index 1fef7e314550..2fbfa773ddcb 100644
--- a/content/media/video/src/nsOggDecoder.cpp
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -1490,7 +1490,12 @@ void nsOggDecoder::NetworkError()
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()
diff --git a/content/media/video/src/nsWaveDecoder.cpp b/content/media/video/src/nsWaveDecoder.cpp
index 76390739bc54..149f503dd5cb 100644
--- a/content/media/video/src/nsWaveDecoder.cpp
+++ b/content/media/video/src/nsWaveDecoder.cpp
@@ -145,6 +145,9 @@ public:
// 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();
@@ -428,7 +431,14 @@ 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
@@ -467,7 +477,9 @@ nsWaveStateMachine::Run()
newState = STATE_ERROR;
}
- NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+ if (event) {
+ NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+ }
ChangeState(newState);
}
}
@@ -952,6 +964,7 @@ nsWaveDecoder::nsWaveDecoder()
mTimeOffset(0.0),
mEndedCurrentTime(0.0),
mEndedDuration(std::numeric_limits::quiet_NaN()),
+ mEnded(PR_FALSE),
mNotifyOnShutdown(PR_FALSE),
mSeekable(PR_TRUE)
{
@@ -1075,9 +1088,13 @@ nsWaveDecoder::Stop()
}
if (mPlaybackThread) {
+ mPlaybackThread->Shutdown();
+ }
+
+ if (mPlaybackStateMachine) {
mEndedCurrentTime = mPlaybackStateMachine->GetCurrentTime();
mEndedDuration = mPlaybackStateMachine->GetDuration();
- mPlaybackThread->Shutdown();
+ mEnded = mPlaybackStateMachine->IsEnded();
}
mPlaybackThread = nsnull;
@@ -1193,6 +1210,15 @@ nsWaveDecoder::IsSeeking() const
return PR_FALSE;
}
+PRBool
+nsWaveDecoder::IsEnded() const
+{
+ if (mPlaybackStateMachine) {
+ return mPlaybackStateMachine->IsEnded();
+ }
+ return mEnded;
+}
+
PRUint64
nsWaveDecoder::GetBytesLoaded()
{