Bug 1127646 - Report MSE Join Latency and MTBR in telemetry - r=cpearce,bsmedberg
authorChris Double <chris.double@double.co.nz>
Tue, 24 Feb 2015 16:11:43 +1300
changeset 233482 afb6a4080533fee145992de3401ed0bc5482eb80
parent 233481 37d8d0362318e45be0d842018e4689e6ac5258c9
child 233483 e7a3117249ee840dfffe7aea75e7afa0d7cc7ea5
push id28413
push usercbook@mozilla.com
push dateFri, 13 Mar 2015 13:03:26 +0000
treeherdermozilla-central@9dbb2d41bb2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, bsmedberg
bugs1127646
milestone39.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 1127646 - Report MSE Join Latency and MTBR in telemetry - r=cpearce,bsmedberg
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/VideoUtils.h
toolkit/components/telemetry/Histograms.json
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1214,16 +1214,19 @@ nsresult HTMLMediaElement::LoadResource(
       // TODO: Handle failure: run "If the media data cannot be fetched at
       // all, due to network errors, causing the user agent to give up
       // trying to fetch the resource" section of resource fetch algorithm.
       return NS_ERROR_FAILURE;
     }
     mMediaSource = source.forget();
     nsRefPtr<MediaResource> resource =
       MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
+    if (IsAutoplayEnabled()) {
+      mJoinLatency.Start();
+    }
     return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
   }
 
   nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
   if (nsContentUtils::ChannelShouldInheritPrincipal(NodePrincipal(),
                                                     mLoadingSrc,
                                                     false, // aInheritForAboutBlank
                                                     false // aForceInherit
@@ -2580,16 +2583,27 @@ HTMLMediaElement::ReportMSETelemetry()
                                        mReadyState == HTMLMediaElement::HAVE_CURRENT_DATA;
     if (stalled) {
       state = STALLED;
     }
   }
 
   Telemetry::Accumulate(Telemetry::VIDEO_MSE_UNLOAD_STATE, state);
   LOG(PR_LOG_DEBUG, ("%p VIDEO_MSE_UNLOAD_STATE = %d", this, state));
+
+  Telemetry::Accumulate(Telemetry::VIDEO_MSE_PLAY_TIME_MS, SECONDS_TO_MS(mPlayTime.Total()));
+  LOG(PR_LOG_DEBUG, ("%p VIDEO_MSE_PLAY_TIME_MS = %f", this, mPlayTime.Total()));
+
+  Telemetry::Accumulate(Telemetry::VIDEO_MSE_BUFFERING_COUNT, mRebufferTime.Count());
+  LOG(PR_LOG_DEBUG, ("%p VIDEO_MSE_BUFFERING_COUNT = %d", this, mRebufferTime.Count()));
+
+  double latency = mJoinLatency.Count() ? mJoinLatency.Total() / mJoinLatency.Count() : 0.0;
+  Telemetry::Accumulate(Telemetry::VIDEO_MSE_JOIN_LATENCY_MS, SECONDS_TO_MS(latency));
+  LOG(PR_LOG_DEBUG, ("%p VIDEO_MSE_JOIN_LATENCY = %f (%d ms) count=%d\n",
+                     this, latency, SECONDS_TO_MS(latency), mJoinLatency.Count()));
 }
 
 void HTMLMediaElement::UnbindFromTree(bool aDeep,
                                       bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     Pause();
   }
@@ -3659,16 +3673,33 @@ nsresult HTMLMediaElement::DispatchAsync
   // if the page comes out of the bfcache.
   if (mEventDeliveryPaused) {
     mPendingEvents.AppendElement(aName);
     return NS_OK;
   }
 
   nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
   NS_DispatchToMainThread(event);
+
+  // Only collect rebuffer and stall rate stats for MSE video.
+  if (!mMediaSource) {
+    return NS_OK;
+  }
+
+  if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) {
+    mPlayTime.Start();
+    mRebufferTime.Pause();
+    mJoinLatency.Pause();
+  } else if (aName.EqualsLiteral("waiting")) {
+    mPlayTime.Pause();
+    mRebufferTime.Start();
+  } else if (aName.EqualsLiteral("pause")) {
+    mPlayTime.Pause();
+  }
+
   return NS_OK;
 }
 
 nsresult HTMLMediaElement::DispatchPendingMediaEvents()
 {
   NS_ASSERTION(!mEventDeliveryPaused,
                "Must not be in bfcache when dispatching pending media events");
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1349,14 +1349,59 @@ protected:
     ELEMENT_INTREE,
     // The MediaElement is not in the DOM tree now but had been binded to the
     // tree before.
     ELEMENT_NOT_INTREE_HAD_INTREE
   };
 
   ElementInTreeState mElementInTreeState;
 
+public:
+  // Helper class to measure times for MSE telemetry stats
+  class TimeDurationAccumulator {
+  public:
+    TimeDurationAccumulator()
+      : mCount(0)
+    {
+    }
+    void Start() {
+      if (IsStarted()) {
+        return;
+      }
+      mStartTime = TimeStamp::Now();
+    }
+    void Pause() {
+      if (!IsStarted()) {
+        return;
+      }
+      mSum += (TimeStamp::Now() - mStartTime);
+      mCount++;
+      mStartTime = TimeStamp();
+    }
+    bool IsStarted() const {
+      return !mStartTime.IsNull();
+    }
+    double Total() const {
+      return mSum.ToSeconds();
+    }
+    uint32_t Count() const {
+      return mCount;
+    }
+  private:
+    TimeStamp mStartTime;
+    TimeDuration mSum;
+    uint32_t mCount;
+  };
+private:
+  // Total time an MSE video has spent playing
+  TimeDurationAccumulator mPlayTime;
+
+  // Time spent buffering in an MSE video
+  TimeDurationAccumulator mRebufferTime;
+
+  // Time spent between video load and video playback.
+  TimeDurationAccumulator mJoinLatency;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLMediaElement_h
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -143,18 +143,21 @@ CheckedInt64 FramesToUsecs(int64_t aFram
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
 
 // Number of microseconds per second. 1e6.
 static const int64_t USECS_PER_S = 1000000;
 
 // Number of microseconds per millisecond.
 static const int64_t USECS_PER_MS = 1000;
 
+// Converts milliseconds to seconds.
+#define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
+
 // Converts seconds to milliseconds.
-#define MS_TO_SECONDS(s) ((double)(s) / (PR_MSEC_PER_SEC))
+#define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
 
 // Converts from seconds to microseconds. Returns failure if the resulting
 // integer is too big to fit in an int64_t.
 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
 
 // The maximum height and width of the video. Used for
 // sanitizing the memory allocation of the RGB buffer.
 // The maximum resolution we anticipate encountering in the
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7394,16 +7394,35 @@
   },
   "DATA_STORAGE_ENTRIES": {
     "expires_in_version": "default",
     "kind": "linear",
     "high": "1024",
     "n_buckets": 16,
     "description": "The number of entries in persistent DataStorage (HSTS and HPKP data, basically)"
   },
+  "VIDEO_MSE_JOIN_LATENCY_MS" : {
+    "expires_in_version": "45",
+    "description": "Time in MS between MSE video load and playback",
+    "kind": "exponential",
+    "high": "30000",
+    "n_buckets": 50
+  },
+  "VIDEO_MSE_PLAY_TIME_MS" : {
+    "expires_in_version": "45",
+    "description": "Total time spent playing MSE video",
+    "kind": "exponential",
+    "high": "7200000",
+    "n_buckets": 100
+  },
+  "VIDEO_MSE_BUFFERING_COUNT" : {
+    "expires_in_version": "45",
+    "description": "Count of times that MSE video was buffering",
+    "kind": "count"
+  },
   "VIDEO_MSE_UNLOAD_STATE": {
     "expires_in_version": "45",
     "kind": "enumerated",
     "n_values": 5,
     "description": "MSE video state when unloading. ended = 0, paused = 1, stalled = 2, seeking = 3, other = 4"
   },
   "FX_SANITIZE_TOTAL": {
     "alert_emails": ["firefox-dev@mozilla.org", "gavin@mozilla.com"],