Mozilla Home
Privacy
Cookies
Legal
Bugzilla
Browse
Advanced Search
New Bug
Reports
Documentation
Log In
Log In with GitHub
or
Remember me
Browse
Advanced Search
New Bug
Reports
Documentation
Attachment 496253 Details for
Bug 580531
[patch]
Patch v2 1/3: base stats
580531-stats.patch (text/plain), 49.33 KB, created by
Chris Pearce [:cpearce (Not reading bugmail)]
(
hide
)
Description:
Patch v2 1/3: base stats
Filename:
MIME Type:
Creator:
Chris Pearce [:cpearce (Not reading bugmail)]
Size:
49.33 KB
patch
obsolete
># HG changeset patch ># User Chris Pearce <chris@pearce.org.nz>, Chris Double <chris.double@double.co.nz> ># Parent 837d7b346a647b21005963738eb49f848141e0d9 >Bug 580531 - Add playback statistics for video. r=? a=? > >diff --git a/content/html/content/public/nsHTMLVideoElement.h b/content/html/content/public/nsHTMLVideoElement.h >--- a/content/html/content/public/nsHTMLVideoElement.h >+++ b/content/html/content/public/nsHTMLVideoElement.h >@@ -78,11 +78,16 @@ public: > > // Returns the current video frame width and height. > // If there is no video frame, returns the given default size. > nsIntSize GetVideoSize(nsIntSize defaultSize); > > virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel); > > virtual nsXPCClassInfo* GetClassInfo(); >+ >+ // Dispatches an event to increment the counter of the number of frames >+ // painted. Called on the main thread by the nsVideoFrame when a new image >+ // is painted. >+ void NotifyPaintedFrame(); > }; > > #endif >diff --git a/content/html/content/src/nsHTMLVideoElement.cpp b/content/html/content/src/nsHTMLVideoElement.cpp >--- a/content/html/content/src/nsHTMLVideoElement.cpp >+++ b/content/html/content/src/nsHTMLVideoElement.cpp >@@ -178,8 +178,53 @@ nsresult nsHTMLVideoElement::SetAcceptHe > "audio/*;q=0.6,*/*;q=0.5"); > > return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), > value, > PR_FALSE); > } > > NS_IMPL_URI_ATTR(nsHTMLVideoElement, Poster, poster) >+ >+/* readonly attribute unsigned long mozDecodedFrames; */ >+NS_IMETHODIMP nsHTMLVideoElement::GetMozParsedFrames(PRUint32 *aMozDecodedFrames) >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ *aMozDecodedFrames = mDecoder ? mDecoder->GetParsedFrames() : 0; >+ return NS_OK; >+} >+ >+/* readonly attribute unsigned long mozDroppedFrames; */ >+NS_IMETHODIMP nsHTMLVideoElement::GetMozDecodedFrames(PRUint32 *aMozDroppedFrames) >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ *aMozDroppedFrames = mDecoder ? mDecoder->GetDecodedFrames() : 0; >+ return NS_OK; >+} >+ >+NS_IMETHODIMP nsHTMLVideoElement::GetMozPresentedFrames(PRUint32 *aMozDroppedFrames) >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ *aMozDroppedFrames = mDecoder ? mDecoder->GetPresentedFrames() : 0; >+ return NS_OK; >+} >+ >+NS_IMETHODIMP nsHTMLVideoElement::GetMozPaintedFrames(PRUint32 *aMozDroppedFrames) >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ *aMozDroppedFrames = mDecoder ? mDecoder->GetPaintedFrames() : 0; >+ return NS_OK; >+} >+ >+NS_IMETHODIMP nsHTMLVideoElement::GetMozFrameDelay(float *aMozFrameDelay) >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ *aMozFrameDelay = mImageContainer ? mImageContainer->GetPaintDelay().ToSeconds() : 0; >+ return NS_OK; >+} >+ >+void nsHTMLVideoElement::NotifyPaintedFrame() >+{ >+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); >+ if (mDecoder) { >+ mDecoder->NotifyPaintedFrame(); >+ } >+} >diff --git a/content/media/nsBuiltinDecoderReader.cpp b/content/media/nsBuiltinDecoderReader.cpp >--- a/content/media/nsBuiltinDecoderReader.cpp >+++ b/content/media/nsBuiltinDecoderReader.cpp >@@ -320,17 +320,18 @@ nsresult nsBuiltinDecoderReader::DecodeT > { > // Decode forward to the target frame. Start with video, if we have it. > if (HasVideo()) { > PRBool eof = PR_FALSE; > PRInt64 startTime = -1; > while (HasVideo() && !eof) { > while (mVideoQueue.GetSize() == 0 && !eof) { > PRBool skip = PR_FALSE; >- eof = !DecodeVideoFrame(skip, 0); >+ PRUint32 parsed=0, decoded=0; >+ eof = !DecodeVideoFrame(skip, 0, parsed, decoded); > { > MonitorAutoExit exitReaderMon(mMonitor); > MonitorAutoEnter decoderMon(mDecoder->GetMonitor()); > if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) { > return NS_ERROR_FAILURE; > } > } > } >diff --git a/content/media/nsBuiltinDecoderReader.h b/content/media/nsBuiltinDecoderReader.h >--- a/content/media/nsBuiltinDecoderReader.h >+++ b/content/media/nsBuiltinDecoderReader.h >@@ -444,18 +444,20 @@ public: > // in mAudioQueue. Returns PR_TRUE when there's more audio to decode, > // PR_FALSE if the audio is finished, end of file has been reached, > // or an un-recoverable read error has occured. > virtual PRBool DecodeAudioData() = 0; > > // Reads and decodes one video frame. Packets with a timestamp less > // than aTimeThreshold will be decoded (unless they're not keyframes > // and aKeyframeSkip is PR_TRUE), but will not be added to the queue. >- virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold) = 0; >+ virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded) = 0; > > virtual PRBool HasAudio() = 0; > virtual PRBool HasVideo() = 0; > > // Read header data for all bitstreams in the file. Fills mInfo with > // the data required to present the media. Returns NS_OK on success, > // or NS_ERROR_FAILURE on failure. > virtual nsresult ReadMetadata() = 0; >@@ -515,17 +517,20 @@ protected: > template<class Data> > Data* DecodeToFirstData(DecodeFn aDecodeFn, > MediaQueue<Data>& aQueue); > > // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from > // DecodeToFirstData(). > PRBool DecodeVideoFrame() { > PRBool f = PR_FALSE; >- return DecodeVideoFrame(f, 0); >+ // Ignore these, DecodeVideoFrame() is only called during seeking, so >+ // these aren't relevant to performance. >+ PRUint32 parsed=0, decoded=0; >+ return DecodeVideoFrame(f, 0, parsed, decoded); > } > > // Fills aRanges with ByteRanges denoting the sections of the media which > // have been downloaded and are stored in the media cache. The reader > // monitor must must be held with exactly one lock count. The nsMediaStream > // must be pinned while calling this. > nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges); > >diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp >--- a/content/media/nsBuiltinDecoderStateMachine.cpp >+++ b/content/media/nsBuiltinDecoderStateMachine.cpp >@@ -292,36 +292,43 @@ void nsBuiltinDecoderStateMachine::Decod > ((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) || > (!videoPump && videoQueueSize < LOW_VIDEO_FRAMES))) > { > skipToNextKeyframe = PR_TRUE; > LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe")); > } > > // Video decode. >+ PRUint32 parsed = 0, decoded = 0; > if (videoPlaying && !videoWait) { > // Time the video decode, so that if it's slow, we can increase our low > // audio threshold to reduce the chance of an audio underrun while we're > // waiting for a video decode to complete. > TimeStamp start = TimeStamp::Now(); >- videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime); >+ videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, >+ currentTime, >+ parsed, >+ decoded); > TimeDuration decodeTime = TimeStamp::Now() - start; > if (!decodeCloseToDownload && > THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold) > { > lowAudioThreshold = > NS_MIN(static_cast<PRInt64>(THRESHOLD_FACTOR * decodeTime.ToMilliseconds()), > static_cast<PRInt64>(AMPLE_AUDIO_MS)); > ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold, > ampleAudioThreshold); > LOG(PR_LOG_DEBUG, > ("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld", > lowAudioThreshold, ampleAudioThreshold)); > } > } >+ if (parsed || decoded) { >+ mDecoder->NotifyDecodedFrames(parsed, decoded); >+ } > { > MonitorAutoEnter mon(mDecoder->GetMonitor()); > mDecoder->GetMonitor().NotifyAll(); > } > > // Audio decode. > if (audioPlaying && !audioWait) { > audioPlaying = mReader->DecodeAudioData(); >@@ -922,17 +929,17 @@ nsresult nsBuiltinDecoderStateMachine::R > LoadMetadata(); > if (mState == DECODER_STATE_SHUTDOWN) { > continue; > } > > VideoData* videoData = FindStartTime(); > if (videoData) { > MonitorAutoExit exitMon(mDecoder->GetMonitor()); >- RenderVideoFrame(videoData); >+ RenderVideoFrame(videoData, TimeStamp::Now()); > } > > // Start the decode threads, so that we can pre buffer the streams. > // and calculate the start time in order to determine the duration. > if (NS_FAILED(StartDecodeThreads())) { > continue; > } > >@@ -1051,23 +1058,23 @@ nsresult nsBuiltinDecoderStateMachine::R > } > if (NS_SUCCEEDED(res)){ > SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull; > NS_ASSERTION(!audio || (audio->mTime <= seekTime && > seekTime <= audio->mTime + audio->mDuration), > "Seek target should lie inside the first audio block after seek"); > PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime; > mAudioStartTime = startTime; >- mPlayDuration = TimeDuration::FromMilliseconds(startTime); >+ mPlayDuration = TimeDuration::FromMilliseconds(startTime - mStartTime); > if (HasVideo()) { > nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront()); > if (video) { > NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime, > "Seek target should lie inside the first frame after seek"); >- RenderVideoFrame(video); >+ RenderVideoFrame(video, TimeStamp::Now()); > mReader->mVideoQueue.PopFront(); > } > } > } > } > mDecoder->StartProgressUpdates(); > if (mState == DECODER_STATE_SHUTDOWN) > continue; >@@ -1193,28 +1200,31 @@ nsresult nsBuiltinDecoderStateMachine::R > } > break; > } > } > > return NS_OK; > } > >-void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData) >+void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData, TimeStamp aTarget) > { > NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); > > if (aData->mDuplicate) { > return; > } > > nsRefPtr<Image> image = aData->mImage; > if (image) { > const nsVideoInfo& info = mReader->GetInfo(); >- mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height), info.mPixelAspectRatio, image); >+ mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height), >+ info.mPixelAspectRatio, >+ image, >+ aTarget); > } > } > > PRInt64 > nsBuiltinDecoderStateMachine::GetAudioClock() > { > NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread."); > if (!mAudioStream || !HasAudio()) >@@ -1249,26 +1259,28 @@ void nsBuiltinDecoderStateMachine::Advan > // the end of the audio, use the audio clock. However if we've finished > // audio, or don't have audio, use the system clock. > PRInt64 clock_time = -1; > PRInt64 audio_time = GetAudioClock(); > if (HasAudio() && !mAudioCompleted && audio_time != -1) { > clock_time = audio_time; > // Resync against the audio clock, while we're trusting the > // audio clock. This ensures no "drift", particularly on Linux. >- mPlayStartTime = TimeStamp::Now() - TimeDuration::FromMilliseconds(clock_time); >+ mPlayDuration = TimeDuration::FromMilliseconds(clock_time); >+ mPlayStartTime = TimeStamp::Now(); > } else { > // Sound is disabled on this system. Sync to the system clock. > TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration; > clock_time = (PRInt64)(1000 * t.ToSeconds()); > // Ensure the clock can never go backwards. > NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards"); > clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime; > } > >+ // Skip video frames up to the current playback position. > NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time."); > nsAutoPtr<VideoData> videoData; > if (mReader->mVideoQueue.GetSize() > 0) { > VideoData* data = mReader->mVideoQueue.PeekFront(); > while (clock_time >= data->mTime) { > mVideoFrameEndTime = data->mEndTime; > videoData = data; > mReader->mVideoQueue.PopFront(); >@@ -1276,24 +1288,31 @@ void nsBuiltinDecoderStateMachine::Advan > if (mReader->mVideoQueue.GetSize() == 0) > break; > data = mReader->mVideoQueue.PeekFront(); > } > } > > PRInt64 frameDuration = AUDIO_DURATION_MS; > if (videoData) { >- // Decode one frame and display it >- NS_ASSERTION(videoData->mTime >= mStartTime, "Should have positive frame time"); >+ // Display the next frame. >+ NS_ASSERTION(videoData->mTime >= mStartTime, >+ "Should have positive frame time"); >+ >+ // Calculate the system-clock time at which the frame should start being displayed. >+ TimeStamp presTime = mPlayStartTime - mPlayDuration + >+ TimeDuration::FromMilliseconds(videoData->mTime - mStartTime); >+ >+ NS_ASSERTION(presTime <= TimeStamp::Now(), >+ "Frame must start before or at now"); > { > MonitorAutoExit exitMon(mDecoder->GetMonitor()); >- // If we have video, we want to increment the clock in steps of the frame >- // duration. >- RenderVideoFrame(videoData); >+ RenderVideoFrame(videoData, presTime); > } >+ mDecoder->NotifyPresentedFrame(); > frameDuration = videoData->mEndTime - videoData->mTime; > videoData = nsnull; > } > > // Kick the decode thread in case it filled its buffers and put itself > // to sleep. > mDecoder->GetMonitor().NotifyAll(); > >diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h >--- a/content/media/nsBuiltinDecoderStateMachine.h >+++ b/content/media/nsBuiltinDecoderStateMachine.h >@@ -295,17 +295,17 @@ protected: > > // Finds the end time of the last frame of data in the file, storing the value > // in mEndTime if successful. The decoder must be held with exactly one lock > // count. Called on the state machine thread. > void FindEndTime(); > > // Performs YCbCr to RGB conversion, and pushes the image down the > // rendering pipeline. Called on the state machine thread. >- void RenderVideoFrame(VideoData* aData); >+ void RenderVideoFrame(VideoData* aData, TimeStamp aTarget); > > // If we have video, display a video frame if it's time for display has > // arrived, otherwise sleep until it's time for the next sample. Update > // the current frame time as appropriate, and trigger ready state update. > // The decoder monitor must be held with exactly one lock count. Called > // on the state machine thread. > void AdvanceFrame(); > >diff --git a/content/media/nsMediaDecoder.cpp b/content/media/nsMediaDecoder.cpp >--- a/content/media/nsMediaDecoder.cpp >+++ b/content/media/nsMediaDecoder.cpp >@@ -70,16 +70,21 @@ > // nsMediaDecoder::CanPlayThrough() calculation more stable in the case of > // fluctuating bitrates. > #define CAN_PLAY_THROUGH_MARGIN 20 > > nsMediaDecoder::nsMediaDecoder() : > mElement(0), > mRGBWidth(-1), > mRGBHeight(-1), >+ mStatsMonitor("nsMediaDecoder.stats"), >+ mParsedFrames(0), >+ mDecodedFrames(0), >+ mPresentedFrames(0), >+ mPaintedFrames(0), > mVideoUpdateLock(nsnull), > mPixelAspectRatio(1.0), > mFrameBufferLength(0), > mPinnedForSeek(PR_FALSE), > mSizeChanged(PR_FALSE), > mShuttingDown(PR_FALSE) > { > MOZ_COUNT_CTOR(nsMediaDecoder); >@@ -240,29 +245,31 @@ void nsMediaDecoder::FireTimeUpdate() > { > if (!mElement) > return; > mElement->FireTimeUpdate(PR_TRUE); > } > > void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize, > float aPixelAspectRatio, >- Image* aImage) >+ Image* aImage, >+ TimeStamp aTarget) > { > nsAutoLock lock(mVideoUpdateLock); > > if (mRGBWidth != aSize.width || mRGBHeight != aSize.height || > mPixelAspectRatio != aPixelAspectRatio) { > mRGBWidth = aSize.width; > mRGBHeight = aSize.height; > mPixelAspectRatio = aPixelAspectRatio; > mSizeChanged = PR_TRUE; > } > if (mImageContainer && aImage) { > mImageContainer->SetCurrentImage(aImage); >+ mImageContainer->SetPaintTarget(aTarget); > } > } > > void nsMediaDecoder::PinForSeek() > { > nsMediaStream* stream = GetCurrentStream(); > if (!stream || mPinnedForSeek) { > return; >@@ -310,8 +317,36 @@ PRBool nsMediaDecoder::CanPlayThrough() > // playback position, so that if the bitrate of the media fluctuates, or if > // our download rate or decode rate estimation is otherwise inaccurate, > // we don't suddenly discover that we need to buffer. This is particularly > // required near the start of the media, when not much data is downloaded. > PRInt64 readAheadMargin = stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN; > return stats.mTotalBytes == stats.mDownloadPosition || > stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin; > } >+ >+void nsMediaDecoder::NotifyPaintedFrame() { >+ mozilla::MonitorAutoEnter mon(mStatsMonitor); >+ ++mPaintedFrames; >+} >+ >+void nsMediaDecoder::NotifyPresentedFrame() { >+ mozilla::MonitorAutoEnter mon(mStatsMonitor); >+ ++mPresentedFrames; >+} >+ >+void nsMediaDecoder::NotifyDecodedFrames(PRUint32 aParsed, PRUint32 aDecoded) { >+ mozilla::MonitorAutoEnter mon(mStatsMonitor); >+ mParsedFrames += aParsed; >+ mDecodedFrames += aDecoded; >+} >+ >+#define IMPL_GET_STAT_METHOD(X) \ >+PRUint32 nsMediaDecoder::Get##X##Frames() { \ >+ mozilla::MonitorAutoEnter mon(mStatsMonitor); \ >+ return m##X##Frames; \ >+} >+ >+IMPL_GET_STAT_METHOD(Parsed); >+IMPL_GET_STAT_METHOD(Decoded); >+IMPL_GET_STAT_METHOD(Presented); >+IMPL_GET_STAT_METHOD(Painted); >+ >diff --git a/content/media/nsMediaDecoder.h b/content/media/nsMediaDecoder.h >--- a/content/media/nsMediaDecoder.h >+++ b/content/media/nsMediaDecoder.h >@@ -42,16 +42,17 @@ > > #include "nsIPrincipal.h" > #include "nsSize.h" > #include "prlog.h" > #include "gfxContext.h" > #include "gfxRect.h" > #include "nsITimer.h" > #include "ImageLayers.h" >+#include "mozilla/Monitor.h" > > class nsHTMLMediaElement; > class nsMediaStream; > class nsIStreamListener; > class nsTimeRanges; > > // The size to use for audio data frames in MozAudioAvailable events. > // This value is per channel, and is chosen to give ~43 fps of events, >@@ -82,16 +83,17 @@ private: > // which can be called from any thread. > class nsMediaDecoder : public nsIObserver > { > public: > typedef mozilla::TimeStamp TimeStamp; > typedef mozilla::TimeDuration TimeDuration; > typedef mozilla::layers::ImageContainer ImageContainer; > typedef mozilla::layers::Image Image; >+ typedef mozilla::Monitor Monitor; > > nsMediaDecoder(); > virtual ~nsMediaDecoder(); > > // Create a new decoder of the same type as this one. > virtual nsMediaDecoder* Clone() = 0; > > // Perform any initialization required for the decoder. >@@ -271,26 +273,50 @@ public: > // the element is not a video element. This can be called from any > // thread; ImageContainers can be used from any thread. > ImageContainer* GetImageContainer() { return mImageContainer; } > > // Set the video width, height, pixel aspect ratio, and current image. > // Ownership of the image is transferred to the decoder. > void SetVideoData(const gfxIntSize& aSize, > float aPixelAspectRatio, >- Image* aImage); >+ Image* aImage, >+ TimeStamp aTarget); > > // Constructs the time ranges representing what segments of the media > // are buffered and playable. > virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0; > > // Returns PR_TRUE if we can play the entire media through without stopping > // to buffer, given the current download and playback rates. > PRBool CanPlayThrough(); > >+ // Returns number of frames which have been parsed from the media. >+ // Can be called on any thread. >+ PRUint32 GetParsedFrames(); >+ >+ // Returns the number of parsed frames which have been decoded. >+ // Can be called on any thread. >+ PRUint32 GetDecodedFrames(); >+ >+ // Returns the number of decoded frames which have been sent to the rendering >+ // pipeline for painting ("presented"). >+ // Can be called on any thread. >+ PRUint32 GetPresentedFrames(); >+ >+ // Returns the number of presented frames which ended up being painted. >+ // Can be called on any thread. >+ PRUint32 GetPaintedFrames(); >+ >+ // Playback statistics gathering functions. Called when frames reach various >+ // stages through the decode/rendering pipeline. Can be called on any thread. >+ void NotifyDecodedFrames(PRUint32 aParsed, PRUint32 aDecoded); >+ void NotifyPresentedFrame(); >+ void NotifyPaintedFrame(); >+ > protected: > > // Start timer to update download progress information. > nsresult StartProgress(); > > // Stop progress information timer. > nsresult StopProgress(); > >@@ -307,16 +333,25 @@ protected: > // This should only ever be accessed from the main thread. > // It is set in Init and cleared in Shutdown when the element goes away. > // The decoder does not add a reference the element. > nsHTMLMediaElement* mElement; > > PRInt32 mRGBWidth; > PRInt32 mRGBHeight; > >+ // Monitor to protect access of playback statistics. >+ Monitor mStatsMonitor; >+ >+ // Playback statistics counters. Access protected by mStatsMonitor; >+ PRUint32 mParsedFrames; >+ PRUint32 mDecodedFrames; >+ PRUint32 mPresentedFrames; >+ PRUint32 mPaintedFrames; >+ > nsRefPtr<ImageContainer> mImageContainer; > > // Time that the last progress event was fired. Read/Write from the > // main thread only. > 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 >diff --git a/content/media/ogg/nsOggReader.cpp b/content/media/ogg/nsOggReader.cpp >--- a/content/media/ogg/nsOggReader.cpp >+++ b/content/media/ogg/nsOggReader.cpp >@@ -282,17 +282,17 @@ nsresult nsOggReader::ReadMetadata() > > // Initialize the first Theora and Vorbis bitstreams. According to the > // Theora spec these can be considered the 'primary' bitstreams for playback. > // Extract the metadata needed from these streams. > // Set a default callback period for if we have no video data > if (mTheoraState && mTheoraState->Init()) { > gfxIntSize sz(mTheoraState->mInfo.pic_width, > mTheoraState->mInfo.pic_height); >- mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull); >+ mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull, TimeStamp::Now()); > } > if (mVorbisState) { > mVorbisState->Init(); > } > > if (!HasAudio() && !HasVideo() && mSkeletonState) { > // We have a skeleton track, but no audio or video, may as well disable > // the skeleton, we can't do anything useful with this media. >@@ -559,19 +559,22 @@ nsresult nsOggReader::DecodeTheora(nsTAr > } > if (!aFrames.AppendElement(v)) { > delete v; > } > } > return NS_OK; > } > >-PRBool nsOggReader::DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold) >+PRBool nsOggReader::DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded) > { >+ aParsed = aDecoded = 0; > MonitorAutoEnter mon(mMonitor); > NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), > "Should be on state machine or AV thread."); > // We chose to keep track of the Theora granulepos ourselves, rather than > // rely on th_decode_packetin() to do it for us. This is because > // th_decode_packetin() simply works by incrementing a counter every time > // it's called, so if we drop frames and don't call it, subsequent granulepos > // will be wrong. Whenever we read a packet which has a granulepos, we use >@@ -591,16 +594,17 @@ PRBool nsOggReader::DecodeVideoFrame(PRB > // Failed to read another page, must be the end of file. We can't have > // already encountered an end of bitstream packet, else we wouldn't be > // here, so this bitstream must be missing its end of stream packet, or > // is otherwise corrupt (oggz-chop can output files like this). Inform > // the queue that there will be no more frames. > mVideoQueue.Finish(); > return PR_FALSE; > } >+ aParsed++; > > if (packet.granulepos > 0) { > // We've found a packet with a granulepos, we can now determine the > // buffered packet's timestamps, as well as the timestamps for any > // packets we read subsequently. > mTheoraGranulepos = packet.granulepos; > } > >@@ -677,16 +681,17 @@ PRBool nsOggReader::DecodeVideoFrame(PRB > NS_ASSERTION(mTheoraGranulepos > 0, "We must Theora granulepos!"); > > if (!ReadOggPacket(mTheoraState, &packet)) { > // Failed to read from file, so EOF or other premature failure. > // Inform the queue that there will be no more frames. > mVideoQueue.Finish(); > return PR_FALSE; > } >+ aParsed++; > > endOfStream = packet.e_o_s != 0; > > // Maintain the Theora granulepos. We must do this even if we drop frames, > // otherwise our clock will be wrong after we've skipped frames. > if (packet.granulepos != -1) { > // Incoming packet has a granulepos, use that as it's granulepos. > mTheoraGranulepos = packet.granulepos; >@@ -729,16 +734,17 @@ PRBool nsOggReader::DecodeVideoFrame(PRB > for (PRUint32 i = 0; i < frames.Length(); i++) { > nsAutoPtr<VideoData> data(frames[i].forget()); > if (aKeyframeSkip && data->mKeyframe) { > aKeyframeSkip = PR_FALSE; > } > > if (!aKeyframeSkip) { > mVideoQueue.Push(data.forget()); >+ aDecoded++; > } > } > > if (endOfStream) { > // We've encountered an end of bitstream packet. Inform the queue that > // there will be no more frames. > mVideoQueue.Finish(); > } >@@ -1102,17 +1108,18 @@ nsresult nsOggReader::SeekInBufferedRang > return res; > } > > // We have an active Theora bitstream. Decode the next Theora frame, and > // extract its keyframe's time. > PRBool eof; > do { > PRBool skip = PR_FALSE; >- eof = !DecodeVideoFrame(skip, 0); >+ PRUint32 parsed, decoded; >+ eof = !DecodeVideoFrame(skip, 0, parsed, decoded); > { > MonitorAutoExit exitReaderMon(mMonitor); > MonitorAutoEnter decoderMon(mDecoder->GetMonitor()); > if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) { > return NS_ERROR_FAILURE; > } > } > } while (!eof && >diff --git a/content/media/ogg/nsOggReader.h b/content/media/ogg/nsOggReader.h >--- a/content/media/ogg/nsOggReader.h >+++ b/content/media/ogg/nsOggReader.h >@@ -63,18 +63,20 @@ public: > > virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor); > virtual nsresult ResetDecode(); > virtual PRBool DecodeAudioData(); > > // If the Theora granulepos has not been captured, it may read several packets > // until one with a granulepos has been captured, to ensure that all packets > // read have valid time info. >- virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold); >+ virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded); > > virtual VideoData* FindStartTime(PRInt64 aOffset, > PRInt64& aOutStartTime); > > // Get the end time of aEndOffset. This is the playback position we'd reach > // after playback finished at aEndOffset. > virtual PRInt64 FindEndTime(PRInt64 aEndOffset); > >diff --git a/content/media/raw/nsRawReader.cpp b/content/media/raw/nsRawReader.cpp >--- a/content/media/raw/nsRawReader.cpp >+++ b/content/media/raw/nsRawReader.cpp >@@ -163,18 +163,21 @@ PRBool nsRawReader::ReadFromStream(nsMed > aLength -= bytesRead; > aBuf += bytesRead; > } > > return PR_TRUE; > } > > PRBool nsRawReader::DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold) >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded) > { >+ aParsed = aDecoded = 0; > mozilla::MonitorAutoEnter autoEnter(mMonitor); > NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), > "Should be on state machine thread or decode thread."); > > if (!mFrameSize) > return PR_FALSE; // Metadata read failed. We should refuse to play. > > PRInt64 currentFrameTime = 1000 * mCurrentFrame / mFrameRate; >@@ -194,16 +197,18 @@ PRBool nsRawReader::DecodeVideoFrame(PRB > !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) { > return PR_FALSE; > } > > if (!ReadFromStream(stream, buffer, length)) { > return PR_FALSE; > } > >+ aParsed++; >+ > if (currentFrameTime >= aTimeThreshold) > break; > > mCurrentFrame++; > currentFrameTime += 1000.0 / mFrameRate; > } > > VideoData::YCbCrBuffer b; >@@ -232,16 +237,17 @@ PRBool nsRawReader::DecodeVideoFrame(PRB > b, > 1, // In raw video every frame is a keyframe > -1); > if (!v) > return PR_FALSE; > > mVideoQueue.Push(v); > mCurrentFrame++; >+ aDecoded++; > currentFrameTime += 1000 / mFrameRate; > > return PR_TRUE; > } > > nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime) > { > mozilla::MonitorAutoEnter autoEnter(mMonitor); >@@ -264,17 +270,18 @@ nsresult nsRawReader::Seek(PRInt64 aTime > > nsresult rv = stream->Seek(nsISeekableStream::NS_SEEK_SET, offset); > NS_ENSURE_SUCCESS(rv, rv); > > mVideoQueue.Erase(); > > while(mVideoQueue.GetSize() == 0) { > PRBool keyframeSkip = PR_FALSE; >- if (!DecodeVideoFrame(keyframeSkip, 0)) { >+ PRUint32 parsed, decoded; >+ if (!DecodeVideoFrame(keyframeSkip, 0, parsed, decoded)) { > mCurrentFrame = frame; > return NS_ERROR_FAILURE; > } > > { > mozilla::MonitorAutoExit autoMonitorExit(mMonitor); > mozilla::MonitorAutoEnter autoMonitor(mDecoder->GetMonitor()); > if (mDecoder->GetDecodeState() == >diff --git a/content/media/raw/nsRawReader.h b/content/media/raw/nsRawReader.h >--- a/content/media/raw/nsRawReader.h >+++ b/content/media/raw/nsRawReader.h >@@ -92,18 +92,20 @@ class nsRawReader : public nsBuiltinDeco > public: > nsRawReader(nsBuiltinDecoder* aDecoder); > ~nsRawReader(); > > virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor); > virtual nsresult ResetDecode(); > virtual PRBool DecodeAudioData(); > >- virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold); >+ virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded); > > virtual PRBool HasAudio() > { > return PR_FALSE; > } > > virtual PRBool HasVideo() > { >diff --git a/content/media/webm/nsWebMReader.cpp b/content/media/webm/nsWebMReader.cpp >--- a/content/media/webm/nsWebMReader.cpp >+++ b/content/media/webm/nsWebMReader.cpp >@@ -538,19 +538,22 @@ PRBool nsWebMReader::DecodeAudioData() > if (!holder) { > mAudioQueue.Finish(); > return PR_FALSE; > } > > return DecodeAudioPacket(holder->mPacket, holder->mOffset); > } > >-PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold) >+PRBool nsWebMReader::DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded) > { >+ aParsed = aDecoded = 0; > MonitorAutoEnter mon(mMonitor); > NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), > "Should be on state machine or decode thread."); > > nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO)); > if (!holder) { > mVideoQueue.Finish(); > return PR_FALSE; >@@ -611,38 +614,40 @@ PRBool nsWebMReader::DecodeVideoFrame(PR > } > > vpx_codec_stream_info_t si; > memset(&si, 0, sizeof(si)); > si.sz = sizeof(si); > vpx_codec_peek_stream_info(&vpx_codec_vp8_dx_algo, data, length, &si); > if ((aKeyframeSkip && !si.is_kf) || (aKeyframeSkip && si.is_kf && tstamp_ms < aTimeThreshold)) { > aKeyframeSkip = PR_TRUE; >+ aParsed++; // Assume 1 frame per chunk. > break; > } > > if (aKeyframeSkip && si.is_kf) { > aKeyframeSkip = PR_FALSE; > } > >- if(vpx_codec_decode(&mVP8, data, length, NULL, 0)) { >+ if (vpx_codec_decode(&mVP8, data, length, NULL, 0)) { > return PR_FALSE; > } > > // If the timestamp of the video frame is less than > // the time threshold required then it is not added > // to the video queue and won't be displayed. > if (tstamp_ms < aTimeThreshold) { >+ aParsed++; // Assume 1 frame per chunk. > continue; > } > > vpx_codec_iter_t iter = NULL; > vpx_image_t *img; > >- while((img = vpx_codec_get_frame(&mVP8, &iter))) { >+ while ((img = vpx_codec_get_frame(&mVP8, &iter))) { > NS_ASSERTION(mInfo.mPicture.width == static_cast<PRInt32>(img->d_w), > "WebM picture width from header does not match decoded frame"); > NS_ASSERTION(mInfo.mPicture.height == static_cast<PRInt32>(img->d_h), > "WebM picture height from header does not match decoded frame"); > NS_ASSERTION(img->fmt == IMG_FMT_I420, "WebM image format is not I420"); > > // Chroma shifts are rounded down as per the decoding examples in the VP8 SDK > VideoData::YCbCrBuffer b; >@@ -667,16 +672,20 @@ PRBool nsWebMReader::DecodeVideoFrame(PR > tstamp_ms, > next_tstamp / NS_PER_MS, > b, > si.is_kf, > -1); > if (!v) { > return PR_FALSE; > } >+ aParsed++; >+ aDecoded++; >+ NS_ASSERTION(aDecoded <= aParsed, >+ "Expect only 1 frame per chunk per packet in WebM..."); > mVideoQueue.Push(v); > } > } > > return PR_TRUE; > } > > nsresult nsWebMReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime, >diff --git a/content/media/webm/nsWebMReader.h b/content/media/webm/nsWebMReader.h >--- a/content/media/webm/nsWebMReader.h >+++ b/content/media/webm/nsWebMReader.h >@@ -133,18 +133,20 @@ public: > > virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor); > virtual nsresult ResetDecode(); > virtual PRBool DecodeAudioData(); > > // If the Theora granulepos has not been captured, it may read several packets > // until one with a granulepos has been captured, to ensure that all packets > // read have valid time info. >- virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip, >- PRInt64 aTimeThreshold); >+ virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip, >+ PRInt64 aTimeThreshold, >+ PRUint32& aParsed, >+ PRUint32& aDecoded); > > virtual PRBool HasAudio() > { > mozilla::MonitorAutoEnter mon(mMonitor); > return mHasAudio; > } > > virtual PRBool HasVideo() >diff --git a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl >--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl >+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl >@@ -43,18 +43,37 @@ > * <video> element. > * > * For more information on this interface, please see > * http://www.whatwg.org/specs/web-apps/current-work/#video > * > * @status UNDER_DEVELOPMENT > */ > >-[scriptable, uuid(edf468dc-42eb-4494-920b-56a315172640)] >+[scriptable, uuid(e1f52aa5-9962-4019-b7b3-af3aee6e4d48)] > interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement > { > attribute long width; > attribute long height; > readonly attribute unsigned long videoWidth; > readonly attribute unsigned long videoHeight; > attribute DOMString poster; >+ >+ // A count of the number of video frames that have demuxed from the media >+ // resource. If we were playing perfectly, we'd be able to paint this many >+ // frames. >+ readonly attribute unsigned long mozParsedFrames; >+ >+ // A count of the number of frames that have been decoded. We may drop >+ // frames if the decode is taking too much time. >+ readonly attribute unsigned long mozDecodedFrames; >+ >+ // A count of the number of frames that have been presented to the rendering >+ // pipeline. We may drop frames if they arrive late at the renderer. >+ readonly attribute unsigned long mozPresentedFrames; >+ >+ // Number of presented frames which were drawn on screen. >+ readonly attribute unsigned long mozPaintedFrames; >+ >+ // Time which the last painted video frame was late by, in seconds. >+ readonly attribute float mozFrameDelay; > }; > >diff --git a/gfx/layers/ImageLayers.h b/gfx/layers/ImageLayers.h >--- a/gfx/layers/ImageLayers.h >+++ b/gfx/layers/ImageLayers.h >@@ -34,19 +34,21 @@ > * the terms of any one of the MPL, the GPL or the LGPL. > * > * ***** END LICENSE BLOCK ***** */ > > #ifndef GFX_IMAGELAYER_H > #define GFX_IMAGELAYER_H > > #include "Layers.h" >+#include "mozilla/Monitor.h" > > #include "gfxPattern.h" > #include "nsThreadUtils.h" >+#include "mozilla/TimeStamp.h" > > namespace mozilla { > namespace layers { > > /** > * A class representing a buffer of pixel data. The data can be in one > * of various formats including YCbCr. > * >@@ -108,18 +110,20 @@ protected: > * we need a separate class here is that ImageLayers aren't threadsafe > * (because layers can only be used on the main thread) and we want to > * be able to set the current Image from any thread, to facilitate > * video playback without involving the main thread, for example. > */ > class THEBES_API ImageContainer { > THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer) > >+ typedef mozilla::Monitor Monitor; >+ > public: >- ImageContainer() {} >+ ImageContainer() : mTimeMonitor("ImageContainer"), mImagePainted(PR_FALSE) {} > virtual ~ImageContainer() {} > > /** > * Create an Image in one of the given formats. > * Picks the "best" format from the list and creates an Image of that > * format. > * Returns null if this backend does not support any of the formats. > */ >@@ -182,20 +186,66 @@ public: > > /** > * Sets a size that the image is expected to be rendered at. > * This is a hint for image backends to optimize scaling. > * Default implementation in this class is to ignore the hint. > */ > virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { } > >+ /** >+ * Returns the duration which the paint was late by, if a paint target >+ * was specified. >+ */ >+ TimeDuration GetPaintDelay(); >+ >+ /** >+ * Notifies the ImageLayer that it's been painted, so it can calculate >+ * the delay between the target paint time, and the achieved paint time. >+ */ >+ void NotifyPainted(TimeStamp aPaintTime); >+ >+ /** >+ * Sets the time at which we'd like the contained to be image painted. >+ */ >+ void SetPaintTarget(TimeStamp aTime); >+ >+ /** >+ * Returns the target time at which we'd like the contained image >+ * to be painted, as previously set by SetPaintTarget(). >+ */ >+ TimeStamp GetPaintTarget(); >+ > protected: > LayerManager* mManager; > >- ImageContainer(LayerManager* aManager) : mManager(aManager) {} >+ /** >+ * Protects acces to mTargetTime, mDelay, and mImagePainted. >+ */ >+ Monitor mTimeMonitor; >+ >+ /** >+ * Time at which we aim to paint the image. Set by SetPaintTarget(). This >+ * is typically set on video frames in the video decoder. >+ */ >+ TimeStamp mTargetTime; >+ >+ /** >+ * Duration by which the paint was late by. This is only valid if a target >+ * paint time was specified, and if the image has actually been painted. >+ */ >+ TimeDuration mDelay; >+ >+ /** >+ * Set to PR_TRUE if the currently contained image has been painted at >+ * least once. >+ */ >+ PRBool mImagePainted; >+ >+ ImageContainer(LayerManager* aManager) : mManager(aManager), mTimeMonitor("ImageContainer") {} > }; > > /** > * A Layer which renders an Image. > */ > class THEBES_API ImageLayer : public Layer { > public: > /** >diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp >--- a/gfx/layers/Layers.cpp >+++ b/gfx/layers/Layers.cpp >@@ -180,16 +180,37 @@ namespace layers { > already_AddRefed<gfxASurface> > LayerManager::CreateOptimalSurface(const gfxIntSize &aSize, > gfxASurface::gfxImageFormat aFormat) > { > return gfxPlatform::GetPlatform()-> > CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat)); > } > >+static void NotifyPainted(Layer* aLayer, TimeStamp aTimeStamp) { >+ NS_ASSERTION(aLayer, "Must have specified a non-null layer"); >+ NS_ASSERTION(!aTimeStamp.IsNull(), "Must have a valid timestamp"); >+ if (aLayer->GetType() == Layer::TYPE_IMAGE) { >+ ImageLayer* imgLayer = static_cast<ImageLayer*>(aLayer); >+ ImageContainer* container = imgLayer->GetContainer(); >+ container->NotifyPainted(aTimeStamp); >+ } >+ Layer* child = aLayer->GetFirstChild(); >+ for (; child; child = child->GetNextSibling()) { >+ NotifyPainted(child, aTimeStamp); >+ } >+} >+ >+void >+LayerManager::NotifyPainted() >+{ >+ if (mRoot) >+ ::NotifyPainted(mRoot, TimeStamp::Now()); >+} >+ > //-------------------------------------------------- > // Layer > > PRBool > Layer::CanUseOpaqueSurface() > { > // If the visible content in the layer is opaque, there is no need > // for an alpha channel. >@@ -644,10 +665,38 @@ LayerManager::PrintInfo(nsACString& aTo, > > /*static*/ void LayerManager::InitLog() {} > /*static*/ bool LayerManager::IsLogEnabled() { return false; } > > #endif // MOZ_LAYERS_HAVE_LOG > > PRLogModuleInfo* LayerManager::sLog; > >+TimeDuration ImageContainer::GetPaintDelay() { >+ MonitorAutoEnter mon(mTimeMonitor); >+ return mDelay; >+} >+ >+void ImageContainer::NotifyPainted(TimeStamp aPaintTime) { >+ MonitorAutoEnter mon(mTimeMonitor); >+ if (mImagePainted || mTargetTime.IsNull() || mTargetTime > aPaintTime) >+ return; >+ mDelay = aPaintTime - mTargetTime; >+ // Remember that we've painted this image, so that we won't recalculate >+ // and assume a larger delay if we paint this image again. >+ mImagePainted = PR_TRUE; >+} >+ >+TimeStamp ImageContainer::GetPaintTarget() { >+ MonitorAutoEnter mon(mTimeMonitor); >+ return mTargetTime; >+} >+ >+void ImageContainer::SetPaintTarget(TimeStamp aTime) { >+ MonitorAutoEnter mon(mTimeMonitor); >+ mTargetTime = aTime; >+ // Reset our "has been painted" flag, so we know to recalculate the paint >+ // delay the first time this frame is painted. >+ mImagePainted = PR_FALSE; >+} >+ > } // namespace layers > } // namespace mozilla >diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h >--- a/gfx/layers/Layers.h >+++ b/gfx/layers/Layers.h >@@ -418,16 +418,23 @@ public: > * Log information about just this layer manager itself to the NSPR > * log (if enabled for "Layers"). > */ > void LogSelf(const char* aPrefix=""); > > static bool IsLogEnabled(); > static PRLogModuleInfo* GetLog() { return sLog; } > >+ /** >+ * Notifies all ImageLayers in the layer tree that they've been painted, so >+ * that they can record paint-delay statistics. Call this at the end of every >+ * EndTransaction() implementation. >+ */ >+ void NotifyPainted(); >+ > protected: > nsRefPtr<Layer> mRoot; > LayerUserDataSet mUserData; > PRPackedBool mDestroyed; > PRPackedBool mSnapEffectiveTransforms; > > // Print interesting information about this into aTo. Internally > // used to implement Dump*() and Log*(). >diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp >--- a/gfx/layers/basic/BasicLayers.cpp >+++ b/gfx/layers/basic/BasicLayers.cpp >@@ -1230,16 +1230,18 @@ BasicLayerManager::EndTransaction(DrawTh > if (useDoubleBuffering) { > finalTarget->SetOperator(gfxContext::OPERATOR_SOURCE); > PopGroupWithCachedSurface(finalTarget, cachedSurfaceOffset); > } > > mTarget = nsnull; > } > >+ NotifyPainted(); >+ > #ifdef MOZ_LAYERS_HAVE_LOG > Log(); > MOZ_LAYERS_LOG(("]----- EndTransaction")); > #endif > > #ifdef DEBUG > mPhase = PHASE_NONE; > #endif >@@ -2580,16 +2582,18 @@ BasicShadowLayerManager::EndTransaction( > default: > NS_RUNTIMEABORT("not reached"); > } > } > } else if (HasShadowManager()) { > NS_WARNING("failed to forward Layers transaction"); > } > >+ NotifyPainted(); >+ > #ifdef DEBUG > mPhase = PHASE_NONE; > #endif > > // this may result in Layers being deleted, which results in > // PLayer::Send__delete__() and DeallocShmem() > mKeepAlive.Clear(); > } >diff --git a/gfx/layers/d3d10/LayerManagerD3D10.cpp b/gfx/layers/d3d10/LayerManagerD3D10.cpp >--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp >+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp >@@ -240,16 +240,17 @@ LayerManagerD3D10::EndTransaction(DrawTh > // The results of our drawing always go directly into a pixel buffer, > // so we don't need to pass any global transform here. > mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); > > Render(); > mCurrentCallbackInfo.Callback = nsnull; > mCurrentCallbackInfo.CallbackData = nsnull; > mTarget = nsnull; >+ NotifyPainted(); > } > > already_AddRefed<ThebesLayer> > LayerManagerD3D10::CreateThebesLayer() > { > nsRefPtr<ThebesLayer> layer = new ThebesLayerD3D10(this); > return layer.forget(); > } >diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp >--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp >+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp >@@ -161,16 +161,18 @@ LayerManagerD3D9::EndTransaction(DrawThe > mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); > > Render(); > /* Clean this out for sanity */ > mCurrentCallbackInfo.Callback = NULL; > mCurrentCallbackInfo.CallbackData = NULL; > // Clear mTarget, next transaction could have no target > mTarget = NULL; >+ >+ NotifyPainted(); > } > > void > LayerManagerD3D9::SetRoot(Layer *aLayer) > { > mRoot = aLayer; > } > >diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp >--- a/gfx/layers/opengl/LayerManagerOGL.cpp >+++ b/gfx/layers/opengl/LayerManagerOGL.cpp >@@ -416,16 +416,18 @@ LayerManagerOGL::EndTransaction(DrawTheb > Render(); > } > > mThebesLayerCallback = nsnull; > mThebesLayerCallbackData = nsnull; > > mTarget = NULL; > >+ NotifyPainted(); >+ > #ifdef MOZ_LAYERS_HAVE_LOG > Log(); > MOZ_LAYERS_LOG(("]----- EndTransaction")); > #endif > } > > already_AddRefed<ThebesLayer> > LayerManagerOGL::CreateThebesLayer() >diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp >--- a/layout/generic/nsVideoFrame.cpp >+++ b/layout/generic/nsVideoFrame.cpp >@@ -266,16 +266,29 @@ nsVideoFrame::BuildLayer(nsDisplayListBu > > layer->SetContainer(container); > layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); > // Set a transform on the layer to draw the video in the right place > gfxMatrix transform; > transform.Translate(r.pos); > transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height); > layer->SetTransform(gfx3DMatrix::From2D(transform)); >+ >+ if (HasVideoElement()) { >+ TimeStamp target = container->GetPaintTarget(); >+ if (!target.IsNull() && >+ (mLastPaintedTarget.IsNull() || target != mLastPaintedTarget)) >+ { >+ // This is the first time we've painted this frame, count it. >+ mLastPaintedTarget = target; >+ nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent()); >+ element->NotifyPaintedFrame(); >+ } >+ } >+ > nsRefPtr<Layer> result = layer.forget(); > return result.forget(); > } > > NS_IMETHODIMP > nsVideoFrame::Reflow(nsPresContext* aPresContext, > nsHTMLReflowMetrics& aMetrics, > const nsHTMLReflowState& aReflowState, >diff --git a/layout/generic/nsVideoFrame.h b/layout/generic/nsVideoFrame.h >--- a/layout/generic/nsVideoFrame.h >+++ b/layout/generic/nsVideoFrame.h >@@ -56,16 +56,17 @@ class nsDisplayItem; > > nsIFrame* NS_NewVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); > > class nsVideoFrame : public nsContainerFrame, public nsIAnonymousContentCreator > { > public: > typedef mozilla::layers::Layer Layer; > typedef mozilla::layers::LayerManager LayerManager; >+ typedef mozilla::TimeStamp TimeStamp; > > nsVideoFrame(nsStyleContext* aContext); > > NS_DECL_QUERYFRAME > NS_DECL_FRAMEARENA_HELPERS > > NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, > const nsRect& aDirtyRect, >@@ -141,11 +142,14 @@ protected: > > nsMargin mBorderPadding; > > // Anonymous child which is bound via XBL to the video controls. > nsCOMPtr<nsIContent> mVideoControls; > > // Anonymous child which is the image element of the poster frame. > nsCOMPtr<nsIContent> mPosterImage; >+ >+ // Target timestamp of the last frame we painted. >+ TimeStamp mLastPaintedTarget; > }; > > #endif /* nsVideoFrame_h___ */
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Actions:
View
|
Diff
|
Review
Attachments on
bug 580531
:
458935
|
458937
|
465555
|
465556
|
468611
|
468612
|
468617
|
495942
|
496253
|
496254
|
496258
|
514667
|
515506
|
515799
|
516082
|
516083
|
516105
|
516139
|
516418
|
516419