Bug 1510340 - p2: limit the concurrently created HLSDecoder instances. r=jya
authorJohn Lin <jolin@mozilla.com>
Thu, 21 Feb 2019 01:22:02 +0000
changeset 519412 63b009fbe2bce5aa33df21754f2dcd5fd7e4aa4a
parent 519411 da7bf617583897d5618d81d9c2594ecdc789a87c
child 519413 32c7dc092efda4c745b4b1fe3590249d0d0513df
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)
reviewersjya
bugs1510340
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 1510340 - p2: limit the concurrently created HLSDecoder instances. r=jya Each instance has an instance of Java ExoPlayer that consumes memory in the limited JVM heap. Too many concurrent players will cause OutOfMemoryError. Differential Revision: https://phabricator.services.mozilla.com/D20420
dom/html/HTMLMediaElement.cpp
dom/media/hls/HLSDecoder.cpp
dom/media/hls/HLSDecoder.h
modules/libpref/init/StaticPrefList.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4473,17 +4473,21 @@ nsresult HTMLMediaElement::InitializeDec
   MediaDecoderInit decoderInit(
       this, mMuted ? 0.0 : mVolume, mPreservesPitch,
       ClampPlaybackRate(mPlaybackRate),
       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA, mHasSuspendTaint,
       HasAttr(kNameSpaceID_None, nsGkAtoms::loop), *containerType);
 
 #ifdef MOZ_ANDROID_HLS_SUPPORT
   if (HLSDecoder::IsSupportedType(*containerType)) {
-    RefPtr<HLSDecoder> decoder = new HLSDecoder(decoderInit);
+    RefPtr<HLSDecoder> decoder = HLSDecoder::Create(decoderInit);
+    if (!decoder) {
+      reportCanPlay(false);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
     reportCanPlay(true);
     return SetupDecoder(decoder.get(), aChannel);
   }
 #endif
 
   RefPtr<ChannelMediaDecoder> decoder =
       ChannelMediaDecoder::Create(decoderInit, &diagnostics);
   if (!decoder) {
--- a/dom/media/hls/HLSDecoder.cpp
+++ b/dom/media/hls/HLSDecoder.cpp
@@ -85,17 +85,39 @@ void HLSResourceCallbacksSupport::OnErro
           // Since HLS source should be from the Internet, we treat all resource
           // errors from GeckoHlsPlayer as network errors.
           self->mDecoder->NetworkError(
               MediaResult(NS_ERROR_FAILURE, "HLS error"));
         }
       }));
 }
 
-HLSDecoder::HLSDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+size_t HLSDecoder::sAllocatedInstances = 0;
+
+// static
+RefPtr<HLSDecoder> HLSDecoder::Create(MediaDecoderInit& aInit) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return sAllocatedInstances < StaticPrefs::MediaHlsMaxAllocations()
+             ? new HLSDecoder(aInit)
+             : nullptr;
+}
+
+HLSDecoder::HLSDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {
+  MOZ_ASSERT(NS_IsMainThread());
+  sAllocatedInstances++;
+  HLS_DEBUG("HLSDecoder", "HLSDecoder(): allocated=%zu", sAllocatedInstances);
+}
+
+HLSDecoder::~HLSDecoder() {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(sAllocatedInstances > 0);
+  sAllocatedInstances--;
+  HLS_DEBUG("HLSDecoder", "~HLSDecoder(): allocated=%zu", sAllocatedInstances);
+}
 
 MediaDecoderStateMachine* HLSDecoder::CreateStateMachine() {
   MOZ_ASSERT(NS_IsMainThread());
 
   MediaFormatReaderInit init;
   init.mVideoFrameContainer = GetVideoFrameContainer();
   init.mKnowsCompositor = GetCompositor();
   init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
--- a/dom/media/hls/HLSDecoder.h
+++ b/dom/media/hls/HLSDecoder.h
@@ -11,29 +11,29 @@
 #include "GeneratedJNIWrappers.h"
 
 namespace mozilla {
 
 class HLSResourceCallbacksSupport;
 
 class HLSDecoder final : public MediaDecoder {
  public:
-  // MediaDecoder interface.
-  explicit HLSDecoder(MediaDecoderInit& aInit);
+  static RefPtr<HLSDecoder> Create(MediaDecoderInit& aInit);
 
   // Returns true if the HLS backend is pref'ed on.
   static bool IsEnabled();
 
   // Returns true if aContainerType is an HLS type that we think we can render
   // with the a platform decoder backend.
   // If provided, codecs are checked for support.
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 
   nsresult Load(nsIChannel* aChannel);
 
+  // MediaDecoder interface.
   void Play() override;
 
   void Pause() override;
 
   void AddSizeOfResources(ResourceSizes* aSizes) override;
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
   bool IsTransportSeekable() override { return true; }
   void Suspend() override;
@@ -41,24 +41,28 @@ class HLSDecoder final : public MediaDec
   void Shutdown() override;
 
   // Called as data arrives on the underlying HLS player. Main thread only.
   void NotifyDataArrived();
 
  private:
   friend class HLSResourceCallbacksSupport;
 
+  explicit HLSDecoder(MediaDecoderInit& aInit);
+  ~HLSDecoder();
   MediaDecoderStateMachine* CreateStateMachine();
 
   bool CanPlayThroughImpl() final {
     // TODO: We don't know how to estimate 'canplaythrough' for this decoder.
     // For now we just return true for 'autoplay' can work.
     return true;
   }
 
+  static size_t sAllocatedInstances;  // Access only in the main thread.
+
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIURI> mURI;
   java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper;
   java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks;
   RefPtr<HLSResourceCallbacksSupport> mCallbackSupport;
 };
 
 }  // namespace mozilla
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1558,16 +1558,26 @@ VARCACHE_PREF(
 #endif
 VARCACHE_PREF(
   "media.hls.enabled",
    MediaHlsEnabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
+// Max number of HLS players that can be created concurrently. Used only on
+// Android and when "media.hls.enabled" is true.
+#ifdef ANDROID
+VARCACHE_PREF(
+  "media.hls.max-allocations",
+   MediaHlsMaxAllocations,
+  uint32_t, 20
+)
+#endif
+
 #ifdef MOZ_FMP4
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
 VARCACHE_PREF(
   "media.mp4.enabled",
    MediaMp4Enabled,