Bug 1127646 - Report MSE Join Latency and MTBR in telemetry - r=cpearce,bsmedberg a=sledru
authorChris Double <chris.double@double.co.nz>
Tue, 24 Feb 2015 16:11:43 +1300
changeset 248378 d496848bee5f96e4dca5a422f77ab04e0222de26
parent 248377 1fd8ef835f7196ecbf80b451e91ab6c11e409f38
child 248379 f80361d0ef392f60f9752fd95999043e080b9edf
push id7828
push userrgiles@mozilla.com
push dateWed, 25 Mar 2015 18:32:38 +0000
treeherdermozilla-aurora@d496848bee5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, bsmedberg, sledru
bugs1127646
milestone38.0a2
Bug 1127646 - Report MSE Join Latency and MTBR in telemetry - r=cpearce,bsmedberg a=sledru
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
@@ -1212,16 +1212,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
@@ -2578,16 +2581,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();
   }
@@ -3657,16 +3671,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
@@ -7284,16 +7284,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"],