bug 464376 - Fire media progress events as needed - r+sr=roc

This commit is contained in:
Chris Double 2008-12-06 18:25:16 +13:00
Родитель 59c0d7cfcb
Коммит c53f161056
9 изменённых файлов: 130 добавлений и 22 удалений

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

@ -821,8 +821,10 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE, PR_FALSE, mDecoder->GetBytesLoaded(), mDecoder->GetTotalBytes());
PRInt64 length = mDecoder->GetTotalBytes();
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
length >= 0, mDecoder->GetBytesLoaded(), length);
NS_ENSURE_SUCCESS(rv, rv);
PRBool dummy;

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

@ -68,8 +68,11 @@ class nsChannelToPipeListener : public nsIStreamListener
public:
// If aSeeking is PR_TRUE then this listener was created as part of a
// seek request and is expecting a byte range partial result.
nsChannelToPipeListener(nsMediaDecoder* aDecoder, PRBool aSeeking = PR_FALSE);
// seek request and is expecting a byte range partial result. aOffset
// is the offset in bytes that this listener started reading from.
nsChannelToPipeListener(nsMediaDecoder* aDecoder,
PRBool aSeeking = PR_FALSE,
PRInt64 aOffset = 0);
nsresult Init();
nsresult GetInputStream(nsIInputStream** aStream);
void Stop();
@ -95,7 +98,12 @@ private:
// bytes per second download rate.
PRIntervalTime mIntervalEnd;
// Total bytes transferred so far
// Offset from the beginning of the resource where the listener
// started reading. This is used for computing the current file
// position for progress events.
PRInt64 mOffset;
// Total bytes transferred so far. Used for computing download rates.
PRInt64 mTotalBytes;
// PR_TRUE if this listener is expecting a byte range request result

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

@ -160,8 +160,11 @@ class nsMediaDecoder : public nsIObserver
// Invalidate the frame.
virtual void Invalidate();
// Update progress information.
virtual void Progress();
// Fire progress events if needed according to the time and byte
// constraints outlined in the specification. aTimer is PR_TRUE
// if the method is called as a result of the progress timer rather
// than the result of downloaded data.
virtual void Progress(PRBool aTimer);
// Keep track of the number of bytes downloaded
virtual void UpdateBytesDownloaded(PRUint64 aBytes) = 0;
@ -206,6 +209,16 @@ protected:
PRInt32 mRGBWidth;
PRInt32 mRGBHeight;
// Time that the last progress event was fired. Read/Write from the
// main thread only.
PRIntervalTime mProgressTime;
// Time that data was last read from the media resource. Used for
// computing if the download has stalled. A value of 0 indicates that
// a stall event has already fired and not to fire another one until
// more data is received. Read/Write from the main thread only.
PRIntervalTime mDataTime;
// Has our size changed since the last repaint?
PRPackedBool mSizeChanged;

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

@ -47,10 +47,12 @@
nsChannelToPipeListener::nsChannelToPipeListener(
nsMediaDecoder* aDecoder,
PRBool aSeeking) :
PRBool aSeeking,
PRInt64 aOffset) :
mDecoder(aDecoder),
mIntervalStart(0),
mIntervalEnd(0),
mOffset(aOffset),
mTotalBytes(0),
mSeeking(aSeeking)
{
@ -99,7 +101,7 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
mIntervalStart = PR_IntervalNow();
mIntervalEnd = mIntervalStart;
mTotalBytes = 0;
mDecoder->UpdateBytesDownloaded(mTotalBytes);
mDecoder->UpdateBytesDownloaded(mOffset);
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
if (hc) {
PRUint32 responseStatus = 0;
@ -150,6 +152,10 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
}
}
// Fires an initial progress event and sets up the stall counter so stall events
// fire if no download occurs within the required time frame.
mDecoder->Progress(PR_FALSE);
return NS_OK;
}
@ -183,11 +189,16 @@ nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest,
aCount -= bytes;
mTotalBytes += bytes;
mDecoder->UpdateBytesDownloaded(mTotalBytes);
mDecoder->UpdateBytesDownloaded(mOffset + aOffset + bytes);
} while (aCount) ;
nsresult rv = mOutput->Flush();
NS_ENSURE_SUCCESS(rv, rv);
// Fire a progress events according to the time and byte constraints outlined
// in the spec.
mDecoder->Progress(PR_FALSE);
mIntervalEnd = PR_IntervalNow();
return NS_OK;
}

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

@ -52,6 +52,12 @@
#include "nsPresContext.h"
#include "nsMediaDecoder.h"
// Number of milliseconds between progress events as defined by spec
#define PROGRESS_MS 350
// Number of milliseconds of no data before a stall event is fired as defined by spec
#define STALL_MS 3000
#ifdef PR_LOGGING
// Logging object for decoder
PRLogModuleInfo* gVideoDecoderLog = nsnull;
@ -61,6 +67,8 @@ nsMediaDecoder::nsMediaDecoder() :
mElement(0),
mRGBWidth(-1),
mRGBHeight(-1),
mProgressTime(0),
mDataTime(0),
mSizeChanged(PR_FALSE),
mVideoUpdateLock(nsnull),
mFramerate(0.0)
@ -127,26 +135,46 @@ void nsMediaDecoder::Invalidate()
static void ProgressCallback(nsITimer* aTimer, void* aClosure)
{
nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
decoder->Progress();
decoder->Progress(PR_TRUE);
}
void nsMediaDecoder::Progress()
void nsMediaDecoder::Progress(PRBool aTimer)
{
if (!mElement)
// The check for mProgressTimer is to ensure that progress events
// are only sent when we've asked for them to start via StartProgress.
if (!mElement || !mProgressTimer)
return;
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
PRIntervalTime now = PR_IntervalNow();
if (mProgressTime == 0 ||
PR_IntervalToMilliseconds(PR_IntervalNow() - mProgressTime) >= PROGRESS_MS) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
mProgressTime = now;
}
// The test for aTimer is to ensure that we dispatch 'stalled'
// only when we are not receiving data.
if (aTimer &&
mDataTime != 0 &&
PR_IntervalToMilliseconds(now - mDataTime) >= STALL_MS) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("stalled"));
mDataTime = 0;
}
if (!aTimer) {
mDataTime = now;
}
}
nsresult nsMediaDecoder::StartProgress()
{
nsresult rv = NS_OK;
if (!mProgressTimer) {
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
rv = mProgressTimer->InitWithFuncCallback(ProgressCallback,
this,
350, // Number of milliseconds defined in spec
PROGRESS_MS,
nsITimer::TYPE_REPEATING_PRECISE);
}
return rv;
@ -159,6 +187,7 @@ nsresult nsMediaDecoder::StopProgress()
rv = mProgressTimer->Cancel();
mProgressTimer = nsnull;
}
return rv;
}

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

@ -270,6 +270,7 @@ nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
rv = mInput->Available(&size);
if (NS_SUCCEEDED(rv)) {
mDecoder->SetTotalBytes(size);
mDecoder->UpdateBytesDownloaded(size);
}
/* Get our principal */
@ -546,7 +547,7 @@ public:
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
}
mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE);
mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE, mOffset);
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
mResult = mListener->Init();

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

@ -1254,8 +1254,6 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
StartProgress();
RegisterShutdownObserver();
mReader = new nsChannelReader();
@ -1389,6 +1387,13 @@ void nsOggDecoder::MetadataLoaded()
if (mElement) {
mElement->MetadataLoaded();
}
// Start progress events if we have bytes left to download.
// Need to check this as it's possible the entire file has been
// downloaded before the metadata is decoded.
if (mBytesDownloaded != mContentLength) {
StartProgress();
}
}
void nsOggDecoder::FirstFrameLoaded()
@ -1414,10 +1419,25 @@ void nsOggDecoder::FirstFrameLoaded()
void nsOggDecoder::ResourceLoaded()
{
{
// 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.
nsAutoMonitor mon(mMonitor);
if (mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_LOADING)
return;
}
mBytesDownloaded = mContentLength;
StopProgress();
// Ensure the final progress event gets fired
if (mElement) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
if (mElement) {
mElement->ResourceLoaded();
}
StopProgress();
}
void nsOggDecoder::NetworkError()
@ -1472,7 +1492,16 @@ void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
{
mBytesDownloaded = aBytes;
nsAutoMonitor mon(mMonitor);
// Don't update bytes downloaded if we are in the LOADING
// play state. The value of aBytes is unreliable at this
// stage due to seeking to get the duration done during
// metadata loading. This prevents the progress bar from
// jumping around from 0 to 100% back to 0.
if (mPlayState != PLAY_STATE_LOADING) {
mBytesDownloaded = aBytes;
}
}
void nsOggDecoder::BufferingStopped()
@ -1507,6 +1536,8 @@ void nsOggDecoder::SeekingStopped()
if (mElement) {
mElement->SeekCompleted();
}
StartProgress();
}
void nsOggDecoder::SeekingStarted()
@ -1517,6 +1548,11 @@ void nsOggDecoder::SeekingStarted()
return;
}
// Don't do progress events while seeking
// due to the rapid change in loaded position
// that occurs.
StopProgress();
if (mElement) {
mElement->SeekStarted();
}

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

@ -1111,6 +1111,11 @@ nsWaveDecoder::ResourceLoaded()
mPlaybackStateMachine->StreamEnded();
}
StopProgress();
// Ensure the final progress event gets fired
if (mElement) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
}
void

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

@ -53,6 +53,8 @@ _TEST_FILES = test_autoplay.html \
test_ended2.html \
test_networkState.html \
test_paused.html \
test_progress1.html \
test_progress2.html \
test_readyState.html \
test_seek1.html \
test_seek2.html \
@ -74,7 +76,8 @@ _TEST_FILES = test_autoplay.html \
320x240.ogg \
bug461281.ogg \
seek.ogg \
r11025_s16_c1.wav \
big.wav \
r11025_s16_c1.wav \
r11025_u8_c1.wav \
# test_bug448534.html \
$(NULL)