diff --git a/content/html/content/public/HTMLMediaElement.h b/content/html/content/public/HTMLMediaElement.h index d52407dc4fe..27f6320fb69 100644 --- a/content/html/content/public/HTMLMediaElement.h +++ b/content/html/content/public/HTMLMediaElement.h @@ -25,6 +25,7 @@ #include "nsIDOMWakeLock.h" #include "AudioChannelCommon.h" #include "DecoderTraits.h" +#include "MediaDecoder.h" #include "MediaMetadataManager.h" #include "AudioChannelAgent.h" #include "mozilla/Attributes.h" @@ -522,6 +523,12 @@ public: mTextTracks->AddTextTrack(aTextTrack); } + MediaDecoder::FrameStatistics& GetFrameStatistics() + { + MediaDecoder::FrameStatistics empty; + return mDecoder ? mDecoder->GetFrameStatistics() : empty; + } + protected: class MediaLoadListener; class StreamListener; diff --git a/content/html/content/public/HTMLVideoElement.h b/content/html/content/public/HTMLVideoElement.h index 944ee7a23f8..38af38aec17 100644 --- a/content/html/content/public/HTMLVideoElement.h +++ b/content/html/content/public/HTMLVideoElement.h @@ -10,6 +10,8 @@ #include "mozilla/Attributes.h" #include "nsIDOMHTMLVideoElement.h" #include "mozilla/dom/HTMLMediaElement.h" +#include "mozilla/dom/VideoPlaybackQuality.h" +#include "nsPerformance.h" namespace mozilla { namespace dom { @@ -114,6 +116,8 @@ public: void NotifyOwnerDocumentActivityChanged() MOZ_OVERRIDE; + already_AddRefed VideoPlaybackQuality(); + protected: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; diff --git a/content/html/content/src/HTMLVideoElement.cpp b/content/html/content/src/HTMLVideoElement.cpp index 840dbd78d61..7fca5cafcc4 100644 --- a/content/html/content/src/HTMLVideoElement.cpp +++ b/content/html/content/src/HTMLVideoElement.cpp @@ -258,6 +258,33 @@ HTMLVideoElement::NotifyOwnerDocumentActivityChanged() WakeLockUpdate(); } +already_AddRefed +HTMLVideoElement::VideoPlaybackQuality() +{ + nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow(); + NS_ENSURE_TRUE(window, nullptr); + nsPerformance* perf = window->GetPerformance(); + NS_ENSURE_TRUE(perf, nullptr); + DOMHighResTimeStamp creationTime = perf->GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now()); + + uint64_t totalFrames = 0; + uint64_t droppedFrames = 0; + uint64_t corruptedFrames = 0; + double playbackJitter = 0.0; + if (mDecoder && sVideoStatsEnabled) { + MediaDecoder::FrameStatistics& stats = mDecoder->GetFrameStatistics(); + totalFrames = stats.GetParsedFrames(); + droppedFrames = totalFrames - stats.GetPresentedFrames(); + corruptedFrames = totalFrames - stats.GetDecodedFrames(); + playbackJitter = stats.GetPlaybackJitter(); + } + + nsRefPtr playbackQuality = + new dom::VideoPlaybackQuality(this, creationTime, totalFrames, droppedFrames, + corruptedFrames, playbackJitter); + return playbackQuality.forget(); +} + void HTMLVideoElement::WakeLockCreate() { diff --git a/content/media/MediaDecoder.h b/content/media/MediaDecoder.h index af013152a9b..533056c74af 100644 --- a/content/media/MediaDecoder.h +++ b/content/media/MediaDecoder.h @@ -818,6 +818,7 @@ public: FrameStatistics() : mReentrantMonitor("MediaDecoder::FrameStats"), + mPlaybackJitter(0.0), mParsedFrames(0), mDecodedFrames(0), mPresentedFrames(0) {} @@ -844,6 +845,11 @@ public: return mPresentedFrames; } + double GetPlaybackJitter() { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + return mPlaybackJitter; + } + // Increments the parsed and decoded frame counters by the passed in counts. // Can be called on any thread. void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) { @@ -861,11 +867,22 @@ public: ++mPresentedFrames; } + // Tracks the sum of display errors. + // Can be called on any thread. + void NotifyPlaybackJitter(double aDisplayError) { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + mPlaybackJitter += aDisplayError; + } + private: // ReentrantMonitor to protect access of playback statistics. ReentrantMonitor mReentrantMonitor; + // Sum of display duration error. + // Access protected by mStatsReentrantMonitor. + double mPlaybackJitter; + // Number of frames parsed and demuxed from media. // Access protected by mStatsReentrantMonitor. uint32_t mParsedFrames; diff --git a/content/media/MediaDecoderStateMachine.cpp b/content/media/MediaDecoderStateMachine.cpp index f2da2b86a9e..8b8a8a8208c 100644 --- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -2533,8 +2533,12 @@ void MediaDecoderStateMachine::AdvanceFrame() ScheduleStateMachine(); return; } - mDecoder->GetFrameStatistics().NotifyPresentedFrame(); + MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics(); + frameStats.NotifyPresentedFrame(); remainingTime = currentFrame->mEndTime - clock_time; + int64_t frameDuration = currentFrame->mEndTime - currentFrame->mTime; + double displayError = fabs(double(frameDuration - remainingTime) / USECS_PER_S); + frameStats.NotifyPlaybackJitter(displayError); currentFrame = nullptr; } diff --git a/content/media/VideoPlaybackQuality.cpp b/content/media/VideoPlaybackQuality.cpp new file mode 100644 index 00000000000..247f45ae4cd --- /dev/null +++ b/content/media/VideoPlaybackQuality.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VideoPlaybackQuality.h" + +#include "mozilla/dom/HTMLMediaElement.h" +#include "mozilla/dom/VideoPlaybackQualityBinding.h" +#include "nsContentUtils.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "MediaDecoder.h" + +namespace mozilla { +namespace dom { + +VideoPlaybackQuality::VideoPlaybackQuality(HTMLMediaElement* aElement, + DOMHighResTimeStamp aCreationTime, + uint64_t aTotalFrames, + uint64_t aDroppedFrames, + uint64_t aCorruptedFrames, + double aPlaybackJitter) + : mElement(aElement) + , mCreationTime(aCreationTime) + , mTotalFrames(aTotalFrames) + , mDroppedFrames(aDroppedFrames) + , mCorruptedFrames(aCorruptedFrames) + , mPlaybackJitter(aPlaybackJitter) +{ + SetIsDOMBinding(); +} + +HTMLMediaElement* +VideoPlaybackQuality::GetParentObject() const +{ + return mElement; +} + +JSObject* +VideoPlaybackQuality::WrapObject(JSContext *aCx, JS::Handle aScope) +{ + return VideoPlaybackQualityBinding::Wrap(aCx, aScope, this); +} + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VideoPlaybackQuality, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VideoPlaybackQuality, Release) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(VideoPlaybackQuality, mElement) + +} // namespace dom +} // namespace mozilla diff --git a/content/media/VideoPlaybackQuality.h b/content/media/VideoPlaybackQuality.h new file mode 100644 index 00000000000..d29db7c599b --- /dev/null +++ b/content/media/VideoPlaybackQuality.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_VideoPlaybackQuality_h_ +#define mozilla_dom_VideoPlaybackQuality_h_ + +#include "mozilla/dom/HTMLMediaElement.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDOMNavigationTiming.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace dom { + +class VideoPlaybackQuality MOZ_FINAL : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VideoPlaybackQuality) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VideoPlaybackQuality) + + VideoPlaybackQuality(HTMLMediaElement* aElement, DOMHighResTimeStamp aCreationTime, + uint64_t aTotalFrames, uint64_t aDroppedFrames, + uint64_t aCorruptedFrames, double aPlaybackJitter); + + HTMLMediaElement* GetParentObject() const; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + + DOMHighResTimeStamp CreationTime() const + { + return mCreationTime; + } + + uint64_t TotalVideoFrames() + { + return mTotalFrames; + } + + uint64_t DroppedVideoFrames() + { + return mDroppedFrames; + } + + uint64_t CorruptedVideoFrames() + { + return mCorruptedFrames; + } + + double PlaybackJitter() + { + return mPlaybackJitter; + } + +private: + nsRefPtr mElement; + DOMHighResTimeStamp mCreationTime; + uint64_t mTotalFrames; + uint64_t mDroppedFrames; + uint64_t mCorruptedFrames; + double mPlaybackJitter; +}; + +} // namespace dom +} // namespace mozilla +#endif /* mozilla_dom_VideoPlaybackQuality_h_ */ diff --git a/content/media/moz.build b/content/media/moz.build index 8e1c32d1f16..92276707dab 100644 --- a/content/media/moz.build +++ b/content/media/moz.build @@ -85,6 +85,7 @@ EXPORTS.mozilla.dom += [ 'TextTrackCue.h', 'TextTrackCueList.h', 'TextTrackList.h', + 'VideoPlaybackQuality.h', 'VideoStreamTrack.h', ] @@ -112,6 +113,7 @@ CPP_SOURCES += [ 'TextTrackCueList.cpp', 'TextTrackList.cpp', 'VideoFrameContainer.cpp', + 'VideoPlaybackQuality.cpp', 'VideoSegment.cpp', 'VideoStreamTrack.cpp', 'VideoUtils.cpp', diff --git a/dom/base/moz.build b/dom/base/moz.build index 7d8fb890b10..082d917c646 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -28,6 +28,7 @@ EXPORTS += [ 'nsDOMClassInfoClasses.h', 'nsDOMClassInfoID.h', 'nsDOMJSUtils.h', + 'nsDOMNavigationTiming.h', 'nsDOMString.h', 'nsFocusManager.h', 'nsIDOMClassInfo.h', @@ -47,6 +48,7 @@ EXPORTS += [ 'nsJSUtils.h', 'nsPIDOMWindow.h', 'nsPIWindowRoot.h', + 'nsPerformance.h', 'nsStructuredCloneContainer.h', 'nsWindowMemoryReporter.h', 'nsWrapperCache.h', diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index f1bc6c6bad5..8cb1db61a6d 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1122,6 +1122,10 @@ DOMInterfaces = { 'workers': True, }], +'VideoPlaybackQuality': { + 'nativeOwnership': 'refcounted', +}, + 'VideoStreamTrack': { }, diff --git a/dom/webidl/HTMLVideoElement.webidl b/dom/webidl/HTMLVideoElement.webidl index a6c34fb49ba..4cf565f6b06 100644 --- a/dom/webidl/HTMLVideoElement.webidl +++ b/dom/webidl/HTMLVideoElement.webidl @@ -45,3 +45,9 @@ partial interface HTMLVideoElement { // True if the video has an audio track available. readonly attribute boolean mozHasAudio; }; + +// https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#idl-def-HTMLVideoElement +partial interface HTMLVideoElement { + [Pref="media.mediasource.enabled", Creator] + readonly attribute VideoPlaybackQuality videoPlaybackQuality; +}; diff --git a/dom/webidl/VideoPlaybackQuality.webidl b/dom/webidl/VideoPlaybackQuality.webidl new file mode 100644 index 00000000000..5fb6b3e8205 --- /dev/null +++ b/dom/webidl/VideoPlaybackQuality.webidl @@ -0,0 +1,20 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +[Pref="media.mediasource.enabled"] +interface VideoPlaybackQuality { + readonly attribute unsigned long totalVideoFrames; + readonly attribute unsigned long droppedVideoFrames; + readonly attribute unsigned long corruptedVideoFrames; + readonly attribute double playbackJitter; +}; + diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 489a41c6740..657b4657b2e 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -345,6 +345,7 @@ webidl_files = \ WheelEvent.webidl \ UndoManager.webidl \ URLUtils.webidl \ + VideoPlaybackQuality.webidl \ VideoStreamTrack.webidl \ WaveShaperNode.webidl \ Window.webidl \