/* 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 DOM_TelemetryProbesReporter_H_ #define DOM_TelemetryProbesReporter_H_ #include "MediaInfo.h" #include "mozilla/Maybe.h" #include "mozilla/AwakeTimeStamp.h" #include "AudioChannelService.h" #include "nsISupportsImpl.h" namespace mozilla { class FrameStatistics; class TelemetryProbesReporterOwner { public: virtual Maybe<nsAutoString> GetKeySystem() const = 0; virtual MediaInfo GetMediaInfo() const = 0; virtual FrameStatistics* GetFrameStatistics() const = 0; virtual bool IsEncrypted() const = 0; virtual void DispatchAsyncTestingEvent(const nsAString& aName) = 0; #ifdef MOZ_WMF_CDM virtual bool IsUsingWMFCDM() const = 0; #endif }; enum class MediaContent : uint8_t { MEDIA_HAS_NOTHING = (0 << 0), MEDIA_HAS_VIDEO = (1 << 0), MEDIA_HAS_AUDIO = (1 << 1), MEDIA_HAS_COLOR_DEPTH_ABOVE_8 = (1 << 2), }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MediaContent) /** * This class is used for collecting and reporting telemetry probes for * its owner which should inherit from TelemetryProbesReporterOwner. We use it * for HTMLMediaElement, and each element has one corresponding reporter. */ class TelemetryProbesReporter final { public: explicit TelemetryProbesReporter(TelemetryProbesReporterOwner* aOwner); ~TelemetryProbesReporter() = default; enum class Visibility { eInitial, eVisible, eInvisible, }; static MediaContent MediaInfoToMediaContent(const MediaInfo& aInfo); using AudibleState = dom::AudioChannelService::AudibleState; // State transitions void OnPlay(Visibility aVisibility, MediaContent aContent, bool aIsMuted); void OnPause(Visibility aVisibility); void OnShutdown(); void OnVisibilityChanged(Visibility aVisibility); void OnAudibleChanged(AudibleState aAudible); void OnMediaContentChanged(MediaContent aContent); void OnMutedChanged(bool aMuted); void OnDecodeSuspended(); void OnDecodeResumed(); void OntFirstFrameLoaded(const TimeDuration& aLoadedFirstFrameTime, bool aIsMSE, bool aIsExternalEngineStateMachine); double GetTotalVideoPlayTimeInSeconds() const; double GetTotalVideoHDRPlayTimeInSeconds() const; double GetVisibleVideoPlayTimeInSeconds() const; double GetInvisibleVideoPlayTimeInSeconds() const; double GetVideoDecodeSuspendedTimeInSeconds() const; double GetTotalAudioPlayTimeInSeconds() const; double GetInaudiblePlayTimeInSeconds() const; double GetAudiblePlayTimeInSeconds() const; double GetMutedPlayTimeInSeconds() const; private: void StartInvisibleVideoTimeAccumulator(); void PauseInvisibleVideoTimeAccumulator(); void StartInaudibleAudioTimeAccumulator(); void PauseInaudibleAudioTimeAccumulator(); void StartMutedAudioTimeAccumulator(); void PauseMutedAudioTimeAccumulator(); bool HasOwnerHadValidVideo() const; bool HasOwnerHadValidMedia() const; void AssertOnMainThreadAndNotShutdown() const; void ReportTelemetry(); void ReportResultForVideo(); void ReportResultForAudio(); void ReportResultForVideoFrameStatistics(double aTotalPlayTimeS, const nsCString& key); #ifdef MOZ_WMF_CDM void ReportResultForMFCDMPlaybackIfNeeded(double aTotalPlayTimeS, const nsCString& aResolution); #endif // Helper class to measure times for playback telemetry stats class TimeDurationAccumulator { public: TimeDurationAccumulator() = default; void Start() { if (IsStarted()) { return; } mStartTime = Some(AwakeTimeStamp::NowLoRes()); } void Pause() { if (!IsStarted()) { return; } mSum = (AwakeTimeStamp::NowLoRes() - mStartTime.value()); mStartTime = Nothing(); } bool IsStarted() const { return mStartTime.isSome(); } double GetAndClearTotal() { MOZ_ASSERT(!IsStarted(), "only call this when accumulator is paused"); double total = mSum.ToSeconds(); mStartTime = Nothing(); mSum = AwakeTimeDuration(); return total; } double PeekTotal() const { if (!IsStarted()) { return mSum.ToSeconds(); } return (AwakeTimeStamp::NowLoRes() - mStartTime.value()).ToSeconds(); } private: Maybe<AwakeTimeStamp> mStartTime; AwakeTimeDuration mSum; }; // The owner is HTMLMediaElement that is guaranteed being always alive during // our whole life cycle. TelemetryProbesReporterOwner* mOwner; // Total time an element has spent on playing video. TimeDurationAccumulator mTotalVideoPlayTime; // Total time an element has spent on playing video that has a color depth // greater than 8, which is likely HDR video. TimeDurationAccumulator mTotalVideoHDRPlayTime; // Total time an element has spent on playing audio TimeDurationAccumulator mTotalAudioPlayTime; // Total time a VIDEO element has spent playing while the corresponding media // element is invisible. TimeDurationAccumulator mInvisibleVideoPlayTime; // Total time an element has spent playing audio that was not audible TimeDurationAccumulator mInaudibleAudioPlayTime; // Total time an element with an audio track has spent muted TimeDurationAccumulator mMutedAudioPlayTime; // Total time a VIDEO has spent in video-decode-suspend mode. TimeDurationAccumulator mVideoDecodeSuspendedTime; Visibility mMediaElementVisibility = Visibility::eInitial; MediaContent mMediaContent = MediaContent::MEDIA_HAS_NOTHING; bool mIsPlaying = false; bool mIsMuted = false; }; } // namespace mozilla #endif // DOM_TelemetryProbesReporter_H_