From 004a9ff890326c403b90a9437b9ae51e8bcf2c28 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sat, 11 Apr 2009 21:39:24 +1200 Subject: [PATCH] Bug 486991. Convert nsMediaStream and media decoders to use TimeStamp/TimeDuration. r=doublec --- content/media/video/public/nsMediaDecoder.h | 13 +++-- content/media/video/public/nsMediaStream.h | 43 ++++++++------- content/media/video/src/nsMediaDecoder.cpp | 20 +++---- content/media/video/src/nsMediaStream.cpp | 14 ++--- content/media/video/src/nsOggDecoder.cpp | 58 +++++++++++++-------- content/media/video/src/nsWaveDecoder.cpp | 47 +++++++++-------- 6 files changed, 114 insertions(+), 81 deletions(-) diff --git a/content/media/video/public/nsMediaDecoder.h b/content/media/video/public/nsMediaDecoder.h index 1c798c3ffc7..75e3ee704c2 100644 --- a/content/media/video/public/nsMediaDecoder.h +++ b/content/media/video/public/nsMediaDecoder.h @@ -45,7 +45,7 @@ #include "gfxContext.h" #include "gfxRect.h" #include "nsITimer.h" -#include "prinrval.h" +#include "nsTimeStamp.h" #ifdef PR_LOGGING extern PRLogModuleInfo* gVideoDecoderLog; @@ -61,7 +61,10 @@ class nsHTMLMediaElement; // called from any thread. class nsMediaDecoder : public nsIObserver { - public: +public: + typedef mozilla::TimeStamp TimeStamp; + typedef mozilla::TimeDuration TimeDuration; + nsMediaDecoder(); virtual ~nsMediaDecoder(); @@ -265,14 +268,14 @@ protected: // Time that the last progress event was fired. Read/Write from the // main thread only. - PRIntervalTime mProgressTime; + TimeStamp mProgressTime; // Time that data was last read from the media resource. Used for // computing if the download has stalled and to rate limit progress events - // when data is arriving slower than PROGRESS_MS. A value of 0 indicates + // when data is arriving slower than PROGRESS_MS. A value of null 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; + TimeStamp mDataTime; // Lock around the video RGB, width and size data. This // is used in the decoder backend threads and the main thread diff --git a/content/media/video/public/nsMediaStream.h b/content/media/video/public/nsMediaStream.h index 520007decf3..a5a8eb592da 100644 --- a/content/media/video/public/nsMediaStream.h +++ b/content/media/video/public/nsMediaStream.h @@ -46,6 +46,7 @@ #include "nsIStreamListener.h" #include "prlock.h" #include "nsMediaCache.h" +#include "nsTimeStamp.h" // For HTTP seeking, if number of bytes needing to be // seeked forward is less than this value then a read is @@ -64,25 +65,28 @@ class nsMediaDecoder; * 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. + * All methods take "now" as a parameter so the user of this class can + * control the timeline used. */ class nsChannelStatistics { public: + typedef mozilla::TimeStamp TimeStamp; + typedef mozilla::TimeDuration TimeDuration; + nsChannelStatistics() { Reset(); } void Reset() { - mLastStartTime = mAccumulatedTime = 0; + mLastStartTime = TimeStamp(); + mAccumulatedTime = TimeDuration(0); mAccumulatedBytes = 0; mIsStarted = PR_FALSE; } - void Start(PRIntervalTime aNow) { + void Start(TimeStamp aNow) { if (mIsStarted) return; mLastStartTime = aNow; mIsStarted = PR_TRUE; } - void Stop(PRIntervalTime aNow) { + void Stop(TimeStamp aNow) { if (!mIsStarted) return; mAccumulatedTime += aNow - mLastStartTime; @@ -97,25 +101,28 @@ public: mAccumulatedBytes += aBytes; } double GetRateAtLastStop(PRPackedBool* aReliable) { - *aReliable = mAccumulatedTime >= PR_TicksPerSecond(); - return double(mAccumulatedBytes)*PR_TicksPerSecond()/mAccumulatedTime; + double seconds = mAccumulatedTime.ToSeconds(); + *aReliable = seconds >= 1.0; + if (seconds <= 0.0) + return 0.0; + return double(mAccumulatedBytes)/seconds; } - double GetRate(PRIntervalTime aNow, PRPackedBool* aReliable) { - PRIntervalTime time = mAccumulatedTime; + double GetRate(TimeStamp aNow, PRPackedBool* aReliable) { + TimeDuration time = mAccumulatedTime; if (mIsStarted) { time += aNow - mLastStartTime; } - *aReliable = time >= PR_TicksPerSecond(); - NS_ASSERTION(time >= 0, "Time wraparound?"); - if (time <= 0) + double seconds = time.ToSeconds(); + *aReliable = seconds >= 1.0; + if (seconds <= 0.0) return 0.0; - return double(mAccumulatedBytes)*PR_TicksPerSecond()/time; + return double(mAccumulatedBytes)/seconds; } private: - PRInt64 mAccumulatedBytes; - PRIntervalTime mAccumulatedTime; - PRIntervalTime mLastStartTime; - PRPackedBool mIsStarted; + PRInt64 mAccumulatedBytes; + TimeDuration mAccumulatedTime; + TimeStamp mLastStartTime; + PRPackedBool mIsStarted; }; /* diff --git a/content/media/video/src/nsMediaDecoder.cpp b/content/media/video/src/nsMediaDecoder.cpp index e591c6e1c86..68af3b47f4e 100644 --- a/content/media/video/src/nsMediaDecoder.cpp +++ b/content/media/video/src/nsMediaDecoder.cpp @@ -68,8 +68,8 @@ nsMediaDecoder::nsMediaDecoder() : mElement(0), mRGBWidth(-1), mRGBHeight(-1), - mProgressTime(0), - mDataTime(0), + mProgressTime(), + mDataTime(), mVideoUpdateLock(nsnull), mFramerate(0.0), mSizeChanged(PR_FALSE), @@ -153,25 +153,27 @@ void nsMediaDecoder::Progress(PRBool aTimer) if (!mElement) return; - PRIntervalTime now = PR_IntervalNow(); + TimeStamp now = TimeStamp::Now(); if (!aTimer) { mDataTime = now; } - PRUint32 progressDelta = PR_IntervalToMilliseconds(now - mProgressTime); - PRUint32 networkDelta = PR_IntervalToMilliseconds(now - mDataTime); - // If PROGRESS_MS has passed since the last progress event fired and more // data has arrived since then, fire another progress event. - if (progressDelta >= PROGRESS_MS && networkDelta <= PROGRESS_MS) { + if ((mProgressTime.IsNull() || + now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) && + !mDataTime.IsNull() && + now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) { mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress")); mProgressTime = now; } - if (mDataTime != 0 && networkDelta >= STALL_MS) { + if (!mDataTime.IsNull() && + now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) { mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("stalled")); - mDataTime = 0; + // Null it out + mDataTime = TimeStamp(); } } diff --git a/content/media/video/src/nsMediaStream.cpp b/content/media/video/src/nsMediaStream.cpp index 1f076844fc7..c5be46e6ff5 100644 --- a/content/media/video/src/nsMediaStream.cpp +++ b/content/media/video/src/nsMediaStream.cpp @@ -60,6 +60,8 @@ #define HTTP_OK_CODE 200 #define HTTP_PARTIAL_RESPONSE_CODE 206 +using mozilla::TimeStamp; + nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) : nsMediaStream(aDecoder, aChannel, aURI), @@ -207,7 +209,7 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest) { nsAutoLock lock(mLock); - mChannelStatistics.Start(PR_IntervalNow()); + mChannelStatistics.Start(TimeStamp::Now()); } if (mSuspendCount > 0) { @@ -229,7 +231,7 @@ nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { nsAutoLock lock(mLock); - mChannelStatistics.Stop(PR_IntervalNow()); + mChannelStatistics.Stop(TimeStamp::Now()); } mCacheStream.NotifyDataEnded(aStatus); @@ -376,7 +378,7 @@ void nsMediaChannelStream::CloseChannel() { nsAutoLock lock(mLock); - mChannelStatistics.Stop(PR_IntervalNow()); + mChannelStatistics.Stop(TimeStamp::Now()); } if (mListener) { @@ -429,7 +431,7 @@ void nsMediaChannelStream::Suspend() if (mSuspendCount == 0 && mChannel) { { nsAutoLock lock(mLock); - mChannelStatistics.Stop(PR_IntervalNow()); + mChannelStatistics.Stop(TimeStamp::Now()); } mChannel->Suspend(); } @@ -444,7 +446,7 @@ void nsMediaChannelStream::Resume() if (mSuspendCount == 0 && mChannel) { { nsAutoLock lock(mLock); - mChannelStatistics.Start(PR_IntervalNow()); + mChannelStatistics.Start(TimeStamp::Now()); } mChannel->Resume(); // XXX need to do something fancier here because we often won't @@ -586,7 +588,7 @@ double nsMediaChannelStream::GetDownloadRate(PRPackedBool* aIsReliable) { nsAutoLock lock(mLock); - return mChannelStatistics.GetRate(PR_IntervalNow(), aIsReliable); + return mChannelStatistics.GetRate(TimeStamp::Now(), aIsReliable); } PRInt64 diff --git a/content/media/video/src/nsOggDecoder.cpp b/content/media/video/src/nsOggDecoder.cpp index 9c1cce32795..3d81b648621 100644 --- a/content/media/video/src/nsOggDecoder.cpp +++ b/content/media/video/src/nsOggDecoder.cpp @@ -53,6 +53,9 @@ #include "nsNetUtil.h" #include "nsOggDecoder.h" +using mozilla::TimeDuration; +using mozilla::TimeStamp; + /* The maximum height and width of the video. Used for sanitizing the memory allocation of the RGB buffer @@ -395,18 +398,18 @@ private: // for synchronising frames. It is reset after a seek as the mTime member // of FrameData is reset to start at 0 from the first frame after a seek. // Accessed only via the decoder thread. - PRIntervalTime mPlayStartTime; + TimeStamp mPlayStartTime; // The time that playback was most recently paused, either via // buffering or pause. This is used to compute mPauseDuration for // a/v sync adjustments. Accessed only via the decoder thread. - PRIntervalTime mPauseStartTime; + TimeStamp mPauseStartTime; // The total time that has been spent in completed pauses (via // 'pause' or buffering). This is used to adjust for these // pauses when computing a/v synchronisation. Accessed only via the // decoder thread. - PRIntervalTime mPauseDuration; + TimeDuration mPauseDuration; // PR_TRUE if the media is playing and the decoder has started // the sound and adjusted the sync time for pauses. PR_FALSE @@ -432,7 +435,7 @@ private: // Time that buffering started. Used for buffering timeout and only // accessed in the decoder thread. - PRIntervalTime mBufferingStart; + TimeStamp mBufferingStart; // Download position where we should stop buffering. Only // accessed in the decoder thread. @@ -497,8 +500,8 @@ private: nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder) : mDecoder(aDecoder), mPlayer(0), - mPlayStartTime(0), - mPauseStartTime(0), + mPlayStartTime(), + mPauseStartTime(), mPauseDuration(0), mPlaying(PR_FALSE), mCallbackPeriod(1.0), @@ -507,7 +510,7 @@ nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder) : mAudioRate(0), mAudioChannels(0), mAudioTrack(-1), - mBufferingStart(0), + mBufferingStart(), mBufferingEndOffset(0), mLastFrameTime(0), mLastFramePosition(-1), @@ -554,9 +557,17 @@ nsOggDecodeStateMachine::FrameData* nsOggDecodeStateMachine::NextFrame() if (mLastFramePosition >= 0) { NS_ASSERTION(frame->mEndStreamPosition >= mLastFramePosition, "Playback positions must not decrease without an intervening reset"); - mDecoder->mPlaybackStatistics.Start(frame->mTime*PR_TicksPerSecond()); + TimeStamp base = mPlayStartTime; + if (base.IsNull()) { + // It doesn't really matter what 'base' is, so just use 'now' if + // we haven't started playback. + base = TimeStamp::Now(); + } + mDecoder->mPlaybackStatistics.Start( + base + TimeDuration::FromMilliseconds(NS_round(frame->mTime*1000))); mDecoder->mPlaybackStatistics.AddBytes(frame->mEndStreamPosition - mLastFramePosition); - mDecoder->mPlaybackStatistics.Stop(mLastFrameTime*PR_TicksPerSecond()); + mDecoder->mPlaybackStatistics.Stop( + base + TimeDuration::FromMilliseconds(NS_round(mLastFrameTime*1000))); mDecoder->UpdatePlaybackRate(); } mLastFramePosition = frame->mEndStreamPosition; @@ -653,16 +664,17 @@ void nsOggDecodeStateMachine::PlayFrame() { if (!mDecodedFrames.IsEmpty()) { FrameData* frame = mDecodedFrames.Peek(); + TimeStamp now = TimeStamp::Now(); if (frame->mState == OGGPLAY_STREAM_JUST_SEEKED) { // After returning from a seek all mTime members of // FrameData start again from a time position of 0. // Reset the play start time. - mPlayStartTime = PR_IntervalNow(); - mPauseDuration = 0; + mPlayStartTime = now; + mPauseDuration = TimeDuration(0); frame->mState = OGGPLAY_STREAM_INITIALISED; } - double time = (PR_IntervalToMilliseconds(PR_IntervalNow()-mPlayStartTime-mPauseDuration)/1000.0); + double time = (now - mPlayStartTime - mPauseDuration).ToSeconds(); if (time >= frame->mTime) { // Audio for the current frame is played, but video for the next frame // is displayed, to account for lag from the time the audio is written @@ -786,13 +798,15 @@ void nsOggDecodeStateMachine::StartPlayback() mPlaying = PR_TRUE; // If this is the very first play, then set the initial start time - if (mPlayStartTime == 0) { - mPlayStartTime = PR_IntervalNow(); + if (mPlayStartTime.IsNull()) { + mPlayStartTime = TimeStamp::Now(); } // If we have been paused previously, then compute duration spent paused - if (mPauseStartTime != 0) { - mPauseDuration += PR_IntervalNow() - mPauseStartTime; + if (!mPauseStartTime.IsNull()) { + mPauseDuration += TimeStamp::Now() - mPauseStartTime; + // Null out mPauseStartTime + mPauseStartTime = TimeStamp(); } } @@ -801,7 +815,7 @@ void nsOggDecodeStateMachine::StopPlayback() // NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "StopPlayback() called without acquiring decoder monitor"); StopAudio(); mPlaying = PR_FALSE; - mPauseStartTime = PR_IntervalNow(); + mPauseStartTime = TimeStamp::Now(); } void nsOggDecodeStateMachine::UpdatePlaybackPosition(float aTime) @@ -1010,7 +1024,7 @@ nsresult nsOggDecodeStateMachine::Run() NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, UpdateReadyStateForData); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); - mBufferingStart = PR_IntervalNow(); + mBufferingStart = TimeStamp::Now(); PRPackedBool reliable; double playbackRate = mDecoder->ComputePlaybackRate(&reliable); mBufferingEndOffset = mDecoder->mDecoderPosition + @@ -1100,15 +1114,15 @@ nsresult nsOggDecodeStateMachine::Run() case DECODER_STATE_BUFFERING: { - PRIntervalTime now = PR_IntervalNow(); - if ((PR_IntervalToMilliseconds(now - mBufferingStart) < BUFFERING_WAIT*1000) && + TimeStamp now = TimeStamp::Now(); + if (now - mBufferingStart < TimeDuration::FromSeconds(BUFFERING_WAIT) && mDecoder->mReader->Stream()->GetCachedDataEnd(mDecoder->mDecoderPosition) < mBufferingEndOffset && !mDecoder->mReader->Stream()->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) && !mDecoder->mReader->Stream()->IsSuspendedByCache()) { LOG(PR_LOG_DEBUG, - ("In buffering: buffering data until %d bytes available or %d milliseconds", + ("In buffering: buffering data until %d bytes available or %f seconds", PRUint32(mBufferingEndOffset - mDecoder->mReader->Stream()->GetCachedDataEnd(mDecoder->mDecoderPosition)), - BUFFERING_WAIT*1000 - (PR_IntervalToMilliseconds(now - mBufferingStart)))); + BUFFERING_WAIT - (now - mBufferingStart).ToSeconds())); mon.Wait(PR_MillisecondsToInterval(1000)); if (mState == DECODER_STATE_SHUTDOWN) continue; diff --git a/content/media/video/src/nsWaveDecoder.cpp b/content/media/video/src/nsWaveDecoder.cpp index e7d285d9c40..c563f0601cf 100644 --- a/content/media/video/src/nsWaveDecoder.cpp +++ b/content/media/video/src/nsWaveDecoder.cpp @@ -51,6 +51,9 @@ #include "nsThreadUtils.h" #include "nsWaveDecoder.h" +using mozilla::TimeDuration; +using mozilla::TimeStamp; + // Maximum number of seconds to wait when buffering. #define BUFFERING_TIMEOUT 3 @@ -114,7 +117,7 @@ class nsWaveStateMachine : public nsRunnable { public: nsWaveStateMachine(nsWaveDecoder* aDecoder, - PRUint32 aBufferWaitTime, float aInitialVolume); + TimeDuration aBufferWaitTime, float aInitialVolume); ~nsWaveStateMachine(); void SetStream(nsMediaStream* aStream) { mStream = aStream; } @@ -257,12 +260,12 @@ private: // playback resumes, so it is possible for this to be null. nsAutoPtr mAudioStream; - // Maximum time in milliseconds to spend waiting for data during buffering. - PRUint32 mBufferingWait; + // Maximum time to spend waiting for data during buffering. + TimeDuration mBufferingWait; // Machine time that buffering began, used with mBufferingWait to time out // buffering. - PRIntervalTime mBufferingStart; + TimeStamp mBufferingStart; // Download position where we should stop buffering. Only accessed // in the decoder thread. @@ -334,11 +337,12 @@ private: }; nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder* aDecoder, - PRUint32 aBufferWaitTime, float aInitialVolume) + TimeDuration aBufferWaitTime, + float aInitialVolume) : mDecoder(aDecoder), mStream(nsnull), mBufferingWait(aBufferWaitTime), - mBufferingStart(0), + mBufferingStart(), mBufferingEndOffset(0), mSampleRate(0), mChannels(0), @@ -512,15 +516,15 @@ nsWaveStateMachine::Run() break; case STATE_BUFFERING: { - PRIntervalTime now = PR_IntervalNow(); - if ((PR_IntervalToMilliseconds(now - mBufferingStart) < mBufferingWait) && + TimeStamp now = TimeStamp::Now(); + if (now - mBufferingStart < mBufferingWait && mStream->GetCachedDataEnd(mPlaybackPosition) < mBufferingEndOffset && !mStream->IsDataCachedToEndOfStream(mPlaybackPosition) && !mStream->IsSuspendedByCache()) { LOG(PR_LOG_DEBUG, - ("In buffering: buffering data until %d bytes available or %d milliseconds\n", + ("In buffering: buffering data until %d bytes available or %f seconds\n", PRUint32(mBufferingEndOffset - mStream->GetCachedDataEnd(mPlaybackPosition)), - mBufferingWait - (PR_IntervalToMilliseconds(now - mBufferingStart)))); + (mBufferingWait - (now - mBufferingStart)).ToSeconds())); monitor.Wait(PR_MillisecondsToInterval(1000)); } else { ChangeState(mNextState); @@ -541,13 +545,12 @@ nsWaveStateMachine::Run() } } - PRUint32 startTime = PR_IntervalToMilliseconds(PR_IntervalNow()); - startTime -= AUDIO_BUFFER_LENGTH; - PRIntervalTime lastWakeup = PR_MillisecondsToInterval(startTime); + TimeStamp now = TimeStamp::Now(); + TimeStamp lastWakeup = now - + TimeDuration::FromMilliseconds(AUDIO_BUFFER_LENGTH); do { - PRIntervalTime now = PR_IntervalNow(); - PRInt32 sleepTime = PR_IntervalToMilliseconds(now - lastWakeup); + TimeDuration sleepTime = now - lastWakeup; lastWakeup = now; // We aim to have AUDIO_BUFFER_LENGTH milliseconds of audio @@ -556,12 +559,13 @@ nsWaveStateMachine::Run() // wake early, we only buffer sleepTime milliseconds of audio since // there is still AUDIO_BUFFER_LENGTH - sleepTime milliseconds of // audio buffered. - PRInt32 targetTime = AUDIO_BUFFER_LENGTH; + TimeDuration targetTime = + TimeDuration::FromMilliseconds(AUDIO_BUFFER_LENGTH); if (sleepTime < targetTime) { targetTime = sleepTime; } - PRInt64 len = TimeToBytes(float(targetTime) / 1000.0f); + PRInt64 len = TimeToBytes(targetTime.ToSeconds()); PRInt64 leftToPlay = GetDataLength() - (mPlaybackPosition - mWavePCMOffset); @@ -578,9 +582,9 @@ nsWaveStateMachine::Run() // we need to advance playback to free up cache space) if (mState != STATE_ENDED && available < len && !mStream->IsSuspendedByCache()) { - mBufferingStart = PR_IntervalNow(); + mBufferingStart = now; mBufferingEndOffset = mPlaybackPosition + - TimeToBytes(float(mBufferingWait) / 1000.0f); + TimeToBytes(mBufferingWait.ToSeconds()); mNextState = mState; ChangeState(STATE_BUFFERING); @@ -633,6 +637,7 @@ nsWaveStateMachine::Run() if (mState == STATE_PLAYING) { monitor.Wait(PR_MillisecondsToInterval(AUDIO_BUFFER_WAKEUP)); + now = TimeStamp::Now(); } } while (mState == STATE_PLAYING); break; @@ -1295,8 +1300,8 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr RegisterShutdownObserver(); mPlaybackStateMachine = new nsWaveStateMachine(this, - BUFFERING_TIMEOUT * 1000, - mInitialVolume); + TimeDuration::FromMilliseconds(BUFFERING_TIMEOUT), + mInitialVolume); NS_ENSURE_TRUE(mPlaybackStateMachine, NS_ERROR_OUT_OF_MEMORY); // Open the stream *after* setting mPlaybackStateMachine, to ensure