зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset ba595db2b681
This commit is contained in:
Родитель
546a3091e1
Коммит
b4f66d939a
|
@ -111,6 +111,11 @@ public:
|
|||
// when the video playback has ended.
|
||||
void PlaybackEnded();
|
||||
|
||||
// Called by the decoder object, on the main thread, when
|
||||
// approximately enough of the resource has been loaded to play
|
||||
// through without pausing for buffering.
|
||||
void CanPlayThrough();
|
||||
|
||||
// Called by the video decoder object, on the main thread,
|
||||
// when the resource has started seeking.
|
||||
void SeekStarted();
|
||||
|
@ -129,13 +134,6 @@ public:
|
|||
nsresult DispatchAsyncSimpleEvent(const nsAString& aName);
|
||||
nsresult DispatchAsyncProgressEvent(const nsAString& aName);
|
||||
|
||||
// Called by the decoder when some data has been downloaded or
|
||||
// buffering/seeking has ended. aNextFrameAvailable is true when
|
||||
// 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);
|
||||
|
||||
// Use this method to change the mReadyState member, so required
|
||||
// events can be fired.
|
||||
void ChangeReadyState(nsMediaReadyState aState);
|
||||
|
|
|
@ -966,6 +966,8 @@ void nsHTMLMediaElement::MetadataLoaded()
|
|||
void nsHTMLMediaElement::FirstFrameLoaded()
|
||||
{
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
mLoadedFirstFrame = PR_TRUE;
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadeddata"));
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::ResourceLoaded()
|
||||
|
@ -993,6 +995,11 @@ void nsHTMLMediaElement::PlaybackEnded()
|
|||
DispatchSimpleEvent(NS_LITERAL_STRING("ended"));
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::CanPlayThrough()
|
||||
{
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::SeekStarted()
|
||||
{
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeking"));
|
||||
|
@ -1010,109 +1017,30 @@ PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
|
|||
PR_TRUE);
|
||||
}
|
||||
|
||||
// Number of bytes to add to the download size when we're computing
|
||||
// when the download will finish --- a safety margin in case bandwidth
|
||||
// or other conditions are worse than expected
|
||||
static const PRInt32 gDownloadSizeSafetyMargin = 1000000;
|
||||
|
||||
void nsHTMLMediaElement::UpdateReadyStateForData(PRBool aNextFrameAvailable)
|
||||
{
|
||||
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||
NS_ASSERTION(!aNextFrameAvailable, "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()) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now see if we should set HAVE_ENOUGH_DATA
|
||||
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
if (stats.mTotalBytes < 0 || stats.mTotalBytes == stats.mDownloadPosition) {
|
||||
// If it's something we don't know the size of, then we can't
|
||||
// make an estimate, so let's just go straight to HAVE_ENOUGH_DATA,
|
||||
// since otherwise autoplay elements will never play.
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.mDownloadRateReliable && stats.mPlaybackRateReliable) {
|
||||
PRInt64 bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
|
||||
PRInt64 bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
|
||||
double timeToDownload =
|
||||
(bytesToDownload + gDownloadSizeSafetyMargin)/stats.mDownloadRate;
|
||||
double timeToPlay = bytesToPlayback/stats.mPlaybackRate;
|
||||
LOG(PR_LOG_DEBUG, ("Download rate=%f, playback rate=%f, timeToDownload=%f, timeToPlay=%f",
|
||||
stats.mDownloadRate, stats.mPlaybackRate, timeToDownload, timeToPlay));
|
||||
if (timeToDownload <= timeToPlay) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
||||
}
|
||||
|
||||
void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
||||
{
|
||||
nsMediaReadyState oldState = mReadyState;
|
||||
|
||||
// Handle raising of "waiting" event during seek (see 4.8.10.9)
|
||||
if (mPlayingBeforeSeek && oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
||||
if (mPlayingBeforeSeek && aState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA)
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
|
||||
}
|
||||
|
||||
|
||||
mReadyState = aState;
|
||||
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
||||
switch (mReadyState) {
|
||||
switch(mReadyState) {
|
||||
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
|
||||
if (oldState != mReadyState) {
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_NOTHING"));
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMHTMLMediaElement::HAVE_METADATA:
|
||||
if (oldState != mReadyState) {
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_METADATA"));
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_NOTHING"));
|
||||
break;
|
||||
|
||||
case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
|
||||
if (oldState != mReadyState) {
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_CURRENT_DATA"));
|
||||
}
|
||||
if (oldState <= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
||||
!mLoadedFirstFrame) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadeddata"));
|
||||
mLoadedFirstFrame = PR_TRUE;
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_CURRENT_DATA"));
|
||||
break;
|
||||
|
||||
|
||||
case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
|
||||
if (oldState != mReadyState) {
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_FUTURE_DATA"));
|
||||
}
|
||||
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
|
||||
if (IsPotentiallyPlaying()) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
|
||||
}
|
||||
}
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_FUTURE_DATA"));
|
||||
break;
|
||||
|
||||
|
||||
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplaythrough"));
|
||||
if (oldState != mReadyState) {
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_ENOUGH_DATA"));
|
||||
}
|
||||
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
|
||||
}
|
||||
if (oldState <= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplaythrough"));
|
||||
}
|
||||
if (mAutoplaying &&
|
||||
mPaused &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) {
|
||||
|
@ -1123,10 +1051,6 @@ void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_ENOUGH_DATA"));
|
||||
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
||||
IsPotentiallyPlaying()) {
|
||||
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1177,9 +1101,9 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
|
|||
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
|
||||
NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
|
||||
|
||||
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
PRInt64 length = mDecoder->GetTotalBytes();
|
||||
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
|
||||
stats.mTotalBytes >= 0, stats.mDownloadPosition, stats.mTotalBytes);
|
||||
length >= 0, mDecoder->GetBytesLoaded(), length);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool dummy;
|
||||
|
|
|
@ -71,6 +71,14 @@ public:
|
|||
// be read without blocking.
|
||||
PRUint32 Available();
|
||||
|
||||
// Return average number of bytes per second that the
|
||||
// download of the media resource is achieving.
|
||||
float DownloadRate();
|
||||
|
||||
// Return average number of bytes per second that the
|
||||
// playback of the media resource is achieving.
|
||||
float PlaybackRate();
|
||||
|
||||
// Suspend any downloads that are in progress.
|
||||
void Suspend();
|
||||
|
||||
|
@ -88,6 +96,7 @@ public:
|
|||
|
||||
public:
|
||||
nsMediaStream mStream;
|
||||
unsigned long mCurrentPosition;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
#include "nsIStreamListener.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
||||
// Constant for download and playback rates that are unknown, or otherwise
|
||||
// unable to be computed.
|
||||
#define NS_MEDIA_UNKNOWN_RATE -1.0
|
||||
|
||||
class nsMediaDecoder;
|
||||
|
||||
/*
|
||||
|
@ -67,12 +71,17 @@ class nsChannelToPipeListener : public nsIStreamListener
|
|||
// 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);
|
||||
PRBool aSeeking = PR_FALSE,
|
||||
PRInt64 aOffset = 0);
|
||||
nsresult Init();
|
||||
nsresult GetInputStream(nsIInputStream** aStream);
|
||||
void Stop();
|
||||
void Cancel();
|
||||
|
||||
// Return the download rate in bytes per second. Returns
|
||||
// less than zero if the download has complated.
|
||||
double BytesPerSecond() const;
|
||||
|
||||
nsIPrincipal* GetCurrentPrincipal();
|
||||
|
||||
private:
|
||||
|
@ -81,6 +90,22 @@ private:
|
|||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsRefPtr<nsMediaDecoder> mDecoder;
|
||||
|
||||
// Interval when download started. Used in
|
||||
// computing bytes per second download rate.
|
||||
PRIntervalTime mIntervalStart;
|
||||
|
||||
// Interval when last downloaded bytes occurred. Used in computer
|
||||
// bytes per second download rate.
|
||||
PRIntervalTime mIntervalEnd;
|
||||
|
||||
// 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
|
||||
PRPackedBool mSeeking;
|
||||
};
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include "gfxContext.h"
|
||||
#include "gfxRect.h"
|
||||
#include "nsITimer.h"
|
||||
#include "prinrval.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gVideoDecoderLog;
|
||||
|
@ -57,8 +56,7 @@ extern PRLogModuleInfo* gVideoDecoderLog;
|
|||
class nsHTMLMediaElement;
|
||||
|
||||
// All methods of nsMediaDecoder must be called from the main thread only
|
||||
// with the exception of SetRGBData and GetStatistics, which can be
|
||||
// called from any thread.
|
||||
// with the exception of SetRGBData. The latter can be called from any thread.
|
||||
class nsMediaDecoder : public nsIObserver
|
||||
{
|
||||
public:
|
||||
|
@ -140,39 +138,13 @@ class nsMediaDecoder : public nsIObserver
|
|||
// Call in the main thread only.
|
||||
virtual PRBool IsEnded() const = 0;
|
||||
|
||||
struct Statistics {
|
||||
// Estimate of the current playback rate (bytes/second).
|
||||
double mPlaybackRate;
|
||||
// Estimate of the current download rate (bytes/second)
|
||||
double mDownloadRate;
|
||||
// Total length of media stream in bytes; -1 if not known
|
||||
PRInt64 mTotalBytes;
|
||||
// Current position of the download, in bytes. This position (and
|
||||
// the other positions) should only increase unless the current
|
||||
// playback position is explicitly changed. This may require
|
||||
// some fudging by the decoder if operations like seeking or finding the
|
||||
// duration require seeks in the underlying stream.
|
||||
PRInt64 mDownloadPosition;
|
||||
// Current position of decoding, in bytes (how much of the stream
|
||||
// has been consumed)
|
||||
PRInt64 mDecoderPosition;
|
||||
// Current position of playback, in bytes
|
||||
PRInt64 mPlaybackPosition;
|
||||
// If false, then mDownloadRate cannot be considered a reliable
|
||||
// estimate (probably because the download has only been running
|
||||
// a short time).
|
||||
PRPackedBool mDownloadRateReliable;
|
||||
// If false, then mPlaybackRate cannot be considered a reliable
|
||||
// estimate (probably because playback has only been running
|
||||
// a short time).
|
||||
PRPackedBool mPlaybackRateReliable;
|
||||
};
|
||||
// Return the current number of bytes loaded from the video file.
|
||||
// This is used for progress events.
|
||||
virtual PRUint64 GetBytesLoaded() = 0;
|
||||
|
||||
// Return statistics. This is used for progress events and other things.
|
||||
// This can be called from any thread. It's only a snapshot of the
|
||||
// current state, since other threads might be changing the state
|
||||
// at any time.
|
||||
virtual Statistics GetStatistics() = 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;
|
||||
|
||||
// Set the size of the video file in bytes.
|
||||
virtual void SetTotalBytes(PRInt64 aBytes) = 0;
|
||||
|
@ -192,32 +164,8 @@ class nsMediaDecoder : public nsIObserver
|
|||
// than the result of downloaded data.
|
||||
virtual void Progress(PRBool aTimer);
|
||||
|
||||
// Called by nsMediaStream when a seek operation happens (could be
|
||||
// called either before or after the seek completes). Called on the main
|
||||
// thread. This may be called as a result of the stream opening (the
|
||||
// offset should be zero in that case).
|
||||
// Reads from streams after a seek MUST NOT complete before
|
||||
// NotifyDownloadSeeked has been delivered. (We assume the reads
|
||||
// and the seeks happen on the same calling thread.)
|
||||
virtual void NotifyDownloadSeeked(PRInt64 aOffsetBytes) = 0;
|
||||
|
||||
// Called by nsChannelToPipeListener or nsMediaStream when data has
|
||||
// been received.
|
||||
// Call on the main thread only. aBytes of data have just been received.
|
||||
// Reads from streams MUST NOT complete before the NotifyBytesDownloaded
|
||||
// for those bytes has been delivered. (We assume reads and seeks
|
||||
// happen on the same calling thread.)
|
||||
virtual void NotifyBytesDownloaded(PRInt64 aBytes) = 0;
|
||||
|
||||
// Called by nsChannelToPipeListener or nsMediaStream when the
|
||||
// download has ended. Called on the main thread only. aStatus is
|
||||
// the result from OnStopRequest.
|
||||
virtual void NotifyDownloadEnded(nsresult aStatus) = 0;
|
||||
|
||||
// Called by nsMediaStream when data has been read from the stream
|
||||
// for playback.
|
||||
// Call on any thread. aBytes of data have just been consumed.
|
||||
virtual void NotifyBytesConsumed(PRInt64 aBytes) = 0;
|
||||
// Keep track of the number of bytes downloaded
|
||||
virtual void UpdateBytesDownloaded(PRUint64 aBytes) = 0;
|
||||
|
||||
// Cleanup internal data structures. Must be called on the main
|
||||
// thread by the owning object before that object disposes of this object.
|
||||
|
@ -256,70 +204,6 @@ protected:
|
|||
float aFramerate,
|
||||
unsigned char* aRGBBuffer);
|
||||
|
||||
/**
|
||||
* This class is useful for estimating rates of data passing through
|
||||
* some channel. The idea is that activity on the channel "starts"
|
||||
* and "stops" over time. At certain times data passes through the
|
||||
* channel (usually while the channel is active; data passing through
|
||||
* an inactive channel is ignored). The GetRate() function computes
|
||||
* an estimate of the "current rate" of the channel, which is some
|
||||
* kind of average of the data passing through over the time the
|
||||
* channel is active.
|
||||
*
|
||||
* Timestamps and time durations are measured in PRIntervalTimes, but
|
||||
* all methods take "now" as a parameter so the user of this class can
|
||||
* define what the timeline means.
|
||||
*/
|
||||
class ChannelStatistics {
|
||||
public:
|
||||
ChannelStatistics() { Reset(); }
|
||||
void Reset() {
|
||||
mLastStartTime = mAccumulatedTime = 0;
|
||||
mAccumulatedBytes = 0;
|
||||
mIsStarted = PR_FALSE;
|
||||
}
|
||||
void Start(PRIntervalTime aNow) {
|
||||
if (mIsStarted)
|
||||
return;
|
||||
mLastStartTime = aNow;
|
||||
mIsStarted = PR_TRUE;
|
||||
}
|
||||
void Stop(PRIntervalTime aNow) {
|
||||
if (!mIsStarted)
|
||||
return;
|
||||
mAccumulatedTime += aNow - mLastStartTime;
|
||||
mIsStarted = PR_FALSE;
|
||||
}
|
||||
void AddBytes(PRInt64 aBytes) {
|
||||
if (!mIsStarted) {
|
||||
// ignore this data, it may be related to seeking or some other
|
||||
// operation we don't care about
|
||||
return;
|
||||
}
|
||||
mAccumulatedBytes += aBytes;
|
||||
}
|
||||
double GetRateAtLastStop(PRPackedBool* aReliable) {
|
||||
*aReliable = mAccumulatedTime >= PR_TicksPerSecond();
|
||||
return double(mAccumulatedBytes)*PR_TicksPerSecond()/mAccumulatedTime;
|
||||
}
|
||||
double GetRate(PRIntervalTime aNow, PRPackedBool* aReliable) {
|
||||
PRIntervalTime time = mAccumulatedTime;
|
||||
if (mIsStarted) {
|
||||
time += aNow - mLastStartTime;
|
||||
}
|
||||
*aReliable = time >= PR_TicksPerSecond();
|
||||
NS_ASSERTION(time >= 0, "Time wraparound?");
|
||||
if (time <= 0)
|
||||
return 0.0;
|
||||
return double(mAccumulatedBytes)*PR_TicksPerSecond()/time;
|
||||
}
|
||||
private:
|
||||
PRInt64 mAccumulatedBytes;
|
||||
PRIntervalTime mAccumulatedTime;
|
||||
PRIntervalTime mLastStartTime;
|
||||
PRPackedBool mIsStarted;
|
||||
};
|
||||
|
||||
protected:
|
||||
// Timer used for updating progress events
|
||||
nsCOMPtr<nsITimer> mProgressTimer;
|
||||
|
|
|
@ -89,13 +89,12 @@ public:
|
|||
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset) = 0;
|
||||
virtual PRInt64 Tell() = 0;
|
||||
virtual PRUint32 Available() = 0;
|
||||
virtual float DownloadRate() = 0;
|
||||
virtual void Cancel() { }
|
||||
virtual nsIPrincipal* GetCurrentPrincipal() = 0;
|
||||
virtual void Suspend() = 0;
|
||||
virtual void Resume() = 0;
|
||||
|
||||
nsMediaDecoder* Decoder() { return mDecoder; }
|
||||
|
||||
protected:
|
||||
// This is not an nsCOMPointer to prevent a circular reference
|
||||
// between the decoder to the media stream object. The stream never
|
||||
|
@ -176,6 +175,15 @@ class nsMediaStream
|
|||
// read without blocking. Can be called from any thread.
|
||||
PRUint32 Available();
|
||||
|
||||
// Return the current download rate in bytes per second. Returns less than
|
||||
// zero if the download has completed. Can be called from any
|
||||
// thread.
|
||||
float DownloadRate();
|
||||
|
||||
// Return the current playback rate in bytes per second. Can be
|
||||
// called from any thread.
|
||||
float PlaybackRate();
|
||||
|
||||
// Cancels any currently blocking request and forces that request to
|
||||
// return an error. Call on main thread only.
|
||||
void Cancel();
|
||||
|
@ -197,6 +205,17 @@ class nsMediaStream
|
|||
// only. Open is always called first on the main thread before any
|
||||
// other calls from other threads.
|
||||
nsAutoPtr<nsStreamStrategy> mStreamStrategy;
|
||||
|
||||
// Time used for computing average playback rate. Written on the
|
||||
// main thread only during the Open call. Read from any thread during
|
||||
// calls to PlaybackRate() - which can only ever happen after Open.
|
||||
PRIntervalTime mPlaybackRateStart;
|
||||
|
||||
// Bytes downloaded for average playback rate computation. Initialized
|
||||
// on the main thread during Open(). After that it is read and written
|
||||
// possibly on a different thread, but exclusively from that
|
||||
// thread. In the case of the Ogg Decoder, it is the Decoder thread.
|
||||
PRUint32 mPlaybackRateCount;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -325,10 +325,7 @@ class nsOggDecoder : public nsMediaDecoder
|
|||
virtual void GetCurrentURI(nsIURI** aURI);
|
||||
virtual nsIPrincipal* GetCurrentPrincipal();
|
||||
|
||||
virtual void NotifyBytesDownloaded(PRInt64 aBytes);
|
||||
virtual void NotifyDownloadSeeked(PRInt64 aOffsetBytes);
|
||||
virtual void NotifyDownloadEnded(nsresult aStatus);
|
||||
virtual void NotifyBytesConsumed(PRInt64 aBytes);
|
||||
virtual void UpdateBytesDownloaded(PRUint64 aBytes);
|
||||
|
||||
// Called when the video file has completed downloading.
|
||||
// Call on the main thread only.
|
||||
|
@ -358,8 +355,6 @@ class nsOggDecoder : public nsMediaDecoder
|
|||
// Returns the channel reader.
|
||||
nsChannelReader* GetReader() { return mReader; }
|
||||
|
||||
virtual Statistics GetStatistics();
|
||||
|
||||
// Suspend any media downloads that are in progress. Called by the
|
||||
// media element when it is sent to the bfcache. Call on the main
|
||||
// thread only.
|
||||
|
@ -417,10 +412,22 @@ protected:
|
|||
// Call on the main thread only.
|
||||
void PlaybackEnded();
|
||||
|
||||
// Return the current number of bytes loaded from the video file.
|
||||
// This is used for progress events.
|
||||
virtual PRUint64 GetBytesLoaded();
|
||||
|
||||
// Return the size of the video file in bytes.
|
||||
// This is used for progress events.
|
||||
virtual PRInt64 GetTotalBytes();
|
||||
|
||||
// Buffering of data has stopped. Inform the element on the main
|
||||
// thread.
|
||||
void BufferingStopped();
|
||||
|
||||
// Buffering of data has started. Inform the element on the main
|
||||
// thread.
|
||||
void BufferingStarted();
|
||||
|
||||
// Seeking has stopped. Inform the element on the main
|
||||
// thread.
|
||||
void SeekingStopped();
|
||||
|
@ -440,37 +447,11 @@ private:
|
|||
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.
|
||||
* The following members should be accessed on the main thread only
|
||||
******/
|
||||
|
||||
// Size of the media file in bytes. Set on the first
|
||||
// HTTP request from nsChannelToPipe Listener. -1 if not known.
|
||||
PRInt64 mTotalBytes;
|
||||
// Current download position in the stream.
|
||||
PRInt64 mDownloadPosition;
|
||||
// Download position to report if asked. This is the same as
|
||||
// mDownloadPosition normally, but we don't update it while ignoring
|
||||
// progress. This lets us avoid reporting progress changes due to reads
|
||||
// that are only servicing our seek operations.
|
||||
PRInt64 mProgressPosition;
|
||||
// Current decoding position in the stream. This is where the decoder
|
||||
// is up to consuming the stream.
|
||||
PRInt64 mDecoderPosition;
|
||||
// Current playback position in the stream. This is (approximately)
|
||||
// where we're up to playing back the stream.
|
||||
PRInt64 mPlaybackPosition;
|
||||
// Data needed to estimate download data rate. The timeline used for
|
||||
// this estimate is wall-clock time.
|
||||
ChannelStatistics mDownloadStatistics;
|
||||
// Data needed to estimate playback data rate. The timeline used for
|
||||
// this estimate is "decode time" (where the "current time" is the
|
||||
// time of the last decoded video frame).
|
||||
ChannelStatistics mPlaybackStatistics;
|
||||
// Total number of bytes downloaded so far.
|
||||
PRUint64 mBytesDownloaded;
|
||||
|
||||
// The URI of the current resource
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
|
@ -497,6 +478,11 @@ private:
|
|||
// started this is reset to negative.
|
||||
float mRequestedSeekTime;
|
||||
|
||||
// Size of the media file in bytes. Set on the first non-byte range
|
||||
// HTTP request from nsChannelToPipe Listener. Accessed on the
|
||||
// main thread only.
|
||||
PRInt64 mContentLength;
|
||||
|
||||
// Duration of the media resource. Set to -1 if unknown.
|
||||
// Set when the Ogg metadata is loaded. Accessed on the main thread
|
||||
// only.
|
||||
|
@ -546,14 +532,13 @@ private:
|
|||
// Any change to the state must call NotifyAll on the monitor.
|
||||
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.
|
||||
// True when the media resource has completely loaded. Accessed on
|
||||
// the main thread only.
|
||||
PRPackedBool mResourceLoaded;
|
||||
|
||||
// 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
|
||||
// before a seek or during loading of metadata to prevent the progress indicator
|
||||
// from jumping around. Read/Write from any thread. Must have decode monitor
|
||||
// locked before accessing.
|
||||
PRPackedBool mIgnoreProgressData;
|
||||
|
|
|
@ -191,19 +191,18 @@ class nsWaveDecoder : public nsMediaDecoder
|
|||
// Element is notifying us that the requested playback rate has changed.
|
||||
virtual nsresult PlaybackRateChanged();
|
||||
|
||||
virtual void NotifyBytesDownloaded(PRInt64 aBytes);
|
||||
virtual void NotifyDownloadSeeked(PRInt64 aOffset);
|
||||
virtual void NotifyDownloadEnded(nsresult aStatus);
|
||||
virtual void NotifyBytesConsumed(PRInt64 aBytes);
|
||||
|
||||
virtual Statistics GetStatistics();
|
||||
|
||||
// Getter/setter for mContentLength.
|
||||
virtual PRInt64 GetTotalBytes();
|
||||
virtual void SetTotalBytes(PRInt64 aBytes);
|
||||
|
||||
// Getter/setter for mSeekable.
|
||||
virtual void SetSeekable(PRBool aSeekable);
|
||||
virtual PRBool GetSeekable();
|
||||
|
||||
// Getter/setter for mBytesDownloaded.
|
||||
virtual PRUint64 GetBytesLoaded();
|
||||
virtual void UpdateBytesDownloaded(PRUint64 aBytes);
|
||||
|
||||
// Must be called by the owning object before disposing the decoder.
|
||||
virtual void Shutdown();
|
||||
|
||||
|
@ -218,8 +217,8 @@ class nsWaveDecoder : public nsMediaDecoder
|
|||
virtual void Resume();
|
||||
|
||||
private:
|
||||
// Change the element's ready state as necessary
|
||||
void UpdateReadyStateForData();
|
||||
// Notifies the nsHTMLMediaElement that buffering has started.
|
||||
void BufferingStarted();
|
||||
|
||||
// Notifies the element that buffering has stopped.
|
||||
void BufferingStopped();
|
||||
|
@ -243,6 +242,12 @@ private:
|
|||
void RegisterShutdownObserver();
|
||||
void UnregisterShutdownObserver();
|
||||
|
||||
// Length of the current resource, or -1 if not available.
|
||||
PRInt64 mContentLength;
|
||||
|
||||
// Total bytes downloaded by mStream so far.
|
||||
PRUint64 mBytesDownloaded;
|
||||
|
||||
// Volume that the audio backend will be initialized with.
|
||||
float mInitialVolume;
|
||||
|
||||
|
@ -283,12 +288,6 @@ private:
|
|||
// True when the media resource has completely loaded. Accessed on
|
||||
// the main thread only.
|
||||
PRPackedBool mResourceLoaded;
|
||||
|
||||
// True if MetadataLoaded has been reported to the element.
|
||||
PRPackedBool mMetadataLoadedReported;
|
||||
|
||||
// True if ResourceLoaded has been reported to the element.
|
||||
PRPackedBool mResourceLoadedReported;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -53,6 +53,16 @@ PRUint32 nsChannelReader::Available()
|
|||
return mStream.Available();
|
||||
}
|
||||
|
||||
float nsChannelReader::DownloadRate()
|
||||
{
|
||||
return mStream.DownloadRate();
|
||||
}
|
||||
|
||||
float nsChannelReader::PlaybackRate()
|
||||
{
|
||||
return mStream.PlaybackRate();
|
||||
}
|
||||
|
||||
OggPlayErrorCode nsChannelReader::initialise(int aBlock)
|
||||
{
|
||||
return E_OGGPLAY_OK;
|
||||
|
@ -81,6 +91,7 @@ size_t nsChannelReader::io_read(char* aBuffer, size_t aCount)
|
|||
if (!NS_SUCCEEDED(rv)) {
|
||||
return static_cast<size_t>(OGGZ_ERR_SYSTEM);
|
||||
}
|
||||
mCurrentPosition += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
@ -136,6 +147,7 @@ nsresult nsChannelReader::Init(nsMediaDecoder* aDecoder, nsIURI* aURI,
|
|||
nsIChannel* aChannel,
|
||||
nsIStreamListener** aStreamListener)
|
||||
{
|
||||
mCurrentPosition = 0;
|
||||
return mStream.Open(aDecoder, aURI, aChannel, aStreamListener);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,13 @@
|
|||
|
||||
nsChannelToPipeListener::nsChannelToPipeListener(
|
||||
nsMediaDecoder* aDecoder,
|
||||
PRBool aSeeking) :
|
||||
PRBool aSeeking,
|
||||
PRInt64 aOffset) :
|
||||
mDecoder(aDecoder),
|
||||
mIntervalStart(0),
|
||||
mIntervalEnd(0),
|
||||
mOffset(aOffset),
|
||||
mTotalBytes(0),
|
||||
mSeeking(aSeeking)
|
||||
{
|
||||
}
|
||||
|
@ -82,6 +87,11 @@ void nsChannelToPipeListener::Cancel()
|
|||
mInput->Close();
|
||||
}
|
||||
|
||||
double nsChannelToPipeListener::BytesPerSecond() const
|
||||
{
|
||||
return mOutput ? mTotalBytes / ((PR_IntervalToMilliseconds(mIntervalEnd-mIntervalStart)) / 1000.0) : NS_MEDIA_UNKNOWN_RATE;
|
||||
}
|
||||
|
||||
nsresult nsChannelToPipeListener::GetInputStream(nsIInputStream** aStream)
|
||||
{
|
||||
NS_IF_ADDREF(*aStream = mInput);
|
||||
|
@ -103,11 +113,15 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
|
|||
}
|
||||
}
|
||||
|
||||
mIntervalStart = PR_IntervalNow();
|
||||
mIntervalEnd = mIntervalStart;
|
||||
mTotalBytes = 0;
|
||||
mDecoder->UpdateBytesDownloaded(mOffset);
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
|
||||
if (hc) {
|
||||
nsCAutoString ranges;
|
||||
hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
|
||||
ranges);
|
||||
nsresult rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
|
||||
ranges);
|
||||
PRBool acceptsRanges = ranges.EqualsLiteral("bytes");
|
||||
|
||||
PRUint32 responseStatus = 0;
|
||||
|
@ -169,8 +183,12 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
|
|||
nsresult nsChannelToPipeListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
|
||||
{
|
||||
mOutput = nsnull;
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyDownloadEnded(aStatus);
|
||||
if (aStatus != NS_BINDING_ABORTED && mDecoder) {
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
mDecoder->ResourceLoaded();
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
mDecoder->NetworkError();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -184,16 +202,18 @@ nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest,
|
|||
if (!mOutput)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
mDecoder->NotifyBytesDownloaded(aCount);
|
||||
|
||||
PRUint32 bytes = 0;
|
||||
|
||||
do {
|
||||
PRUint32 bytes;
|
||||
nsresult rv = mOutput->WriteFrom(aStream, aCount, &bytes);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
aCount -= bytes;
|
||||
} while (aCount);
|
||||
mTotalBytes += bytes;
|
||||
aOffset += bytes;
|
||||
mDecoder->UpdateBytesDownloaded(mOffset + aOffset);
|
||||
} while (aCount) ;
|
||||
|
||||
nsresult rv = mOutput->Flush();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -201,6 +221,8 @@ nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest,
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ public:
|
|||
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
|
||||
virtual PRInt64 Tell();
|
||||
virtual PRUint32 Available();
|
||||
virtual float DownloadRate();
|
||||
virtual void Cancel();
|
||||
virtual nsIPrincipal* GetCurrentPrincipal();
|
||||
virtual void Suspend();
|
||||
|
@ -200,6 +201,12 @@ PRUint32 nsDefaultStreamStrategy::Available()
|
|||
return count;
|
||||
}
|
||||
|
||||
float nsDefaultStreamStrategy::DownloadRate()
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
return mListener ? mListener->BytesPerSecond() : NS_MEDIA_UNKNOWN_RATE;
|
||||
}
|
||||
|
||||
void nsDefaultStreamStrategy::Cancel()
|
||||
{
|
||||
if (mListener)
|
||||
|
@ -240,6 +247,7 @@ public:
|
|||
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
|
||||
virtual PRInt64 Tell();
|
||||
virtual PRUint32 Available();
|
||||
virtual float DownloadRate();
|
||||
virtual nsIPrincipal* GetCurrentPrincipal();
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
|
@ -257,36 +265,6 @@ private:
|
|||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
};
|
||||
|
||||
class LoadedEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LoadedEvent(nsMediaDecoder* aDecoder, PRInt64 aOffset, PRInt64 aSize) :
|
||||
mOffset(aOffset), mSize(aSize), mDecoder(aDecoder)
|
||||
{
|
||||
MOZ_COUNT_CTOR(LoadedEvent);
|
||||
}
|
||||
~LoadedEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(LoadedEvent);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
if (mOffset >= 0) {
|
||||
mDecoder->NotifyDownloadSeeked(mOffset);
|
||||
}
|
||||
if (mSize > 0) {
|
||||
mDecoder->NotifyBytesDownloaded(mSize);
|
||||
}
|
||||
mDecoder->NotifyDownloadEnded(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
PRInt64 mOffset;
|
||||
PRInt64 mSize;
|
||||
nsRefPtr<nsMediaDecoder> mDecoder;
|
||||
};
|
||||
|
||||
nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
|
||||
{
|
||||
if (aStreamListener) {
|
||||
|
@ -332,6 +310,15 @@ nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Get the file size and inform the decoder. Only files up to 4GB are
|
||||
// supported here.
|
||||
PRUint32 size;
|
||||
rv = mInput->Available(&size);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mDecoder->SetTotalBytes(size);
|
||||
mDecoder->UpdateBytesDownloaded(size);
|
||||
}
|
||||
|
||||
/* Get our principal */
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService("@mozilla.org/scriptsecuritymanager;1");
|
||||
|
@ -343,21 +330,12 @@ nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
|
|||
}
|
||||
}
|
||||
|
||||
// Get the file size and inform the decoder. Only files up to 4GB are
|
||||
// supported here.
|
||||
PRUint32 size;
|
||||
rv = mInput->Available(&size);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mDecoder->SetTotalBytes(size);
|
||||
}
|
||||
|
||||
// This must happen before we return from this function, we can't
|
||||
// defer it to the LoadedEvent because that would allow reads from
|
||||
// the stream to complete before this notification is sent.
|
||||
mDecoder->NotifyBytesDownloaded(size);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder, -1, 0);
|
||||
// For a file stream the resource is considered loaded since there
|
||||
// is no buffering delays, etc reading.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NEW_RUNNABLE_METHOD(nsMediaDecoder, mDecoder, ResourceLoaded);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -377,35 +355,13 @@ nsresult nsFileStreamStrategy::Close()
|
|||
nsresult nsFileStreamStrategy::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (!mInput)
|
||||
return NS_ERROR_FAILURE;
|
||||
return mInput->Read(aBuffer, aCount, aBytes);
|
||||
return mInput ? mInput->Read(aBuffer, aCount, aBytes) : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult nsFileStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
{
|
||||
PRUint32 size = 0;
|
||||
PRInt64 absoluteOffset = 0;
|
||||
nsresult rv;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (!mSeekable)
|
||||
return NS_ERROR_FAILURE;
|
||||
rv = mSeekable->Seek(aWhence, aOffset);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSeekable->Tell(&absoluteOffset);
|
||||
}
|
||||
mInput->Available(&size);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder, absoluteOffset, size);
|
||||
// Synchronous dispatch to ensure the decoder is notified before our caller
|
||||
// proceeds and reads occur.
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
return rv;
|
||||
nsAutoLock lock(mLock);
|
||||
return mSeekable ? mSeekable->Seek(aWhence, aOffset) : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRInt64 nsFileStreamStrategy::Tell()
|
||||
|
@ -430,6 +386,11 @@ PRUint32 nsFileStreamStrategy::Available()
|
|||
return count;
|
||||
}
|
||||
|
||||
float nsFileStreamStrategy::DownloadRate()
|
||||
{
|
||||
return NS_MEDIA_UNKNOWN_RATE;
|
||||
}
|
||||
|
||||
nsIPrincipal* nsFileStreamStrategy::GetCurrentPrincipal()
|
||||
{
|
||||
return mPrincipal;
|
||||
|
@ -464,6 +425,7 @@ public:
|
|||
virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
|
||||
virtual PRInt64 Tell();
|
||||
virtual PRUint32 Available();
|
||||
virtual float DownloadRate();
|
||||
virtual void Cancel();
|
||||
virtual nsIPrincipal* GetCurrentPrincipal();
|
||||
virtual void Suspend();
|
||||
|
@ -532,7 +494,7 @@ nsresult nsHttpStreamStrategy::OpenInternal(nsIStreamListener **aStreamListener,
|
|||
*aStreamListener = nsnull;
|
||||
}
|
||||
|
||||
mListener = new nsChannelToPipeListener(mDecoder, aOffset != 0);
|
||||
mListener = new nsChannelToPipeListener(mDecoder, aOffset != 0, aOffset);
|
||||
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = mListener->Init();
|
||||
|
@ -582,11 +544,12 @@ nsresult nsHttpStreamStrategy::OpenInternal(nsIStreamListener **aStreamListener,
|
|||
rv = mListener->GetInputStream(getter_AddRefs(mPipeInput));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDecoder->NotifyDownloadSeeked(aOffset);
|
||||
mPosition = aOffset;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsHttpStreamStrategy::Close()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
@ -674,14 +637,15 @@ private:
|
|||
nsHttpStreamStrategy* mStrategy;
|
||||
nsMediaDecoder* mDecoder;
|
||||
nsIURI* mURI;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsChannelToPipeListener> mListener;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
PRInt64 mOffset;
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
||||
{
|
||||
PRInt64 totalBytes = mDecoder->GetStatistics().mTotalBytes;
|
||||
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (!mChannel || !mPipeInput)
|
||||
|
@ -693,7 +657,7 @@ nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
|||
// to end of file and sets mAtEOF. Tell() looks for this flag being
|
||||
// set and returns the content length.
|
||||
if(aWhence == nsISeekableStream::NS_SEEK_END && aOffset == 0) {
|
||||
if (totalBytes == -1)
|
||||
if (mDecoder->GetTotalBytes() == -1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
mAtEOF = PR_TRUE;
|
||||
|
@ -707,10 +671,12 @@ nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
|||
// NS_SEEK_SET
|
||||
switch (aWhence) {
|
||||
case nsISeekableStream::NS_SEEK_END: {
|
||||
if (totalBytes == -1)
|
||||
PRInt32 length;
|
||||
mChannel->GetContentLength(&length);
|
||||
if (length == -1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
aOffset += totalBytes;
|
||||
aOffset -= length;
|
||||
aWhence = nsISeekableStream::NS_SEEK_SET;
|
||||
break;
|
||||
}
|
||||
|
@ -751,7 +717,7 @@ nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
|||
// Read until the read cursor reaches new seek point. If Cancel() is
|
||||
// called then the read will fail with an error so we can bail out of
|
||||
// the blocking call.
|
||||
PRInt32 bytesRead = 0;
|
||||
PRUint32 bytesRead = 0;
|
||||
PRUint32 bytes = 0;
|
||||
do {
|
||||
nsresult rv = mPipeInput->Read(data.get(),
|
||||
|
@ -762,13 +728,6 @@ nsresult nsHttpStreamStrategy::Seek(PRInt32 aWhence, PRInt64 aOffset)
|
|||
mPosition += bytes;
|
||||
bytesRead += bytes;
|
||||
} while (bytesRead != bytesAhead);
|
||||
|
||||
// We don't need to notify the decoder here that we seeked. It will
|
||||
// look like we just read ahead a bit. In fact, we mustn't tell
|
||||
// the decoder that we seeked, since the seek notification might
|
||||
// race with the "data downloaded" notification after the data was
|
||||
// written into the pipe, so that the seek notification
|
||||
// happens *first*, hopelessly confusing the decoder.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
@ -792,7 +751,7 @@ PRInt64 nsHttpStreamStrategy::Tell()
|
|||
{
|
||||
// Handle the case of a seek to EOF by liboggz
|
||||
// (See Seek for details)
|
||||
return mAtEOF ? mDecoder->GetStatistics().mTotalBytes : mPosition;
|
||||
return mAtEOF ? mDecoder->GetTotalBytes() : mPosition;
|
||||
}
|
||||
|
||||
PRUint32 nsHttpStreamStrategy::Available()
|
||||
|
@ -809,6 +768,14 @@ PRUint32 nsHttpStreamStrategy::Available()
|
|||
return count;
|
||||
}
|
||||
|
||||
float nsHttpStreamStrategy::DownloadRate()
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (!mListener)
|
||||
return NS_MEDIA_UNKNOWN_RATE;
|
||||
return mListener->BytesPerSecond();
|
||||
}
|
||||
|
||||
void nsHttpStreamStrategy::Cancel()
|
||||
{
|
||||
mCancelled = PR_TRUE;
|
||||
|
@ -839,7 +806,8 @@ void nsHttpStreamStrategy::Resume()
|
|||
mChannel->Resume();
|
||||
}
|
||||
|
||||
nsMediaStream::nsMediaStream()
|
||||
nsMediaStream::nsMediaStream() :
|
||||
mPlaybackRateCount(0)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"nsMediaStream created on non-main thread");
|
||||
|
@ -879,6 +847,9 @@ nsresult nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI,
|
|||
else
|
||||
mStreamStrategy = new nsDefaultStreamStrategy(aDecoder, channel, aURI);
|
||||
|
||||
mPlaybackRateCount = 0;
|
||||
mPlaybackRateStart = PR_IntervalNow();
|
||||
|
||||
return mStreamStrategy->Open(aListener);
|
||||
}
|
||||
|
||||
|
@ -893,9 +864,7 @@ nsresult nsMediaStream::Close()
|
|||
nsresult nsMediaStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
{
|
||||
nsresult rv = mStreamStrategy->Read(aBuffer, aCount, aBytes);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mStreamStrategy->Decoder()->NotifyBytesConsumed(*aBytes);
|
||||
}
|
||||
mPlaybackRateCount += *aBytes;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -914,6 +883,18 @@ PRUint32 nsMediaStream::Available()
|
|||
return mStreamStrategy->Available();
|
||||
}
|
||||
|
||||
float nsMediaStream::DownloadRate()
|
||||
{
|
||||
return mStreamStrategy->DownloadRate();
|
||||
}
|
||||
|
||||
float nsMediaStream::PlaybackRate()
|
||||
{
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
PRUint32 interval = PR_IntervalToMilliseconds(now - mPlaybackRateStart);
|
||||
return static_cast<float>(mPlaybackRateCount) * 1000 / interval;
|
||||
}
|
||||
|
||||
void nsMediaStream::Cancel()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
|
|
|
@ -151,8 +151,6 @@ public:
|
|||
aStream->Write(mAudioData.Elements(), length);
|
||||
}
|
||||
|
||||
// The position in the stream where this frame ended, in bytes
|
||||
PRInt64 mEndStreamPosition;
|
||||
nsAutoArrayPtr<unsigned char> mVideoData;
|
||||
nsTArray<float> mAudioData;
|
||||
int mVideoWidth;
|
||||
|
@ -198,12 +196,12 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
PRBool IsEmpty() const
|
||||
PRBool IsEmpty()
|
||||
{
|
||||
return mEmpty;
|
||||
}
|
||||
|
||||
PRBool IsFull() const
|
||||
PRBool IsFull()
|
||||
{
|
||||
return !mEmpty && mHead == mTail;
|
||||
}
|
||||
|
@ -300,12 +298,6 @@ public:
|
|||
// be called with the decode monitor held.
|
||||
void ClearPositionChangeFlag();
|
||||
|
||||
// Must be called with the decode monitor held. Can be called by main
|
||||
// thread.
|
||||
PRBool HaveNextFrameData() const {
|
||||
return !mDecodedFrames.IsEmpty();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Convert the OggPlay frame information into a format used by Gecko
|
||||
// (RGB for video, float for sound, etc).The decoder monitor must be
|
||||
|
@ -362,9 +354,8 @@ private:
|
|||
OggPlay* mPlayer;
|
||||
|
||||
// Frame data containing decoded video/audio for the frame the
|
||||
// current frame and the previous frame. Always accessed with monitor
|
||||
// held. Written only via the decoder thread, but can be tested on
|
||||
// main thread via HaveNextFrameData.
|
||||
// current frame and the previous frame. Accessed only via the
|
||||
// decoder thread.
|
||||
FrameQueue mDecodedFrames;
|
||||
|
||||
// The time that playback started from the system clock. This is used
|
||||
|
@ -412,17 +403,13 @@ private:
|
|||
|
||||
// Number of bytes to buffer when buffering. Only accessed in the
|
||||
// decoder thread.
|
||||
PRInt64 mBufferingBytes;
|
||||
PRUint32 mBufferingBytes;
|
||||
|
||||
// The time value of the last decoded video frame. Used for
|
||||
// computing the sleep period between frames for a/v sync.
|
||||
// Read/Write from the decode thread only.
|
||||
float mLastFrameTime;
|
||||
|
||||
// The decoder position of the end of the last decoded video frame.
|
||||
// Read/Write from the decode thread only.
|
||||
PRInt64 mLastFramePosition;
|
||||
|
||||
// *****
|
||||
// The follow fields are accessed by the decoder thread or
|
||||
// the main thread.
|
||||
|
@ -491,7 +478,6 @@ nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder) :
|
|||
mBufferingStart(0),
|
||||
mBufferingBytes(0),
|
||||
mLastFrameTime(0),
|
||||
mLastFramePosition(-1),
|
||||
mState(DECODER_STATE_DECODING_METADATA),
|
||||
mSeekTime(0.0),
|
||||
mCurrentFrameTime(0.0),
|
||||
|
@ -530,18 +516,7 @@ nsOggDecodeStateMachine::FrameData* nsOggDecodeStateMachine::NextFrame()
|
|||
}
|
||||
|
||||
frame->mTime = mLastFrameTime;
|
||||
frame->mEndStreamPosition = mDecoder->mDecoderPosition;
|
||||
mLastFrameTime += mCallbackPeriod;
|
||||
|
||||
if (mLastFramePosition >= 0) {
|
||||
NS_ASSERTION(frame->mEndStreamPosition >= mLastFramePosition,
|
||||
"Playback positions must not decrease without an intervening reset");
|
||||
mDecoder->mPlaybackStatistics.Start(frame->mTime*PR_TicksPerSecond());
|
||||
mDecoder->mPlaybackStatistics.AddBytes(frame->mEndStreamPosition - mLastFramePosition);
|
||||
mDecoder->mPlaybackStatistics.Stop(mLastFrameTime*PR_TicksPerSecond());
|
||||
}
|
||||
mLastFramePosition = frame->mEndStreamPosition;
|
||||
|
||||
int num_tracks = oggplay_get_num_tracks(mPlayer);
|
||||
float audioTime = 0.0;
|
||||
float videoTime = 0.0;
|
||||
|
@ -673,7 +648,6 @@ void nsOggDecodeStateMachine::PlayFrame() {
|
|||
PlayAudio(frame);
|
||||
mDecodedFrames.Pop();
|
||||
PlayVideo(mDecodedFrames.IsEmpty() ? frame : mDecodedFrames.Peek());
|
||||
mDecoder->mPlaybackPosition = frame->mEndStreamPosition;
|
||||
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
||||
delete frame;
|
||||
}
|
||||
|
@ -854,7 +828,6 @@ void nsOggDecodeStateMachine::Shutdown()
|
|||
if (mPlayer) {
|
||||
oggplay_prepare_for_close(mPlayer);
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Changed state to SHUTDOWN"));
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
@ -865,7 +838,6 @@ void nsOggDecodeStateMachine::Decode()
|
|||
// we are currently buffering.
|
||||
nsAutoMonitor mon(mDecoder->GetMonitor());
|
||||
if (mState == DECODER_STATE_BUFFERING) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from BUFFERING to DECODING"));
|
||||
mState = DECODER_STATE_DECODING;
|
||||
}
|
||||
}
|
||||
|
@ -874,7 +846,6 @@ void nsOggDecodeStateMachine::Seek(float aTime)
|
|||
{
|
||||
nsAutoMonitor mon(mDecoder->GetMonitor());
|
||||
mSeekTime = aTime;
|
||||
LOG(PR_LOG_DEBUG, ("Changed state to SEEKING (to %f)", aTime));
|
||||
mState = DECODER_STATE_SEEKING;
|
||||
}
|
||||
|
||||
|
@ -894,7 +865,6 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
mon.Enter();
|
||||
|
||||
if (mState == DECODER_STATE_DECODING_METADATA) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING_METADATA to DECODING_FIRSTFRAME"));
|
||||
mState = DECODER_STATE_DECODING_FIRSTFRAME;
|
||||
}
|
||||
break;
|
||||
|
@ -915,7 +885,6 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
FrameData* frame = NextFrame();
|
||||
if (frame) {
|
||||
mDecodedFrames.Push(frame);
|
||||
mDecoder->mPlaybackPosition = frame->mEndStreamPosition;
|
||||
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
||||
PlayVideo(frame);
|
||||
}
|
||||
|
@ -925,7 +894,6 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING_FIRSTFRAME to DECODING"));
|
||||
mState = DECODER_STATE_DECODING;
|
||||
}
|
||||
}
|
||||
|
@ -933,61 +901,51 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
|
||||
case DECODER_STATE_DECODING:
|
||||
{
|
||||
PRBool bufferExhausted = PR_FALSE;
|
||||
|
||||
if (!mDecodedFrames.IsFull()) {
|
||||
PRInt64 initialDownloadPosition = mDecoder->mDownloadPosition;
|
||||
|
||||
mon.Exit();
|
||||
OggPlayErrorCode r = DecodeFrame();
|
||||
mon.Enter();
|
||||
|
||||
// Check whether decoding that frame required us to read data
|
||||
// that wasn't available at the start of the frame. That means
|
||||
// we should probably start buffering.
|
||||
bufferExhausted =
|
||||
mDecoder->mDecoderPosition > initialDownloadPosition;
|
||||
|
||||
if (mState != DECODER_STATE_DECODING)
|
||||
continue;
|
||||
|
||||
// Get the decoded frame and store it in our queue of decoded frames
|
||||
FrameData* frame = NextFrame();
|
||||
if (frame) {
|
||||
mDecodedFrames.Push(frame);
|
||||
}
|
||||
|
||||
if (r != E_OGGPLAY_CONTINUE &&
|
||||
r != E_OGGPLAY_USER_INTERRUPT &&
|
||||
r != E_OGGPLAY_TIMEOUT) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to COMPLETED"));
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
// Show at least the first frame if we're not playing
|
||||
// so we have a poster frame on initial load and after seek.
|
||||
if (!mPlaying && !mDecodedFrames.IsEmpty()) {
|
||||
PlayVideo(mDecodedFrames.Peek());
|
||||
}
|
||||
|
||||
if (bufferExhausted && mState == DECODER_STATE_DECODING &&
|
||||
mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING &&
|
||||
(mDecoder->mTotalBytes < 0 ||
|
||||
mDecoder->mDownloadPosition < mDecoder->mTotalBytes)) {
|
||||
// There is at most one frame in the queue and there's
|
||||
// more data to load. Let's buffer to make sure we can play a
|
||||
// decent amount of video in the future.
|
||||
if (mPlaying) {
|
||||
StopPlayback();
|
||||
// Before decoding check if we should buffer more data
|
||||
if (reader->DownloadRate() >= 0 &&
|
||||
reader->Available() < reader->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) {
|
||||
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
|
||||
if (mPlaying) {
|
||||
StopPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
mBufferingStart = PR_IntervalNow();
|
||||
double playbackRate = mDecoder->GetStatistics().mPlaybackRate;
|
||||
mBufferingBytes = BUFFERING_RATE(playbackRate) * BUFFERING_WAIT;
|
||||
mBufferingBytes = PRUint32(BUFFERING_RATE(reader->PlaybackRate()) * BUFFERING_WAIT);
|
||||
mState = DECODER_STATE_BUFFERING;
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING (%d bytes)", PRInt32(mBufferingBytes)));
|
||||
} else {
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, BufferingStarted);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
else {
|
||||
if (!mDecodedFrames.IsFull()) {
|
||||
mon.Exit();
|
||||
OggPlayErrorCode r = DecodeFrame();
|
||||
mon.Enter();
|
||||
|
||||
if (mState != DECODER_STATE_DECODING)
|
||||
continue;
|
||||
|
||||
// Get the decoded frame and store it in our queue of decoded frames
|
||||
FrameData* frame = NextFrame();
|
||||
if (frame) {
|
||||
mDecodedFrames.Push(frame);
|
||||
}
|
||||
|
||||
if (r != E_OGGPLAY_CONTINUE &&
|
||||
r != E_OGGPLAY_USER_INTERRUPT &&
|
||||
r != E_OGGPLAY_TIMEOUT) {
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
}
|
||||
}
|
||||
|
||||
// Show at least the first frame if we're not playing
|
||||
// so we have a poster frame on initial load and after seek.
|
||||
if (!mPlaying && !mDecodedFrames.IsEmpty()) {
|
||||
PlayVideo(mDecodedFrames.Peek());
|
||||
}
|
||||
|
||||
PlayFrame();
|
||||
}
|
||||
}
|
||||
|
@ -1010,9 +968,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, SeekingStarted);
|
||||
NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
|
||||
|
||||
LOG(PR_LOG_DEBUG, ("Entering oggplay_seek(%f)", seekTime));
|
||||
oggplay_seek(mPlayer, ogg_int64_t(seekTime * 1000));
|
||||
LOG(PR_LOG_DEBUG, ("Leaving oggplay_seek"));
|
||||
|
||||
// Reactivate all tracks. Liboggplay deactivates tracks when it
|
||||
// reads to the end of stream, but they must be reactivated in order
|
||||
|
@ -1024,7 +980,6 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
}
|
||||
|
||||
mon.Enter();
|
||||
mLastFramePosition = mDecoder->mDecoderPosition;
|
||||
mDecoder->StartProgressUpdates();
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
continue;
|
||||
|
@ -1059,44 +1014,40 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
mon.Enter();
|
||||
|
||||
if (mState == DECODER_STATE_SEEKING && mSeekTime == seekTime) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from SEEKING (to %f) to DECODING", seekTime));
|
||||
mState = DECODER_STATE_DECODING;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DECODER_STATE_BUFFERING:
|
||||
{
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
if ((PR_IntervalToMilliseconds(now - mBufferingStart) < BUFFERING_WAIT*1000) &&
|
||||
reader->Available() < mBufferingBytes &&
|
||||
(mDecoder->mTotalBytes < 0 || mDecoder->mDownloadPosition < mDecoder->mTotalBytes)) {
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("In buffering: buffering data until %d bytes available or %d milliseconds",
|
||||
PRUint32(mBufferingBytes - reader->Available()),
|
||||
BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(now - mBufferingStart))));
|
||||
mon.Wait(PR_MillisecondsToInterval(1000));
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
continue;
|
||||
} else {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from BUFFERING to DECODING"));
|
||||
mState = DECODER_STATE_DECODING;
|
||||
}
|
||||
if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart) < BUFFERING_WAIT*1000) &&
|
||||
reader->DownloadRate() >= 0 &&
|
||||
reader->Available() < mBufferingBytes) {
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("Buffering data until %d bytes available or %d milliseconds",
|
||||
mBufferingBytes - reader->Available(),
|
||||
BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart))));
|
||||
mon.Wait(PR_MillisecondsToInterval(1000));
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
mState = DECODER_STATE_DECODING;
|
||||
}
|
||||
|
||||
if (mState != DECODER_STATE_BUFFERING) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, BufferingStopped);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
|
||||
if (!mPlaying) {
|
||||
StartPlayback();
|
||||
}
|
||||
if (mState != DECODER_STATE_BUFFERING) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, BufferingStopped);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
|
||||
if (!mPlaying) {
|
||||
StartPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DECODER_STATE_COMPLETED:
|
||||
{
|
||||
while (mState == DECODER_STATE_COMPLETED &&
|
||||
|
@ -1115,9 +1066,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
|||
|
||||
if (mAudioStream) {
|
||||
mon.Exit();
|
||||
LOG(PR_LOG_DEBUG, ("Begin nsAudioStream::Drain"));
|
||||
mAudioStream->Drain();
|
||||
LOG(PR_LOG_DEBUG, ("End nsAudioStream::Drain"));
|
||||
mon.Enter();
|
||||
if (mState != DECODER_STATE_COMPLETED)
|
||||
continue;
|
||||
|
@ -1256,14 +1205,11 @@ float nsOggDecoder::GetDuration()
|
|||
|
||||
nsOggDecoder::nsOggDecoder() :
|
||||
nsMediaDecoder(),
|
||||
mTotalBytes(-1),
|
||||
mDownloadPosition(0),
|
||||
mProgressPosition(0),
|
||||
mDecoderPosition(0),
|
||||
mPlaybackPosition(0),
|
||||
mBytesDownloaded(0),
|
||||
mCurrentTime(0.0),
|
||||
mInitialVolume(0.0),
|
||||
mRequestedSeekTime(-1.0),
|
||||
mContentLength(-1),
|
||||
mNotifyOnShutdown(PR_FALSE),
|
||||
mSeekable(PR_TRUE),
|
||||
mReader(0),
|
||||
|
@ -1282,7 +1228,7 @@ PRBool nsOggDecoder::Init(nsHTMLMediaElement* aElement)
|
|||
return mMonitor && nsMediaDecoder::Init(aElement);
|
||||
}
|
||||
|
||||
void nsOggDecoder::Shutdown()
|
||||
void nsOggDecoder::Shutdown()
|
||||
{
|
||||
mShuttingDown = PR_TRUE;
|
||||
|
||||
|
@ -1306,10 +1252,7 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
|
|||
mStopping = PR_FALSE;
|
||||
|
||||
// Reset progress member variables
|
||||
mDownloadPosition = 0;
|
||||
mProgressPosition = 0;
|
||||
mDecoderPosition = 0;
|
||||
mPlaybackPosition = 0;
|
||||
mBytesDownloaded = 0;
|
||||
mResourceLoaded = PR_FALSE;
|
||||
|
||||
NS_ASSERTION(!mReader, "Didn't shutdown properly!");
|
||||
|
@ -1338,8 +1281,6 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
|
|||
|
||||
mReader = new nsChannelReader();
|
||||
NS_ENSURE_TRUE(mReader, NS_ERROR_OUT_OF_MEMORY);
|
||||
mDownloadStatistics.Reset();
|
||||
mDownloadStatistics.Start(PR_IntervalNow());
|
||||
|
||||
nsresult rv = mReader->Init(this, mURI, aChannel, aStreamListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1350,7 +1291,7 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
|
|||
mDecodeStateMachine = new nsOggDecodeStateMachine(this);
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mDecodeStateMachine->SetContentLength(mTotalBytes);
|
||||
mDecodeStateMachine->SetContentLength(mContentLength);
|
||||
mDecodeStateMachine->SetSeekable(mSeekable);
|
||||
}
|
||||
|
||||
|
@ -1446,7 +1387,6 @@ void nsOggDecoder::Stop()
|
|||
ChangeState(PLAY_STATE_ENDED);
|
||||
|
||||
StopProgress();
|
||||
mDownloadStatistics.Stop(PR_IntervalNow());
|
||||
|
||||
// Force any outstanding seek and byterange requests to complete
|
||||
// to prevent shutdown from deadlocking.
|
||||
|
@ -1568,7 +1508,7 @@ void nsOggDecoder::FirstFrameLoaded()
|
|||
}
|
||||
}
|
||||
|
||||
if (!mResourceLoaded && mDownloadPosition == mTotalBytes) {
|
||||
if (!mResourceLoaded && mBytesDownloaded == mContentLength) {
|
||||
ResourceLoaded();
|
||||
}
|
||||
}
|
||||
|
@ -1582,23 +1522,28 @@ void nsOggDecoder::ResourceLoaded()
|
|||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
PRBool ignoreProgress = PR_FALSE;
|
||||
|
||||
{
|
||||
// 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 (mIgnoreProgressData || mResourceLoaded || mPlayState == PLAY_STATE_LOADING)
|
||||
ignoreProgress = mIgnoreProgressData;
|
||||
if (ignoreProgress || mResourceLoaded || mPlayState == PLAY_STATE_LOADING)
|
||||
return;
|
||||
|
||||
Progress(PR_FALSE);
|
||||
|
||||
// Note that mTotalBytes should not be -1 now; NotifyDownloadEnded
|
||||
// should have set it to the download position.
|
||||
NS_ASSERTION(mDownloadPosition == mTotalBytes, "Wrong byte count");
|
||||
|
||||
mResourceLoaded = PR_TRUE;
|
||||
StopProgress();
|
||||
}
|
||||
|
||||
Progress(PR_FALSE);
|
||||
|
||||
// If we know the content length, set the bytes downloaded to this
|
||||
// so the final progress event gets the correct final value.
|
||||
if (mContentLength >= 0) {
|
||||
mBytesDownloaded = mContentLength;
|
||||
}
|
||||
|
||||
mResourceLoaded = PR_TRUE;
|
||||
StopProgress();
|
||||
|
||||
// Ensure the final progress event gets fired
|
||||
if (mElement) {
|
||||
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
|
||||
|
@ -1608,7 +1553,7 @@ void nsOggDecoder::ResourceLoaded()
|
|||
|
||||
void nsOggDecoder::NetworkError()
|
||||
{
|
||||
if (mStopping || mShuttingDown)
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mElement)
|
||||
|
@ -1648,125 +1593,52 @@ NS_IMETHODIMP nsOggDecoder::Observe(nsISupports *aSubjet,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsMediaDecoder::Statistics
|
||||
nsOggDecoder::GetStatistics()
|
||||
PRUint64 nsOggDecoder::GetBytesLoaded()
|
||||
{
|
||||
Statistics result;
|
||||
return mBytesDownloaded;
|
||||
}
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
result.mDownloadRate =
|
||||
mDownloadStatistics.GetRate(PR_IntervalNow(), &result.mDownloadRateReliable);
|
||||
if (mDuration >= 0 && mTotalBytes >= 0) {
|
||||
result.mPlaybackRate = double(mTotalBytes)*1000.0/mDuration;
|
||||
result.mPlaybackRateReliable = PR_TRUE;
|
||||
} else {
|
||||
result.mPlaybackRate =
|
||||
mPlaybackStatistics.GetRateAtLastStop(&result.mPlaybackRateReliable);
|
||||
}
|
||||
result.mTotalBytes = mTotalBytes;
|
||||
// Use mProgressPosition here because we don't want changes in
|
||||
// mDownloadPosition due to intermediate seek operations etc to be
|
||||
// reported in progress events
|
||||
result.mDownloadPosition = mProgressPosition;
|
||||
result.mDecoderPosition = mDecoderPosition;
|
||||
result.mPlaybackPosition = mPlaybackPosition;
|
||||
return result;
|
||||
PRInt64 nsOggDecoder::GetTotalBytes()
|
||||
{
|
||||
return mContentLength;
|
||||
}
|
||||
|
||||
void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
// Servers could lie to us about the size of the resource, so make
|
||||
// sure we don't set mTotalBytes to less than what we've already
|
||||
// downloaded
|
||||
mTotalBytes = PR_MAX(mDownloadPosition, aBytes);
|
||||
mContentLength = aBytes;
|
||||
if (mDecodeStateMachine) {
|
||||
mDecodeStateMachine->SetContentLength(mTotalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::NotifyBytesDownloaded(PRInt64 aBytes)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"nsOggDecoder::NotifyBytesDownloaded called on non-main thread");
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
mDownloadPosition += aBytes;
|
||||
if (mTotalBytes >= 0) {
|
||||
// Ensure that mDownloadPosition <= mTotalBytes
|
||||
mTotalBytes = PR_MAX(mTotalBytes, mDownloadPosition);
|
||||
}
|
||||
if (!mIgnoreProgressData) {
|
||||
mDownloadStatistics.AddBytes(aBytes);
|
||||
mProgressPosition = mDownloadPosition;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateReadyStateForData();
|
||||
mDecodeStateMachine->SetContentLength(aBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::NotifyDownloadSeeked(PRInt64 aOffsetBytes)
|
||||
void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
// Don't change mProgressPosition here, since mIgnoreProgressData is set
|
||||
mDownloadPosition = mDecoderPosition = mPlaybackPosition = aOffsetBytes;
|
||||
|
||||
if (!mIgnoreProgressData) {
|
||||
mProgressPosition = mDownloadPosition;
|
||||
mBytesDownloaded = aBytes;
|
||||
}
|
||||
if (mTotalBytes >= 0) {
|
||||
// Ensure that mDownloadPosition <= mTotalBytes
|
||||
mTotalBytes = PR_MAX(mTotalBytes, mDownloadPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
if (aStatus == NS_BINDING_ABORTED)
|
||||
return;
|
||||
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mDownloadStatistics.Stop(PR_IntervalNow());
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
// Update total bytes now we know the end of the stream
|
||||
mTotalBytes = mDownloadPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
ResourceLoaded();
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
NetworkError();
|
||||
}
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
|
||||
void nsOggDecoder::NotifyBytesConsumed(PRInt64 aBytes)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
if (!mIgnoreProgressData) {
|
||||
mDecoderPosition += aBytes;
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::UpdateReadyStateForData()
|
||||
{
|
||||
if (!mElement || mShuttingDown || !mDecodeStateMachine)
|
||||
return;
|
||||
|
||||
PRBool haveNextFrame;
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
haveNextFrame = mDecodeStateMachine->HaveNextFrameData();
|
||||
}
|
||||
mElement->UpdateReadyStateForData(haveNextFrame);
|
||||
}
|
||||
|
||||
void nsOggDecoder::BufferingStopped()
|
||||
{
|
||||
UpdateReadyStateForData();
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mElement) {
|
||||
mElement->ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::BufferingStarted()
|
||||
{
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mElement) {
|
||||
mElement->ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecoder::SeekingStopped()
|
||||
|
@ -1787,7 +1659,6 @@ void nsOggDecoder::SeekingStopped()
|
|||
|
||||
if (mElement) {
|
||||
mElement->SeekCompleted();
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1932,7 +1803,6 @@ PRBool nsOggDecoder::GetSeekable()
|
|||
|
||||
void nsOggDecoder::Suspend()
|
||||
{
|
||||
mDownloadStatistics.Stop(PR_IntervalNow());
|
||||
if (mReader) {
|
||||
mReader->Suspend();
|
||||
}
|
||||
|
@ -1943,19 +1813,14 @@ void nsOggDecoder::Resume()
|
|||
if (mReader) {
|
||||
mReader->Resume();
|
||||
}
|
||||
mDownloadStatistics.Start(PR_IntervalNow());
|
||||
}
|
||||
|
||||
void nsOggDecoder::StopProgressUpdates()
|
||||
{
|
||||
mIgnoreProgressData = PR_TRUE;
|
||||
mDownloadStatistics.Stop(PR_IntervalNow());
|
||||
}
|
||||
|
||||
void nsOggDecoder::StartProgressUpdates()
|
||||
{
|
||||
mIgnoreProgressData = PR_FALSE;
|
||||
// Resync progress position now
|
||||
mProgressPosition = mDownloadPosition;
|
||||
mDownloadStatistics.Start(PR_IntervalNow());
|
||||
}
|
||||
|
|
|
@ -149,8 +149,7 @@ public:
|
|||
PRBool IsEnded();
|
||||
|
||||
// Called by the decoder to indicate that the media stream has closed.
|
||||
// aAtEnd is true if we read to the end of the file.
|
||||
void StreamEnded(PRBool aAtEnd);
|
||||
void StreamEnded();
|
||||
|
||||
// Main state machine loop. Runs forever, until shutdown state is reached.
|
||||
NS_IMETHOD Run();
|
||||
|
@ -160,23 +159,6 @@ public:
|
|||
// position at the appropriate time.
|
||||
void UpdateTimeOffset(float aTime);
|
||||
|
||||
// Called by the decoder, on the main thread.
|
||||
nsMediaDecoder::Statistics GetStatistics();
|
||||
|
||||
// Called on the main thread only
|
||||
void SetTotalBytes(PRInt64 aBytes);
|
||||
// Called on the main thread
|
||||
void NotifyBytesDownloaded(PRInt64 aBytes);
|
||||
// Called on the main thread
|
||||
void NotifyDownloadSeeked(PRInt64 aOffset);
|
||||
// Called on the main thread
|
||||
void NotifyDownloadEnded(nsresult aStatus);
|
||||
// Called on any thread
|
||||
void NotifyBytesConsumed(PRInt64 aBytes);
|
||||
|
||||
// Called by the main thread
|
||||
PRBool HasPendingData();
|
||||
|
||||
private:
|
||||
// Change the current state and wake the playback thread if it is waiting
|
||||
// on mMonitor. Used by public member functions called from both threads,
|
||||
|
@ -303,19 +285,6 @@ private:
|
|||
// recently requested state on completion.
|
||||
State mNextState;
|
||||
|
||||
// Length of the current resource, or -1 if not available.
|
||||
PRInt64 mTotalBytes;
|
||||
// Current download position in the stream.
|
||||
// NOTE: because we don't have to read when we seek, there is no need
|
||||
// to track a separate "progress position" which ignores download
|
||||
// position changes due to reads servicing seeks.
|
||||
PRInt64 mDownloadPosition;
|
||||
// Current playback position in the stream.
|
||||
PRInt64 mPlaybackPosition;
|
||||
// Data needed to estimate download data rate. The channel timeline is
|
||||
// wall-clock time.
|
||||
nsMediaDecoder::ChannelStatistics mDownloadStatistics;
|
||||
|
||||
// Volume that the audio backend will be initialized with.
|
||||
float mInitialVolume;
|
||||
|
||||
|
@ -355,9 +324,6 @@ nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder* aDecoder, nsMediaStream* a
|
|||
mMonitor(nsnull),
|
||||
mState(STATE_LOADING_METADATA),
|
||||
mNextState(STATE_PAUSED),
|
||||
mTotalBytes(-1),
|
||||
mDownloadPosition(0),
|
||||
mPlaybackPosition(0),
|
||||
mInitialVolume(aInitialVolume),
|
||||
mTimeOffset(0.0),
|
||||
mExpectMoreData(PR_TRUE),
|
||||
|
@ -365,7 +331,6 @@ nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder* aDecoder, nsMediaStream* a
|
|||
mMetadataValid(PR_FALSE)
|
||||
{
|
||||
mMonitor = nsAutoMonitor::NewMonitor("nsWaveStateMachine");
|
||||
mDownloadStatistics.Start(PR_IntervalNow());
|
||||
}
|
||||
|
||||
nsWaveStateMachine::~nsWaveStateMachine()
|
||||
|
@ -443,11 +408,12 @@ nsWaveStateMachine::GetDuration()
|
|||
nsAutoMonitor monitor(mMonitor);
|
||||
if (mMetadataValid) {
|
||||
PRUint32 length = mWaveLength;
|
||||
PRInt64 contentLength = mDecoder->GetTotalBytes();
|
||||
// If the decoder has a valid content length, and it's shorter than the
|
||||
// expected length of the PCM data, calculate the playback duration from
|
||||
// the content length rather than the expected PCM data length.
|
||||
if (mTotalBytes >= 0 && mTotalBytes - mWavePCMOffset < length) {
|
||||
length = mTotalBytes - mWavePCMOffset;
|
||||
if (contentLength >= 0 && contentLength - mWavePCMOffset < length) {
|
||||
length = contentLength - mWavePCMOffset;
|
||||
}
|
||||
return BytesToTime(length);
|
||||
}
|
||||
|
@ -480,23 +446,10 @@ nsWaveStateMachine::IsEnded()
|
|||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::StreamEnded(PRBool aAtEnd)
|
||||
nsWaveStateMachine::StreamEnded()
|
||||
{
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mExpectMoreData = PR_FALSE;
|
||||
|
||||
// If we know the content length, set the bytes downloaded to this
|
||||
// so the final progress event gets the correct final value.
|
||||
if (mTotalBytes >= 0) {
|
||||
mDownloadPosition = mTotalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsWaveStateMachine::HasPendingData()
|
||||
{
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
return mPlaybackPosition < mDownloadPosition;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -538,13 +491,14 @@ nsWaveStateMachine::Run()
|
|||
}
|
||||
break;
|
||||
|
||||
case STATE_BUFFERING: {
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
if ((PR_IntervalToMilliseconds(now - mBufferingStart) < mBufferingWait) &&
|
||||
case STATE_BUFFERING:
|
||||
if ((PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart) < mBufferingWait) &&
|
||||
mStream->DownloadRate() >= 0 &&
|
||||
mStream->Available() < mBufferingBytes) {
|
||||
LOG(PR_LOG_DEBUG, ("Buffering data until %d bytes or %d milliseconds\n",
|
||||
LOG(PR_LOG_DEBUG, ("Buffering data until %d bytes or %d milliseconds (rate %f)\n",
|
||||
mBufferingBytes - mStream->Available(),
|
||||
mBufferingWait - (now - mBufferingStart)));
|
||||
mBufferingWait - (PR_IntervalToMilliseconds(PR_IntervalNow() - mBufferingStart)),
|
||||
mStream->DownloadRate()));
|
||||
monitor.Wait(PR_MillisecondsToInterval(1000));
|
||||
} else {
|
||||
ChangeState(mNextState);
|
||||
|
@ -554,26 +508,30 @@ nsWaveStateMachine::Run()
|
|||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_PLAYING: {
|
||||
case STATE_PLAYING:
|
||||
if (!mAudioStream) {
|
||||
OpenAudioStream();
|
||||
} else {
|
||||
mAudioStream->Resume();
|
||||
}
|
||||
|
||||
if (mStream->Available() < mSampleSize) {
|
||||
if (mExpectMoreData) {
|
||||
// Buffer until mBufferingWait milliseconds of data is available.
|
||||
mBufferingBytes = TimeToBytes(float(mBufferingWait) / 1000.0);
|
||||
mBufferingStart = PR_IntervalNow();
|
||||
ChangeState(STATE_BUFFERING);
|
||||
} else {
|
||||
// Media stream has ended and there is less data available than a
|
||||
// single sample so end playback.
|
||||
ChangeState(STATE_ENDED);
|
||||
}
|
||||
if (mStream->DownloadRate() >= 0 &&
|
||||
mStream->Available() < mStream->PlaybackRate() * BUFFERING_SECONDS_LOW_WATER_MARK) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, BufferingStarted);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
// Buffer until mBufferingWait milliseconds of data is available.
|
||||
mBufferingBytes = TimeToBytes(float(mBufferingWait) / 1000.0);
|
||||
mBufferingStart = PR_IntervalNow();
|
||||
ChangeState(STATE_BUFFERING);
|
||||
}
|
||||
|
||||
if (!mExpectMoreData && mStream->Available() < mSampleSize) {
|
||||
// Media stream has ended and there is less data available than a
|
||||
// single sample so end playback.
|
||||
ChangeState(STATE_ENDED);
|
||||
} else {
|
||||
// Assuming enough data is available from the network, we aim to
|
||||
// completely fill the audio backend's buffers with data. This
|
||||
|
@ -635,7 +593,6 @@ nsWaveStateMachine::Run()
|
|||
monitor.Wait(PR_MillisecondsToInterval(PRUint32(nextWakeup)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_SEEKING:
|
||||
{
|
||||
|
@ -802,59 +759,6 @@ nsWaveStateMachine::CloseAudioStream()
|
|||
}
|
||||
}
|
||||
|
||||
nsMediaDecoder::Statistics
|
||||
nsWaveStateMachine::GetStatistics()
|
||||
{
|
||||
nsMediaDecoder::Statistics result;
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
result.mDownloadRate = mDownloadStatistics.GetRate(now, &result.mDownloadRateReliable);
|
||||
result.mPlaybackRate = mSampleRate*mChannels*mSampleSize;
|
||||
result.mPlaybackRateReliable = PR_TRUE;
|
||||
result.mTotalBytes = mTotalBytes;
|
||||
result.mDownloadPosition = mDownloadPosition;
|
||||
result.mDecoderPosition = mPlaybackPosition;
|
||||
result.mPlaybackPosition = mPlaybackPosition;
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::SetTotalBytes(PRInt64 aBytes) {
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mTotalBytes = aBytes;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::NotifyBytesDownloaded(PRInt64 aBytes)
|
||||
{
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mDownloadStatistics.AddBytes(aBytes);
|
||||
mDownloadPosition += aBytes;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::NotifyDownloadSeeked(PRInt64 aOffset)
|
||||
{
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mDownloadPosition = mPlaybackPosition = aOffset;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
if (aStatus == NS_BINDING_ABORTED)
|
||||
return;
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mDownloadStatistics.Stop(PR_IntervalNow());
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveStateMachine::NotifyBytesConsumed(PRInt64 aBytes)
|
||||
{
|
||||
nsAutoMonitor monitor(mMonitor);
|
||||
mPlaybackPosition += aBytes;
|
||||
}
|
||||
|
||||
static PRUint32
|
||||
ReadUint32BE(const char** aBuffer)
|
||||
{
|
||||
|
@ -1086,7 +990,8 @@ nsWaveStateMachine::FindDataOffset()
|
|||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder, nsIObserver)
|
||||
|
||||
nsWaveDecoder::nsWaveDecoder()
|
||||
: mInitialVolume(1.0),
|
||||
: mBytesDownloaded(0),
|
||||
mInitialVolume(1.0),
|
||||
mStream(nsnull),
|
||||
mTimeOffset(0.0),
|
||||
mEndedCurrentTime(0.0),
|
||||
|
@ -1094,9 +999,7 @@ nsWaveDecoder::nsWaveDecoder()
|
|||
mEnded(PR_FALSE),
|
||||
mNotifyOnShutdown(PR_FALSE),
|
||||
mSeekable(PR_TRUE),
|
||||
mResourceLoaded(PR_FALSE),
|
||||
mMetadataLoadedReported(PR_FALSE),
|
||||
mResourceLoadedReported(PR_FALSE)
|
||||
mResourceLoaded(PR_FALSE)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsWaveDecoder);
|
||||
}
|
||||
|
@ -1245,6 +1148,7 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
|
|||
mStopping = PR_FALSE;
|
||||
|
||||
// Reset progress member variables
|
||||
mBytesDownloaded = 0;
|
||||
mResourceLoaded = PR_FALSE;
|
||||
|
||||
if (aStreamListener) {
|
||||
|
@ -1267,19 +1171,15 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
|
|||
mStream = new nsMediaStream();
|
||||
NS_ENSURE_TRUE(mStream, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
mPlaybackStateMachine = new nsWaveStateMachine(this, mStream.get(),
|
||||
BUFFERING_TIMEOUT * 1000,
|
||||
mInitialVolume);
|
||||
NS_ENSURE_TRUE(mPlaybackStateMachine, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Open the stream *after* setting mPlaybackStateMachine, to ensure
|
||||
// that callbacks (e.g. setting stream size) will actually work
|
||||
nsresult rv = mStream->Open(this, mURI, aChannel, aStreamListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewThread(getter_AddRefs(mPlaybackThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mPlaybackStateMachine = new nsWaveStateMachine(this, mStream.get(),
|
||||
BUFFERING_TIMEOUT * 1000,
|
||||
mInitialVolume);
|
||||
rv = mPlaybackThread->Dispatch(mPlaybackStateMachine, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -1298,13 +1198,15 @@ nsWaveDecoder::MetadataLoaded()
|
|||
mElement->FirstFrameLoaded();
|
||||
}
|
||||
|
||||
mMetadataLoadedReported = PR_TRUE;
|
||||
|
||||
if (mResourceLoaded) {
|
||||
ResourceLoaded();
|
||||
} else {
|
||||
if (!mResourceLoaded) {
|
||||
StartProgress();
|
||||
}
|
||||
else if (mElement)
|
||||
{
|
||||
// Resource was loaded during metadata loading, when progress
|
||||
// events are being ignored. Fire the final progress event.
|
||||
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1326,25 +1228,28 @@ nsWaveDecoder::ResourceLoaded()
|
|||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->StreamEnded(PR_TRUE);
|
||||
|
||||
// If we know the content length, set the bytes downloaded to this
|
||||
// so the final progress event gets the correct final value.
|
||||
if (mContentLength >= 0) {
|
||||
mBytesDownloaded = mContentLength;
|
||||
}
|
||||
|
||||
mResourceLoaded = PR_TRUE;
|
||||
|
||||
if (!mMetadataLoadedReported || mResourceLoadedReported)
|
||||
return;
|
||||
if (mElement) {
|
||||
mElement->ResourceLoaded();
|
||||
}
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->StreamEnded();
|
||||
}
|
||||
|
||||
StopProgress();
|
||||
|
||||
// Ensure the final progress event gets fired
|
||||
if (mElement) {
|
||||
// Ensure the final progress event gets fired
|
||||
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
|
||||
mElement->ResourceLoaded();
|
||||
}
|
||||
|
||||
mResourceLoadedReported = PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1357,7 +1262,7 @@ nsWaveDecoder::NetworkError()
|
|||
mElement->NetworkError();
|
||||
}
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->StreamEnded(PR_FALSE);
|
||||
mPlaybackStateMachine->StreamEnded();
|
||||
}
|
||||
Stop();
|
||||
}
|
||||
|
@ -1380,63 +1285,28 @@ nsWaveDecoder::IsEnded() const
|
|||
return mEnded;
|
||||
}
|
||||
|
||||
nsMediaDecoder::Statistics
|
||||
nsWaveDecoder::GetStatistics()
|
||||
PRUint64
|
||||
nsWaveDecoder::GetBytesLoaded()
|
||||
{
|
||||
if (!mPlaybackStateMachine)
|
||||
return Statistics();
|
||||
return mPlaybackStateMachine->GetStatistics();
|
||||
return mBytesDownloaded;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::NotifyBytesDownloaded(PRInt64 aBytes)
|
||||
PRInt64
|
||||
nsWaveDecoder::GetTotalBytes()
|
||||
{
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->NotifyBytesDownloaded(aBytes);
|
||||
}
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::NotifyDownloadSeeked(PRInt64 aBytes)
|
||||
{
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->NotifyDownloadSeeked(aBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->NotifyDownloadEnded(aStatus);
|
||||
}
|
||||
if (aStatus != NS_BINDING_ABORTED) {
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
ResourceLoaded();
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
NetworkError();
|
||||
}
|
||||
}
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::NotifyBytesConsumed(PRInt64 aBytes)
|
||||
{
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->NotifyBytesConsumed(aBytes);
|
||||
}
|
||||
return mContentLength;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::SetTotalBytes(PRInt64 aBytes)
|
||||
{
|
||||
if (mPlaybackStateMachine) {
|
||||
mPlaybackStateMachine->SetTotalBytes(aBytes);
|
||||
} else {
|
||||
NS_WARNING("Forgot total bytes since there is no state machine set up");
|
||||
}
|
||||
mContentLength = aBytes;
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
|
||||
{
|
||||
mBytesDownloaded = aBytes;
|
||||
}
|
||||
|
||||
// An event that gets posted to the main thread, when the media element is
|
||||
|
@ -1484,20 +1354,27 @@ nsWaveDecoder::Observe(nsISupports* aSubject, const char* aTopic, const PRUnicha
|
|||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::UpdateReadyStateForData()
|
||||
nsWaveDecoder::BufferingStarted()
|
||||
{
|
||||
if (!mElement || mShuttingDown || !mPlaybackStateMachine)
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool haveDataToPlay =
|
||||
mPlaybackStateMachine->HasPendingData() && mMetadataLoadedReported;
|
||||
mElement->UpdateReadyStateForData(haveDataToPlay);
|
||||
if (mElement) {
|
||||
mElement->ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWaveDecoder::BufferingStopped()
|
||||
{
|
||||
UpdateReadyStateForData();
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mElement) {
|
||||
mElement->ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1525,7 +1402,6 @@ nsWaveDecoder::SeekingStopped()
|
|||
|
||||
if (mElement) {
|
||||
mElement->SeekCompleted();
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@ _TEST_FILES += \
|
|||
test_ended1.html \
|
||||
test_ended2.html \
|
||||
test_onloadedmetadata.html \
|
||||
test_progress1.html \
|
||||
test_progress3.html \
|
||||
test_standalone.html \
|
||||
test_timeupdate1.html \
|
||||
|
@ -97,6 +96,7 @@ _TEST_FILES += \
|
|||
test_timeupdate3.html \
|
||||
$(NULL)
|
||||
endif
|
||||
# test_progress1.html disabled while we figure out the random failure
|
||||
else
|
||||
_TEST_FILES += \
|
||||
test_can_play_type_no_ogg.html \
|
||||
|
|
|
@ -64,8 +64,8 @@ var gTestedRemoved = false;
|
|||
var gOldPref;
|
||||
|
||||
function result(code) {
|
||||
dump((gTestNum - 1) + ": " + code + "\n");
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
dump("result " + code);
|
||||
opener.is(code, gExpectedResult, gTestDescription);
|
||||
nextTest();
|
||||
}
|
||||
|
@ -105,9 +105,7 @@ function nextTest() {
|
|||
}
|
||||
gExpectedResult = gTests[gTestNum].result;
|
||||
gTestDescription = gTests[gTestNum].description;
|
||||
dump("Starting test " + gTestNum + " at " + gTests[gTestNum].url + "\n");
|
||||
video.src = gTests[gTestNum].url;
|
||||
video.load();
|
||||
gTestNum++;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ function late_add_sources_first(element, name, type) {
|
|||
}
|
||||
|
||||
function check_ogg(e) {
|
||||
is(e.videoWidth, 320, "video width " + e.currentSrc);
|
||||
is(e.videoHeight, 240, "video height " + e.currentSrc);
|
||||
ok(e.videoWidth == 320 && e.videoHeight == 240, "video should be 320x240");
|
||||
}
|
||||
|
||||
function check_wav(e) {
|
||||
|
|
|
@ -12,23 +12,20 @@
|
|||
var completed = false;
|
||||
var load_count = 0;
|
||||
var last_progress = false;
|
||||
var last_progress_total = 0;
|
||||
|
||||
function on_loadedmetadata() {
|
||||
var v = document.getElementById('v');
|
||||
ok(v, "Found video element after metadata loaded");
|
||||
ok(v, "Found video element after metadata loaded: " + v);
|
||||
v.play();
|
||||
dump('test_progress1: on_loadedmetadata exiting\n');
|
||||
}
|
||||
|
||||
function do_progress(e) {
|
||||
dump('test_progress1: do_progress ' + e.loaded + '\n');
|
||||
ok(!completed, "Check for progress event after completed");
|
||||
ok(!completed, "Check for progress event after completed: " + completed);
|
||||
ok(e.lengthComputable, "Check progress lengthComputable");
|
||||
ok(e.loaded >= last_progress_total, "Check progress increasing: " + e.loaded);
|
||||
last_progress_total = e.loaded;
|
||||
ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded);
|
||||
is(e.total, 285310, "Check progress total");
|
||||
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
|
||||
ok(e.total == 285310, "Check progress total: " + e.total);
|
||||
last_progress = e.loaded;
|
||||
}
|
||||
|
||||
|
@ -36,8 +33,8 @@ function do_ended() {
|
|||
dump('test_progress1: do_ended\n');
|
||||
ok(!completed, "Check for duplicate ended event");
|
||||
completed = true;
|
||||
is(last_progress, 285310, "Last progress event size");
|
||||
is(load_count, 1, "load event raised");
|
||||
ok(last_progress == 285310, "Last progress event size: " + last_progress);
|
||||
ok(load_count == 1, "load event raised: " + load_count);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
// Test progress events with wav backend
|
||||
var completed = false;
|
||||
var last_progress = false;
|
||||
var last_progress_total = 0;
|
||||
|
||||
function on_loadedmetadata() {
|
||||
var v = document.getElementById('v');
|
||||
|
@ -20,19 +19,17 @@ function on_loadedmetadata() {
|
|||
}
|
||||
|
||||
function do_progress(e) {
|
||||
ok(!completed, "Check for progress event after completed");
|
||||
ok(!completed, "Check for progress event after completed: " + completed);
|
||||
ok(e.lengthComputable, "Check progress lengthComputable");
|
||||
ok(e.loaded >= last_progress_total, "Check progress increasing: " + e.loaded);
|
||||
last_progress_total = e.loaded;
|
||||
ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded);
|
||||
is(e.total, 102444, "Check progress total");
|
||||
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
|
||||
ok(e.total == 102444, "Check progress total: " + e.total);
|
||||
last_progress = e.loaded;
|
||||
}
|
||||
|
||||
function do_ended() {
|
||||
ok(!completed, "Check for duplicate ended event");
|
||||
completed = true;
|
||||
is(last_progress, 102444, "Last progress event size");
|
||||
ok(last_progress == 102444, "Last progress event size: " + last_progress);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,47 +14,44 @@
|
|||
var completed = false;
|
||||
var load_count = 0;
|
||||
var last_progress = false;
|
||||
var last_progress_total = 0;
|
||||
|
||||
function on_loadedmetadata() {
|
||||
var v = document.getElementById('v');
|
||||
ok(v, "Found video element after metadata loaded");
|
||||
ok(v, "Found video element after metadata loaded: " + v);
|
||||
v.play();
|
||||
dump('test_progress3: on_loadedmetadata exiting\n');
|
||||
dump('test_progress1: on_loadedmetadata exiting\n');
|
||||
}
|
||||
|
||||
function do_progress(e) {
|
||||
dump('test_progress3: do_progress ' + e.loaded + '/' + e.total + '\n');
|
||||
ok(!completed, "Check for progress event after completed");
|
||||
dump('test_progress1: do_progress ' + e.loaded + '\n');
|
||||
ok(!completed, "Check for progress event after completed: " + completed);
|
||||
ok(e.lengthComputable, "Check progress lengthComputable");
|
||||
ok(e.loaded >= last_progress_total, "Check progress increasing: " + e.loaded);
|
||||
last_progress_total = e.loaded;
|
||||
ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded);
|
||||
is(e.total, 28942, "Check progress total");
|
||||
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
|
||||
ok(e.total == 28942, "Check progress total: " + e.total);
|
||||
last_progress = e.loaded;
|
||||
}
|
||||
|
||||
function do_ended() {
|
||||
dump('test_progress3: do_ended\n');
|
||||
dump('test_progress1: do_ended\n');
|
||||
ok(!completed, "Check for duplicate ended event");
|
||||
completed = true;
|
||||
is(last_progress, 28942, "Last progress event size");
|
||||
is(load_count, 1, "load event raised");
|
||||
ok(last_progress == 28942, "Last progress event size: " + last_progress);
|
||||
ok(load_count == 1, "load event raised: " + load_count);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function do_load(e) {
|
||||
load_count++;
|
||||
dump('test_progress3: do_loaded ' + e.loaded + "\n");
|
||||
dump('test_progress1: do_loaded ' + e.loaded + "\n");
|
||||
}
|
||||
|
||||
function do_timeupdate() {
|
||||
var v = document.getElementById('v');
|
||||
dump('test_progress3: timeupdate: ' + v.currentTime + "\n");
|
||||
dump('test_progress1: timeupdate: ' + v.currentTime + "\n");
|
||||
}
|
||||
|
||||
function do_play() {
|
||||
dump('test_progress3: do_play\n');
|
||||
dump('test_progress1: do_play\n');
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
// before metadata loaded is fired.
|
||||
var completed = false;
|
||||
var last_progress = false;
|
||||
var last_progress_total = 0;
|
||||
|
||||
function on_loadedmetadata() {
|
||||
var v = document.getElementById('v');
|
||||
|
@ -22,19 +21,17 @@ function on_loadedmetadata() {
|
|||
}
|
||||
|
||||
function do_progress(e) {
|
||||
ok(!completed, "Check for progress event after completed");
|
||||
ok(!completed, "Check for progress event after completed: " + completed);
|
||||
ok(e.lengthComputable, "Check progress lengthComputable");
|
||||
ok(e.loaded >= last_progress_total, "Check progress increasing: " + e.loaded);
|
||||
last_progress_total = e.loaded;
|
||||
ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded);
|
||||
is(e.total, 11069, "Check progress total");
|
||||
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
|
||||
ok(e.total == 11069, "Check progress total: " + e.total);
|
||||
last_progress = e.loaded;
|
||||
}
|
||||
|
||||
function do_ended() {
|
||||
ok(!completed, "Check for duplicate ended event");
|
||||
completed = true;
|
||||
is(last_progress, 11069, "Last progress event size");
|
||||
ok(last_progress == 11069, "Last progress event size: " + last_progress);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче