Bug 1367950 - Only throttle download of src=url video if the download is 'fast' on desktop. r=jwwang
authorChris Pearce <cpearce@mozilla.com>
Fri, 26 May 2017 13:55:48 +1200
changeset 409046 b597ea8f9bb4b27fcd065e37e57a58562561e1c8
parent 409045 7cc2790574dcc1ca79cbd224d8c49c94bf61337a
child 409047 50ec456995cf34eec3fc3f3c7798e9aefeeb0828
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1367950
milestone55.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 1367950 - Only throttle download of src=url video if the download is 'fast' on desktop. r=jwwang Our canplaythrough logic is opaque to the users, so I expect that our recent change to throttle when we hit the readahead limit would be confusing to users; those on a slow connection would want their media to prebuffer, and not expect the download to stop part way through. They would think that Firefox had stalled at an arbitrary point for some unknown reason, i.e., they'd think Firefox was broken. So I think we're better to instead only throttle if the network is good enough that the user probably doesn't worry about the download not keeping up with playback. We should restore the previous behaviour on mobile of throttling when the download reached the readaheadd limit regardless of canplaythrough or network speed, as the calculus is different on mobile; the user may also be concerned about battery life, or hitting their data cap. And often the faster the cellular network is, the more expensive data on it is. So this patch changes us to throttle when we reach the readahead limit only if the network is fast, where fast is defined as being able to stream at twice the rate estimated to be required to playback without stalling. It also adds a pref to revert to the old behaviour of not considering the network speed, which we enable on mobile to restore it to its previous behaviour. MozReview-Commit-ID: KLIGaQZV6dX
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
mobile/android/app/mobile.js
modules/libpref/init/all.js
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1047,24 +1047,47 @@ MediaDecoder::NotifySuspendedStatusChang
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   if (mResource) {
     bool suspended = mResource->IsSuspendedByCache();
     GetOwner()->NotifySuspendedByCache(suspended);
   }
 }
 
+bool
+MediaDecoder::ShouldThrottleDownload()
+{
+  // We throttle the download if either the throttle override pref is set
+  // (so that we can always throttle in Firefox on mobile) or if the download
+  // is fast enough that there's no concern about playback being interrupted.
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(mDecoderStateMachine, false);
+
+  if (Preferences::GetBool("media.throttle-regardless-of-download-rate",
+                           false)) {
+    return true;
+  }
+
+  MediaStatistics stats = GetStatistics();
+  if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
+    return false;
+  }
+  uint32_t factor =
+    std::max(2u, Preferences::GetUint("media.throttle-factor", 2));
+  return stats.mDownloadRate > factor * stats.mPlaybackRate;
+}
+
 void
 MediaDecoder::NotifyBytesDownloaded()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   UpdatePlaybackRate();
   GetOwner()->DownloadProgressed();
-  mResource->ThrottleReadahead(CanPlayThrough());
+  mResource->ThrottleReadahead(ShouldThrottleDownload());
 }
 
 void
 MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -637,16 +637,18 @@ protected:
 
   // Ensures our media stream has been unpinned.
   void UnpinForSeek();
 
   const char* PlayStateStr();
 
   void OnMetadataUpdate(TimedMetadata&& aMetadata);
 
+  bool ShouldThrottleDownload();
+
   // This should only ever be accessed from the main thread.
   // It is set in the constructor and cleared in Shutdown when the element goes
   // away. The decoder does not add a reference the element.
   MediaDecoderOwner* mOwner;
 
   // The AbstractThread from mOwner.
   const RefPtr<AbstractThread> mAbstractMainThread;
 
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -593,16 +593,19 @@ pref("browser.meta_refresh_when_inactive
 // prevent video elements from preloading too much data
 pref("media.preload.default", 1); // default to preload none
 pref("media.preload.auto", 2);    // preload metadata if preload=auto
 pref("media.cache_size", 32768);    // 32MB media cache
 // Try to save battery by not resuming reading from a connection until we fall
 // below 10s of buffered data.
 pref("media.cache_resume_threshold", 10);
 pref("media.cache_readahead_limit", 30);
+// On mobile we'll throttle the download once the readahead_limit is hit,
+// even if the download is slow. This is to preserve battery and data.
+pref("media.throttle-regardless-of-download-rate", true);
 
 // Number of video frames we buffer while decoding video.
 // On Android this is decided by a similar value which varies for
 // each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
 // number must be less than the OMX equivalent or gecko will think it is
 // chronically starved of video frames. All decoders seen so far have a value
 // of at least 4.
 pref("media.video-queue.default-size", 3);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -307,16 +307,28 @@ pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 30);
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.
 pref("media.cache_readahead_limit", 60);
 
+// We'll throttle the download if the download rate is throttle-factor times
+// the estimated playback rate, AND we satisfy the cache readahead_limit
+// above. The estimated playback rate is time_duration/length_in_bytes.
+// This means we'll only throttle the download if there's no concern that
+// throttling would cause us to stop and buffer.
+pref("media.throttle-factor", 2);
+// By default, we'll throttle media download once we've reached the the
+// readahead_limit if the download is fast. This pref toggles the "and the
+// download is fast" check off, so that we can always throttle the download
+// once the readaheadd limit is reached even on a slow network.
+pref("media.throttle-regardless-of-download-rate", false);
+
 // Master HTML5 media volume scale.
 pref("media.volume_scale", "1.0");
 
 // Timeout for wakelock release
 pref("media.wakelock_timeout", 2000);
 
 // Whether we should play videos opened in a "video document", i.e. videos
 // opened as top-level documents, as opposed to inside a media element.