Bug 1525156 - part2 : Add Telemetry to know how many autoplay media, which has been resumed from blocked, played exactly 7 secocnds or more, or less than 7 seconds. r=cpearce,janerik
authorAlastor Wu <alwu@mozilla.com>
Tue, 26 Feb 2019 04:44:19 +0000
changeset 519109 0a003a54ff4554cb8b68f3f0d54a6bd3ef166c16
parent 519108 adb554a755e5c3e3c4280d6de251e470aff7223c
child 519110 abfb5e0d627cd2b0d8d9e5f819689b514df6df00
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, janerik
bugs1525156
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1525156 - part2 : Add Telemetry to know how many autoplay media, which has been resumed from blocked, played exactly 7 secocnds or more, or less than 7 seconds. r=cpearce,janerik By adding the Telemetry to measure the number of video/audio which played exactly 7 seconds or more, or less than 7 seconds, after those media has been resumed from blocked state, we can know how many media would meet the Chrome's MEI condition, which could help us to know more about the whole landscape of autoplay media. In addition, it could help us know how many media are played 'by users intention' because we assume that users are more likely to stop the media if autoplay media is unblocked by accident. Differential Revision: https://phabricator.services.mozilla.com/D18628
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
toolkit/components/telemetry/Histograms.json
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1712,16 +1712,63 @@ void HTMLMediaElement::ShutdownDecoder()
         mDecoder->GetNextOutputStreamTrackID();
   }
   mDecoder->Shutdown();
   DDUNLINKCHILD(mDecoder.get());
   mDecoder = nullptr;
   ReportAudioTrackSilenceProportionTelemetry();
 }
 
+void HTMLMediaElement::ReportPlayedTimeAfterBlockedTelemetry() {
+  if (!mHasPlayEverBeenBlocked) {
+    return;
+  }
+  mHasPlayEverBeenBlocked = false;
+
+  const double playTimeThreshold = 7.0;
+  const double playTimeAfterBlocked = mCurrentLoadPlayTime.Total();
+  if (playTimeAfterBlocked <= 0.0) {
+    return;
+  }
+
+  const bool isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd =
+      Duration() < playTimeThreshold && Ended();
+  LOG(LogLevel::Debug, ("%p PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED=%f, isVideo=%d",
+                        this, playTimeAfterBlocked, IsVideo()));
+  if (IsVideo() && playTimeAfterBlocked >= playTimeThreshold) {
+    AccumulateCategorical(
+        mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+            VPlayedMoreThan7s);
+  } else if (IsVideo() && playTimeAfterBlocked < playTimeThreshold) {
+    if (isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd) {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              VPlayedToTheEnd);
+    } else {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              VPlayedLessThan7s);
+    }
+  } else if (!IsVideo() && playTimeAfterBlocked >= playTimeThreshold) {
+    AccumulateCategorical(
+        mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+            APlayedMoreThan7s);
+  } else if (!IsVideo() && playTimeAfterBlocked < playTimeThreshold) {
+    if (isDurationLessThanTimeThresholdAndMediaPlayedToTheEnd) {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              APlayedToTheEnd);
+    } else {
+      AccumulateCategorical(
+          mozilla::Telemetry::LABELS_MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED::
+              APlayedLessThan7s);
+    }
+  }
+}
+
 void HTMLMediaElement::AbortExistingLoads() {
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
 
   // Set a new load ID. This will cause events which were enqueued
   // with a different load ID to silently be cancelled.
   mCurrentLoadID++;
 
@@ -1833,16 +1880,17 @@ void HTMLMediaElement::AbortExistingLoad
   mIsRunningSelectResource = false;
 
   if (mTextTrackManager) {
     mTextTrackManager->NotifyReset();
   }
 
   mEventDeliveryPaused = false;
   mPendingEvents.Clear();
+  mCurrentLoadPlayTime.Reset();
 
   AssertReadyStateIsNothing();
 }
 
 void HTMLMediaElement::NoSupportedMediaSourceError(
     const nsACString& aErrorDetails) {
   if (mDecoder) {
     ShutdownDecoder();
@@ -3524,16 +3572,17 @@ HTMLMediaElement::~HTMLMediaElement() {
   }
 
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->Shutdown();
     mAudioChannelWrapper = nullptr;
   }
 
   WakeLockRelease();
+  ReportPlayedTimeAfterBlockedTelemetry();
 
   DecoderDoctorLogger::LogDestruction(this);
 }
 
 void HTMLMediaElement::StopSuspendingAfterFirstFrame() {
   mAllowSuspendAfterFirstFrame = false;
   if (!mSuspendedAfterFirstFrame) return;
   mSuspendedAfterFirstFrame = false;
@@ -3665,16 +3714,17 @@ void HTMLMediaElement::DispatchEventsWhe
 #if defined(MOZ_WIDGET_ANDROID)
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       this, NS_LITERAL_STRING("MozAutoplayMediaBlocked"), CanBubble::eYes,
       ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 #endif
   OwnerDoc()->MaybeNotifyAutoplayBlocked();
   ReportToConsole(nsIScriptError::warningFlag, "BlockAutoplayError");
+  mHasPlayEverBeenBlocked = true;
 }
 
 void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) {
   if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE) {
     // The media load algorithm will be initiated by a user interaction.
     // We want to boost the channel priority for better responsiveness.
     // Note this must be done before UpdatePreloadAction() which will
     // update |mPreloadAction|.
@@ -5722,26 +5772,35 @@ void HTMLMediaElement::DispatchAsyncEven
   } else {
     event = new nsAsyncEventRunner(aName, this);
   }
 
   mMainThreadEventTarget->Dispatch(event.forget());
 
   if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) {
     mPlayTime.Start();
+    mCurrentLoadPlayTime.Start();
     if (IsHidden()) {
       HiddenVideoStart();
     }
   } else if (aName.EqualsLiteral("waiting")) {
     mPlayTime.Pause();
+    mCurrentLoadPlayTime.Pause();
     HiddenVideoStop();
   } else if (aName.EqualsLiteral("pause")) {
     mPlayTime.Pause();
+    mCurrentLoadPlayTime.Pause();
     HiddenVideoStop();
   }
+
+  // It would happen when (1) media aborts current load (2) media pauses (3)
+  // media end (4) media unbind from tree (because we would pause it)
+  if (aName.EqualsLiteral("pause")) {
+    ReportPlayedTimeAfterBlockedTelemetry();
+  }
 }
 
 nsresult HTMLMediaElement::DispatchPendingMediaEvents() {
   NS_ASSERTION(!mEventDeliveryPaused,
                "Must not be in bfcache when dispatching pending media events");
 
   uint32_t count = mPendingEvents.Length();
   for (uint32_t i = 0; i < count; ++i) {
@@ -5866,16 +5925,17 @@ void HTMLMediaElement::SuspendOrResumeEl
       ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d", this,
        aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
 
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
+      mCurrentLoadPlayTime.Pause();
       ReportTelemetry();
 
       // For EME content, we may force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
@@ -5883,16 +5943,19 @@ void HTMLMediaElement::SuspendOrResumeEl
         mMediaKeys->GetKeySystem(keySystem);
       }
       if (mDecoder) {
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
+      if (!mPaused) {
+        mCurrentLoadPlayTime.Start();
+      }
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1664,17 +1664,17 @@ class HTMLMediaElement : public nsGeneri
   bool mBlockedAsWithoutMetadata = false;
 
   // This promise is used to notify MediaElementAudioSourceNode that media
   // element is allowed to play when MediaElement is used as a source for web
   // audio.
   MozPromiseHolder<GenericNonExclusivePromise> mAllowedToPlayPromise;
 
  public:
-  // Helper class to measure times for MSE telemetry stats
+  // Helper class to measure times for playback telemetry stats
   class TimeDurationAccumulator {
    public:
     TimeDurationAccumulator() : mCount(0) {}
     void Start() {
       if (IsStarted()) {
         return;
       }
       mStartTime = TimeStamp::Now();
@@ -1697,16 +1697,21 @@ class HTMLMediaElement : public nsGeneri
     }
     uint32_t Count() const {
       if (!IsStarted()) {
         return mCount;
       }
       // Count current run in this report, without increasing the stored count.
       return mCount + 1;
     }
+    void Reset() {
+      mStartTime = TimeStamp();
+      mSum = TimeDuration();
+      mCount = 0;
+    }
 
    private:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 
  private:
@@ -1728,16 +1733,28 @@ class HTMLMediaElement : public nsGeneri
   TimeDurationAccumulator mPlayTime;
 
   // Total time a video has spent playing while hidden.
   TimeDurationAccumulator mHiddenPlayTime;
 
   // Total time a video has (or would have) spent in video-decode-suspend mode.
   TimeDurationAccumulator mVideoDecodeSuspendTime;
 
+  // Total time a video has spent playing on the current load, it would be reset
+  // when media aborts the current load; be paused when the docuemt enters the
+  // bf-cache and be resumed when the docuemt leaves the bf-cache.
+  TimeDurationAccumulator mCurrentLoadPlayTime;
+
+  // True if media has ever been blocked by autoplay policy before.
+  bool mHasPlayEverBeenBlocked = false;
+
+  // Report the Telemetry about whether media played over the specific time
+  // threshold.
+  void ReportPlayedTimeAfterBlockedTelemetry();
+
   // True if user has called load(), seek() or element has started playing
   // before. It's *only* use for checking autoplay policy
   bool mIsBlessed = false;
 
   // True if the first frame has been successfully loaded.
   bool mFirstFrameLoaded = false;
 
   // Media elements also have a default playback start position, which must
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -14382,16 +14382,30 @@
     ],
     "expires_in_version": "71",
     "kind": "categorical",
     "labels": ["AddAllow", "RemoveAllow", "AddBlock", "RemoveBlock"],
     "bug_numbers": [1520361],
     "description": "The number of times the user adds a site to the allow list, removes a site from the allow list, adds a site to the block list and removes a site from the block list.",
     "releaseChannelCollection": "opt-out"
   },
+  "MEDIA_PLAYED_TIME_AFTER_AUTOPLAY_BLOCKED": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": [
+      "alwu@mozilla.com",
+      "cpearce@mozilla.com",
+      "nohlmeier@mozilla.com"
+    ],
+    "expires_in_version": "72",
+    "kind": "categorical",
+    "labels": ["VPlayedMoreThan7s", "VPlayedLessThan7s", "VPlayedToTheEnd", "APlayedMoreThan7s", "APlayedLessThan7s", "APlayedToTheEnd"],
+    "bug_numbers": [1525156],
+    "description": "The number of times autoplay media (audio and video), which has been blocked by autoplay policy, played exactly 7 seconds or more, or less than 7 seconds after it has been resumed from the blocked state. If media's duration is less than 7 seconds and it played to the end before user pauses it, it would be in another category which is different from 'play >= 7 seconds' and 'play < 7 seconds'.",
+    "releaseChannelCollection": "opt-out"
+  },
   "QM_REPOSITORIES_INITIALIZATION_TIME": {
     "record_in_processes": ["main"],
     "expires_in_version": "72",
     "bug_numbers": [1481716, 1523682],
     "kind": "exponential",
     "high": 30000,
     "n_buckets": 30,
     "releaseChannelCollection": "opt-out",