Bug 476811. Fire 'waiting' event when the decoder starts buffering. r=doublec

--HG--
extra : rebase_source : 1ac5d728e4f3f3e83de61d0806b20f328cdf380c
This commit is contained in:
Robert O'Callahan 2009-02-11 14:43:45 +13:00
Родитель bf033eb12c
Коммит 520f7cff94
6 изменённых файлов: 85 добавлений и 43 удалений

Просмотреть файл

@ -134,7 +134,16 @@ public:
// the data for the next frame is available. This method will
// decide whether to set the ready state to HAVE_CURRENT_DATA,
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
void UpdateReadyStateForData(PRBool aNextFrameAvailable);
enum NextFrameStatus {
// The next frame of audio/video is available
NEXT_FRAME_AVAILABLE,
// The next frame of audio/video is unavailable because the decoder
// is paused while it buffers up data
NEXT_FRAME_UNAVAILABLE_BUFFERING,
// The next frame of audio/video is unavailable for some other reasons
NEXT_FRAME_UNAVAILABLE
};
void UpdateReadyStateForData(NextFrameStatus aNextFrame);
// Use this method to change the mReadyState member, so required
// events can be fired.
@ -272,4 +281,8 @@ protected:
// to ensure that the playstate doesn't change when the user goes Forward/Back
// from the bfcache.
PRPackedBool mPausedBeforeFreeze;
// PR_TRUE if we've reported a "waiting" event since the last
// readyState change to HAVE_CURRENT_DATA.
PRPackedBool mWaitingFired;
};

Просмотреть файл

@ -506,7 +506,8 @@ nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParse
mPaused(PR_TRUE),
mMuted(PR_FALSE),
mIsDoneAddingChildren(!aFromParser),
mPlayingBeforeSeek(PR_FALSE)
mPlayingBeforeSeek(PR_FALSE),
mWaitingFired(PR_FALSE)
{
}
@ -1018,16 +1019,21 @@ PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
// or other conditions are worse than expected
static const PRInt32 gDownloadSizeSafetyMargin = 1000000;
void nsHTMLMediaElement::UpdateReadyStateForData(PRBool aNextFrameAvailable)
void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
{
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
NS_ASSERTION(!aNextFrameAvailable, "How can we have a frame but no metadata?");
NS_ASSERTION(aNextFrame != NEXT_FRAME_AVAILABLE,
"How can we have a frame but no metadata?");
// The arrival of more data can't change us out of this state.
return;
}
if (!aNextFrameAvailable && !mDecoder->IsEnded()) {
if (aNextFrame != NEXT_FRAME_AVAILABLE && !mDecoder->IsEnded()) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
mWaitingFired = PR_TRUE;
}
return;
}
@ -1085,6 +1091,7 @@ void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
if (oldState != mReadyState) {
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_CURRENT_DATA"));
mWaitingFired = PR_FALSE;
}
if (oldState <= nsIDOMHTMLMediaElement::HAVE_METADATA &&
!mLoadedFirstFrame) {

Просмотреть файл

@ -422,10 +422,6 @@ protected:
// Call on the main thread only.
void PlaybackEnded();
// Buffering of data has stopped. Inform the element on the main
// thread.
void BufferingStopped();
// Seeking has stopped. Inform the element on the main
// thread.
void SeekingStopped();
@ -439,16 +435,16 @@ protected:
// This must be called on the main thread only.
void PlaybackPositionChanged();
// Calls mElement->UpdateReadyStateForData, telling it whether we have
// data for the next frame and if we're buffering. Main thread only.
void UpdateReadyStateForData();
private:
// Register/Unregister with Shutdown Observer.
// Call on main thread only.
void RegisterShutdownObserver();
void UnregisterShutdownObserver();
// Calls mElement->UpdateReadyStateForData, telling it whether we have
// data for the next frame.
void UpdateReadyStateForData();
/******
* The following members should be accessed with the decoder lock held.
******/

Просмотреть файл

@ -221,13 +221,10 @@ class nsWaveDecoder : public nsMediaDecoder
// main thread only.
virtual void Resume();
private:
// Change the element's ready state as necessary
// Change the element's ready state as necessary. Main thread only.
void UpdateReadyStateForData();
// Notifies the element that buffering has stopped.
void BufferingStopped();
private:
// Notifies the element that seeking has started.
void SeekingStarted();

Просмотреть файл

@ -308,7 +308,15 @@ public:
// Must be called with the decode monitor held. Can be called by main
// thread.
PRBool HaveNextFrameData() const {
return !mDecodedFrames.IsEmpty();
return !mDecodedFrames.IsEmpty() &&
(mState == DECODER_STATE_DECODING ||
mState == DECODER_STATE_COMPLETED);
}
// Must be called with the decode monitor held. Can be called by main
// thread.
PRBool IsBuffering() const {
return mState == nsOggDecodeStateMachine::DECODER_STATE_BUFFERING;
}
protected:
@ -993,6 +1001,19 @@ nsresult nsOggDecodeStateMachine::Run()
StopPlayback();
}
// We need to tell the element that buffering has started.
// We can't just directly send an asynchronous runnable that
// eventually fires the "waiting" event. The problem is that
// there might be pending main-thread events, such as "data
// received" notifications, that mean we're not actually still
// buffering by the time this runnable executes. So instead
// we just trigger UpdateReadyStateForData; when it runs, it
// will check the current state and decide whether to tell
// the element we're buffering or not.
nsCOMPtr<nsIRunnable> event =
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, UpdateReadyStateForData);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
mBufferingStart = PR_IntervalNow();
double playbackRate = mDecoder->GetStatistics().mPlaybackRate;
mBufferingBytes = BUFFERING_RATE(playbackRate) * BUFFERING_WAIT;
@ -1096,7 +1117,7 @@ nsresult nsOggDecodeStateMachine::Run()
if (mState != DECODER_STATE_BUFFERING) {
nsCOMPtr<nsIRunnable> event =
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, BufferingStopped);
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, UpdateReadyStateForData);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
if (!mPlaying) {
@ -1769,17 +1790,18 @@ void nsOggDecoder::UpdateReadyStateForData()
if (!mElement || mShuttingDown || !mDecodeStateMachine)
return;
PRBool haveNextFrame;
nsHTMLMediaElement::NextFrameStatus frameStatus;
{
nsAutoMonitor mon(mMonitor);
haveNextFrame = mDecodeStateMachine->HaveNextFrameData();
if (mDecodeStateMachine->HaveNextFrameData()) {
frameStatus = nsHTMLMediaElement::NEXT_FRAME_AVAILABLE;
} else if (mDecodeStateMachine->IsBuffering()) {
frameStatus = nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING;
} else {
frameStatus = nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
}
}
mElement->UpdateReadyStateForData(haveNextFrame);
}
void nsOggDecoder::BufferingStopped()
{
UpdateReadyStateForData();
mElement->UpdateReadyStateForData(frameStatus);
}
void nsOggDecoder::SeekingStopped()

Просмотреть файл

@ -174,8 +174,8 @@ public:
// Called on any thread
void NotifyBytesConsumed(PRInt64 aBytes);
// Called by the main thread
PRBool HasPendingData();
// Called by the main thread only
nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus();
private:
// Change the current state and wake the playback thread if it is waiting
@ -492,11 +492,15 @@ nsWaveStateMachine::StreamEnded(PRBool aAtEnd)
}
}
PRBool
nsWaveStateMachine::HasPendingData()
nsHTMLMediaElement::NextFrameStatus
nsWaveStateMachine::GetNextFrameStatus()
{
nsAutoMonitor monitor(mMonitor);
return mPlaybackPosition < mDownloadPosition;
if (mPlaybackPosition < mDownloadPosition)
return nsHTMLMediaElement::NEXT_FRAME_AVAILABLE;
if (mState == STATE_BUFFERING)
return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING;
return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
}
NS_IMETHODIMP
@ -549,7 +553,7 @@ nsWaveStateMachine::Run()
} else {
ChangeState(mNextState);
nsCOMPtr<nsIRunnable> event =
NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, BufferingStopped);
NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, UpdateReadyStateForData);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
@ -568,6 +572,11 @@ nsWaveStateMachine::Run()
// Buffer until mBufferingWait milliseconds of data is available.
mBufferingBytes = TimeToBytes(float(mBufferingWait) / 1000.0);
mBufferingStart = PR_IntervalNow();
nsCOMPtr<nsIRunnable> event =
NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, UpdateReadyStateForData);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
ChangeState(STATE_BUFFERING);
} else {
// Media stream has ended and there is less data available than a
@ -1489,15 +1498,13 @@ nsWaveDecoder::UpdateReadyStateForData()
if (!mElement || mShuttingDown || !mPlaybackStateMachine)
return;
PRBool haveDataToPlay =
mPlaybackStateMachine->HasPendingData() && mMetadataLoadedReported;
mElement->UpdateReadyStateForData(haveDataToPlay);
}
void
nsWaveDecoder::BufferingStopped()
{
UpdateReadyStateForData();
nsHTMLMediaElement::NextFrameStatus frameStatus =
mPlaybackStateMachine->GetNextFrameStatus();
if (frameStatus == nsHTMLMediaElement::NEXT_FRAME_AVAILABLE &&
!mMetadataLoadedReported) {
frameStatus = nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
}
mElement->UpdateReadyStateForData(frameStatus);
}
void