/* 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 "AudioChannelService.h"
#include "MediaCodecsSupport.h"
#include "MediaInfo.h"
#include "mozilla/AwakeTimeStamp.h"
#include "mozilla/DefineEnum.h"
#include "mozilla/EnumSet.h"
#include "mozilla/Maybe.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;
MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(Visibility,
(eInitial, eVisible,
eInvisible));
static MediaContent MediaInfoToMediaContent(
const MediaInfo& aInfo);
using AudibleState = dom::AudioChannelService::AudibleState;
static void ReportDeviceMediaCodecSupported(
const media::MediaCodecsSupported& aSupported);
// 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);
enum class FirstFrameLoadedFlag {
IsMSE,
IsExternalEngineStateMachine,
IsHLS,
IsHardwareDecoding,
};
using FirstFrameLoadedFlagSet = EnumSet<FirstFrameLoadedFlag, uint8_t>;
void OnFirstFrameLoaded(
const double aLoadedFirstFrameTime,
const double aLoadedMetadataTime,
const double aTotalWaitingDataTime,
const double aTotalBufferingTime,
const FirstFrameLoadedFlagSet aFlags,
const MediaInfo& aInfo,
const nsCString& aVideoDecoderName);
double GetTotalVideoPlayTimeInSeconds()
const;
double GetTotalVideoHDRPlayTimeInSeconds()
const;
double GetVisibleVideoPlayTimeInSeconds()
const;
double GetInvisibleVideoPlayTimeInSeconds()
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
void ReportPlaytimeForKeySystem(
const nsAString& aKeySystem,
const double aTotalPlayTimeS,
const nsCString& aCodec,
const nsCString& aResolution);
// 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;
Visibility mMediaElementVisibility = Visibility::eInitial;
MediaContent mMediaContent = MediaContent::MEDIA_HAS_NOTHING;
bool mIsPlaying =
false;
bool mIsMuted =
false;
};
}
// namespace mozilla
#endif // DOM_TelemetryProbesReporter_H_