Bug 1403706 - Remove race conditions in temporary blob - part 2 - MediaRecorder should use MutableBlobStorage, r=pehrsons, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 05 Oct 2017 07:41:41 +0200
changeset 384660 47479015f61072eceb32045b1d8b621728fb7d99
parent 384659 eb5ca8d115e70ff508e5d58f54f5840e9e0f81dc
child 384661 e15a074f4bacba4d41ab9ac2cf90b553281ff3b6
push id32634
push userkwierso@gmail.com
push dateFri, 06 Oct 2017 19:55:44 +0000
treeherdermozilla-central@2d7b8b5dd174 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons, smaug
bugs1403706
milestone58.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 1403706 - Remove race conditions in temporary blob - part 2 - MediaRecorder should use MutableBlobStorage, r=pehrsons, r=smaug
dom/file/MutableBlobStorage.h
dom/media/EncodedBufferCache.cpp
dom/media/EncodedBufferCache.h
dom/media/MediaRecorder.cpp
dom/media/MediaRecorder.h
dom/media/moz.build
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_MutableBlobStorage_h
 #define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
 #include "prio.h"
 
 class nsIEventTarget;
+class nsIRunnable;
 
 namespace mozilla {
 
 class TaskQueue;
 
 namespace dom {
 
 class Blob;
deleted file mode 100644
--- a/dom/media/EncodedBufferCache.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "EncodedBufferCache.h"
-#include "prio.h"
-#include "nsAnonymousTemporaryFile.h"
-#include "mozilla/Monitor.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/File.h"
-#include "nsThreadUtils.h"
-#include "nsXULAppAPI.h"
-
-namespace mozilla {
-
-void
-EncodedBufferCache::AppendBuffer(nsTArray<uint8_t> & aBuf)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-
-  MutexAutoLock lock(mMutex);
-  mDataSize += aBuf.Length();
-
-  mEncodedBuffers.AppendElement()->SwapElements(aBuf);
-
-  if (!mTempFileEnabled && mDataSize > mMaxMemoryStorage) {
-    nsresult rv;
-    PRFileDesc* tempFD = nullptr;
-    {
-      // Release the mMutex because of the sync dispatch to the main thread.
-      MutexAutoUnlock unlock(mMutex);
-      if (XRE_IsParentProcess()) {
-        // In case we are in the parent process, do a synchronous I/O here to open a
-        // temporary file.
-        rv = NS_OpenAnonymousTemporaryFile(&tempFD);
-      } else {
-        // In case we are in the child process, we don't have access to open a file
-        // directly due to sandbox restrictions, so we need to ask the parent process
-        // to do that for us.  In order to initiate the IPC, we need to first go to
-        // the main thread.  This is done by dispatching a runnable to the main thread.
-        // From there, we start an asynchronous IPC, and we block the current thread
-        // using a monitor while this async work is in progress.  When we receive the
-        // resulting file descriptor from the parent process, we notify the monitor
-        // and unblock the current thread and continue.
-        typedef dom::ContentChild::AnonymousTemporaryFileCallback
-          AnonymousTemporaryFileCallback;
-        bool done = false;
-        Monitor monitor("EncodeBufferCache::AppendBuffer");
-        RefPtr<dom::ContentChild> cc = dom::ContentChild::GetSingleton();
-        nsCOMPtr<nsIRunnable> runnable =
-          NewRunnableMethod<AnonymousTemporaryFileCallback>(
-            "dom::ContentChild::AsyncOpenAnonymousTemporaryFile",
-            cc,
-            &dom::ContentChild::AsyncOpenAnonymousTemporaryFile,
-            [&](PRFileDesc* aFile) {
-              rv = aFile ? NS_OK : NS_ERROR_FAILURE;
-              tempFD = aFile;
-              MonitorAutoLock lock(monitor);
-              done = true;
-              lock.Notify();
-            });
-        MonitorAutoLock lock(monitor);
-        rv = NS_DispatchToMainThread(runnable);
-        if (NS_SUCCEEDED(rv)) {
-          while (!done) {
-            lock.Wait();
-          }
-        }
-      }
-    }
-    if (!NS_FAILED(rv)) {
-      // Check the mDataSize again since we release the mMutex before.
-      if (mDataSize > mMaxMemoryStorage) {
-        mFD = tempFD;
-        mTempFileEnabled = true;
-      } else {
-        // Close the tempFD because the data had been taken during the
-        // MutexAutoUnlock.
-        PR_Close(tempFD);
-      }
-    }
-  }
-
-  if (mTempFileEnabled) {
-    // has created temporary file, write buffer in it
-    for (uint32_t i = 0; i < mEncodedBuffers.Length(); i++) {
-      int32_t amount = PR_Write(mFD, mEncodedBuffers.ElementAt(i).Elements(), mEncodedBuffers.ElementAt(i).Length());
-      if (amount < 0 || size_t(amount) < mEncodedBuffers.ElementAt(i).Length()) {
-        NS_WARNING("Failed to write media cache block!");
-      }
-    }
-    mEncodedBuffers.Clear();
-  }
-
-}
-
-already_AddRefed<dom::Blob>
-EncodedBufferCache::ExtractBlob(nsISupports* aParent,
-                                const nsAString &aContentType)
-{
-  MutexAutoLock lock(mMutex);
-  RefPtr<dom::Blob> blob;
-  if (mTempFileEnabled) {
-    // generate new temporary file to write
-    blob = dom::Blob::CreateTemporaryBlob(aParent, mFD, 0, mDataSize,
-                                          aContentType);
-    // fallback to memory blob
-    mTempFileEnabled = false;
-    mDataSize = 0;
-    mFD = nullptr;
-  } else {
-    void* blobData = malloc(mDataSize);
-    NS_ASSERTION(blobData, "out of memory!!");
-
-    if (blobData) {
-      for (uint32_t i = 0, offset = 0; i < mEncodedBuffers.Length(); i++) {
-        memcpy((uint8_t*)blobData + offset, mEncodedBuffers.ElementAt(i).Elements(),
-               mEncodedBuffers.ElementAt(i).Length());
-        offset += mEncodedBuffers.ElementAt(i).Length();
-      }
-      blob = dom::Blob::CreateMemoryBlob(aParent, blobData, mDataSize,
-                                         aContentType);
-      mEncodedBuffers.Clear();
-    } else
-      return nullptr;
-  }
-  mDataSize = 0;
-  return blob.forget();
-}
-
-size_t
-EncodedBufferCache::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
-{
-  MutexAutoLock lock(mMutex);
-  return mEncodedBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/EncodedBufferCache.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef EncodedBufferCache_h_
-#define EncodedBufferCache_h_
-
-#include "nsCOMPtr.h"
-#include "nsTArray.h"
-#include "mozilla/Mutex.h"
-
-struct PRFileDesc;
-
-namespace mozilla {
-
-namespace dom {
-class Blob;
-} // namespace dom
-
-/**
- * Data is moved into a temporary file when it grows beyond
- * the maximal size passed in the Init function.
- * The AppendBuffer and ExtractBlob methods are thread-safe and can be called on
- * different threads at the same time.
- */
-class EncodedBufferCache
-{
-public:
-  explicit EncodedBufferCache(uint32_t aMaxMemoryStorage)
-  : mFD(nullptr),
-    mMutex("EncodedBufferCache.Data.Mutex"),
-    mDataSize(0),
-    mMaxMemoryStorage(aMaxMemoryStorage),
-    mTempFileEnabled(false) { }
-  ~EncodedBufferCache()
-  {
-  }
-  // Append buffers in cache, check if the queue is too large then switch to write buffer to file system
-  // aBuf will append to mEncodedBuffers or temporary File, aBuf also be cleared
-  void AppendBuffer(nsTArray<uint8_t> & aBuf);
-  // Read all buffer from memory or file System, also Remove the temporary file or clean the buffers in memory.
-  already_AddRefed<dom::Blob> ExtractBlob(nsISupports* aParent, const nsAString &aContentType);
-  // Returns the heap size in bytes of our internal buffers.
-  // Note that this intentionally ignores the data in the temp file.
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
-
-private:
-  //array for storing the encoded data.
-  nsTArray<nsTArray<uint8_t> > mEncodedBuffers;
-  // File handle for the temporary file
-  PRFileDesc* mFD;
-  // Used to protect the mEncodedBuffer for avoiding AppendBuffer/Consume on different thread at the same time.
-  Mutex mMutex;
-  // the current buffer size can be read
-  uint64_t mDataSize;
-  // The maximal buffer allowed in memory
-  uint32_t mMaxMemoryStorage;
-  // indicate the buffer is stored on temporary file or not
-  bool mTempFileEnabled;
-};
-
-} // namespace mozilla
-
-#endif
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -4,26 +4,26 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaRecorder.h"
 
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
-#include "EncodedBufferCache.h"
 #include "GeckoProfiler.h"
 #include "MediaDecoder.h"
 #include "MediaEncoder.h"
 #include "MediaStreamGraphImpl.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaRecorderErrorEvent.h"
+#include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 #include "mozilla/media/MediaUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TaskQueue.h"
 #include "nsAutoPtr.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -196,43 +196,100 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder,
 class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
                               public DOMMediaStream::TrackListener
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session)
 
   // Main thread task.
   // Create a blob event and send back to client.
   class PushBlobRunnable : public Runnable
+                         , public MutableBlobStorageCallback
   {
   public:
-    explicit PushBlobRunnable(Session* aSession)
+    NS_DECL_ISUPPORTS_INHERITED
+
+    // aDestroyRunnable can be null. If it's not, it will be dispatched after
+    // the PushBlobRunnable::Run().
+    PushBlobRunnable(Session* aSession, Runnable* aDestroyRunnable)
       : Runnable("dom::MediaRecorder::Session::PushBlobRunnable")
       , mSession(aSession)
+      , mDestroyRunnable(aDestroyRunnable)
     { }
 
     NS_IMETHOD Run() override
     {
       LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
       MOZ_ASSERT(NS_IsMainThread());
 
+      mSession->GetBlobWhenReady(this);
+      return NS_OK;
+    }
+
+    void
+    BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
+                       nsresult aRv) override
+    {
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
-        return NS_OK;
+        return;
+      }
+
+      if (NS_FAILED(aRv)) {
+        recorder->NotifyError(aRv);
+        return;
+      }
+
+      nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
+      if (NS_FAILED(rv)) {
+        recorder->NotifyError(aRv);
       }
 
-      nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
-      if (NS_FAILED(rv)) {
-        recorder->NotifyError(rv);
+      if (mDestroyRunnable &&
+          NS_FAILED(NS_DispatchToMainThread(mDestroyRunnable.forget()))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
+      }
+    }
+
+  private:
+    ~PushBlobRunnable() = default;
+
+    RefPtr<Session> mSession;
+
+    // The generation of the blob is async. In order to avoid dispatching the
+    // DestroyRunnable before pushing the blob event, we store the runnable
+    // here.
+    RefPtr<Runnable> mDestroyRunnable;
+  };
+
+  class StoreEncodedBufferRunnable final : public Runnable
+  {
+    RefPtr<Session> mSession;
+    nsTArray<nsTArray<uint8_t>> mBuffer;
+
+  public:
+    StoreEncodedBufferRunnable(Session* aSession,
+                               nsTArray<nsTArray<uint8_t>>&& aBuffer)
+      : Runnable("StoreEncodedBufferRunnable")
+      , mSession(aSession)
+      , mBuffer(Move(aBuffer))
+    {}
+
+    NS_IMETHOD
+    Run() override
+    {
+      mSession->MaybeCreateMutableBlobStorage();
+      for (uint32_t i = 0; i < mBuffer.Length(); i++) {
+        if (!mBuffer[i].IsEmpty()) {
+          mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
+                                                mBuffer[i].Length());
+        }
       }
 
       return NS_OK;
     }
-
-  private:
-    RefPtr<Session> mSession;
   };
 
   // Notify encoder error, run in main thread task. (Bug 1095381)
   class EncoderErrorNotifierRunnable : public Runnable
   {
   public:
     explicit EncoderErrorNotifierRunnable(Session* aSession)
       : Runnable("dom::MediaRecorder::Session::EncoderErrorNotifierRunnable")
@@ -428,19 +485,18 @@ public:
     : mRecorder(aRecorder)
     , mTimeSlice(aTimeSlice)
     , mStopIssued(false)
     , mIsStartEventFired(false)
     , mNeedSessionEndTask(true)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
-                                           MAX_ALLOW_MEMORY_BUFFER);
-    mEncodedBufferCache = new EncodedBufferCache(maxMem);
+    mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
+                                      MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   void PrincipalChanged(MediaStreamTrack* aTrack) override
   {
     NS_ASSERTION(mMediaStreamTracks.Contains(aTrack),
                  "Principal changed for unrecorded track");
     if (!MediaStreamTracksPrincipalSubsumes()) {
@@ -546,37 +602,53 @@ public:
     return NS_OK;
   }
 
   nsresult RequestData()
   {
     LOG(LogLevel::Debug, ("Session.RequestData"));
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
       MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
-  already_AddRefed<nsIDOMBlob> GetEncodedData()
+  void
+  MaybeCreateMutableBlobStorage()
+  {
+    if (!mMutableBlobStorage) {
+      mMutableBlobStorage =
+        new MutableBlobStorage(MutableBlobStorage::eCouldBeInTemporaryFile,
+                               nullptr, mMaxMemory);
+    }
+  }
+
+  void
+  GetBlobWhenReady(MutableBlobStorageCallback* aCallback)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    return mEncodedBufferCache->ExtractBlob(mRecorder->GetParentObject(),
-                                            mMimeType);
+
+    MaybeCreateMutableBlobStorage();
+    mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
+                                          NS_ConvertUTF16toUTF8(mMimeType),
+                                          aCallback);
+    mMutableBlobStorage = nullptr;
   }
 
   RefPtr<SizeOfPromise>
   SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    size_t encodedBufferSize =
-      mEncodedBufferCache->SizeOfExcludingThis(aMallocSizeOf);
+    size_t encodedBufferSize = mMutableBlobStorage
+                                 ? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
+                                 : 0;
 
     if (!mEncoder) {
       return SizeOfPromise::CreateAndResolve(encodedBufferSize, __func__);
     }
 
     auto& encoder = mEncoder;
     return InvokeAsync(mEncoderThread, __func__,
       [encoder, encodedBufferSize, aMallocSizeOf]() {
@@ -588,21 +660,21 @@ public:
 private:
   // Only DestroyRunnable is allowed to delete Session object on main thread.
   virtual ~Session()
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mShutdownPromise);
     LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
   }
-  // Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
+  // Pull encoded media data from MediaEncoder and put into MutableBlobStorage.
   // Destroy this session object in the end of this function.
   // If the bool aForceFlush is true, we will force to dispatch a
   // PushBlobRunnable to main thread.
-  void Extract(bool aForceFlush)
+  void Extract(bool aForceFlush, Runnable* aDestroyRunnable)
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     LOG(LogLevel::Debug, ("Session.Extract %p", this));
 
     AUTO_PROFILER_LABEL("MediaRecorder::Session::Extract", OTHER);
 
     // Pull encoded media data from MediaEncoder
@@ -610,36 +682,37 @@ private:
     nsresult rv = mEncoder->GetEncodedData(&encodedBuf);
     if (NS_FAILED(rv)) {
       MOZ_RELEASE_ASSERT(encodedBuf.IsEmpty());
       // Even if we failed to encode more data, it might be time to push a blob
       // with already encoded data.
     }
 
     // Append pulled data into cache buffer.
-    for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
-      if (!encodedBuf[i].IsEmpty()) {
-        mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
-      }
-    }
+    NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
+                                                           Move(encodedBuf)));
 
     // Whether push encoded data back to onDataAvailable automatically or we
     // need a flush.
     bool pushBlob = aForceFlush;
     if (!pushBlob &&
         mTimeSlice > 0 &&
         (TimeStamp::Now()-mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
       pushBlob = true;
     }
     if (pushBlob) {
-      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, aDestroyRunnable)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       } else {
         mLastBlobTimeStamp = TimeStamp::Now();
       }
+    } else if (aDestroyRunnable) {
+      if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+      }
     }
   }
 
   void MediaStreamReady(DOMMediaStream* aStream) {
     MOZ_RELEASE_ASSERT(aStream);
 
     if (mStopIssued) {
       return;
@@ -889,83 +962,82 @@ private:
     if (NS_FAILED(rv)) {
       mRecorder->ForceInactive();
       NS_DispatchToMainThread(
         NewRunnableMethod<nsresult>("dom::MediaRecorder::NotifyError",
                                     mRecorder,
                                     &MediaRecorder::NotifyError,
                                     rv));
     }
+
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
+
     if (rv != NS_ERROR_DOM_SECURITY_ERR) {
       // Don't push a blob if there was a security error.
-      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, destroyRunnable)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       }
+    } else {
+      if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+      }
     }
-    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
-      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
-    }
+
     mNeedSessionEndTask = false;
   }
 
   void MediaEncoderInitialized()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     // Pull encoded metadata from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
     nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mMimeType);
     if (NS_FAILED(rv)) {
       MOZ_ASSERT(false);
       return;
     }
 
     // Append pulled data into cache buffer.
-    for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
-      if (!encodedBuf[i].IsEmpty()) {
-        mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
-      }
-    }
+    NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
+                                                           Move(encodedBuf)));
   }
 
   void MediaEncoderDataAvailable()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     if (!mIsStartEventFired) {
       NS_DispatchToMainThread(
         new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
       mIsStartEventFired = true;
     }
 
-    Extract(false);
+    Extract(false, nullptr);
   }
 
   void MediaEncoderError()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
     NS_DispatchToMainThread(
       NewRunnableMethod<nsresult>(
         "dom::MediaRecorder::Session::DoSessionEndTask",
         this, &Session::DoSessionEndTask, NS_ERROR_FAILURE));
   }
 
   void MediaEncoderShutdown()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
     MOZ_ASSERT(mEncoder->IsShutdown());
 
-    // Forces the last blob even if it's not time for it yet.
-    Extract(true);
+    // For the stop event. Let's the creation of the blob to dispatch this runnable.
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
 
-    // For the stop event.
-    if (NS_FAILED(NS_DispatchToMainThread(
-                  new DestroyRunnable(this)))) {
-      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
-    }
+    // Forces the last blob even if it's not time for it yet.
+    Extract(true, destroyRunnable);
 
     // Clean up.
     mEncoderListener->Forget();
     DebugOnly<bool> unregistered =
       mEncoder->UnregisterListener(mEncoderListener);
     MOZ_ASSERT(unregistered);
   }
 
@@ -1060,36 +1132,40 @@ private:
   RefPtr<TaskQueue> mEncoderThread;
   // MediaEncoder pipeline.
   RefPtr<MediaEncoder> mEncoder;
   // Listener through which MediaEncoder signals us.
   RefPtr<EncoderListener> mEncoderListener;
   // Set in Shutdown() and resolved when shutdown is complete.
   RefPtr<ShutdownPromise> mShutdownPromise;
   // A buffer to cache encoded media data.
-  nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
+  RefPtr<MutableBlobStorage> mMutableBlobStorage;
+  // Max memory to use for the MutableBlobStorage.
+  uint64_t mMaxMemory;
   // Current session mimeType
   nsString mMimeType;
   // Timestamp of the last fired dataavailable event.
   TimeStamp mLastBlobTimeStamp;
-  // The interval of passing encoded data from EncodedBufferCache to onDataAvailable
-  // handler. "mTimeSlice < 0" means Session object does not push encoded data to
-  // onDataAvailable, instead, it passive wait the client side pull encoded data
-  // by calling requestData API.
+  // The interval of passing encoded data from MutableBlobStorage to
+  // onDataAvailable handler. "mTimeSlice < 0" means Session object does not
+  // push encoded data to onDataAvailable, instead, it passive wait the client
+  // side pull encoded data by calling requestData API.
   const int32_t mTimeSlice;
   // Indicate this session's stop has been called.
   bool mStopIssued;
   // Indicate the session had fire start event. Encoding thread only.
   bool mIsStartEventFired;
   // False if the InitEncoder called successfully, ensure the
   // ExtractRunnable/DestroyRunnable will end the session.
   // Main thread only.
   bool mNeedSessionEndTask;
 };
 
+NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
+
 MediaRecorder::~MediaRecorder()
 {
   LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
   UnRegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aSourceMediaStream,
                              nsPIDOMWindowInner* aOwnerWindow)
@@ -1443,26 +1519,24 @@ MediaRecorder::IsTypeSupported(const nsA
       return false;
     }
   }
 
   return true;
 }
 
 nsresult
-MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
+MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
 
   BlobEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
-
-  nsCOMPtr<nsIDOMBlob> blob = aBlob;
-  init.mData = static_cast<Blob*>(blob.get());
+  init.mData = aBlob;
 
   RefPtr<BlobEvent> event =
     BlobEvent::Constructor(this,
                            NS_LITERAL_STRING("dataavailable"),
                            init);
   event->SetTrusted(true);
   bool dummy;
   return DispatchEvent(event, &dummy);
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -21,26 +21,27 @@ class DOMMediaStream;
 class ErrorResult;
 struct MediaRecorderOptions;
 class MediaStream;
 class GlobalObject;
 
 namespace dom {
 
 class AudioNode;
+class Blob;
 class DOMException;
 
 /**
  * Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
  * The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts,
  * a MediaEncoder will be created and accept the mediaStream as input source.
- * Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in EncodedBufferCache object.
+ * Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in a MutableBlobStorage object.
  * The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
  * Thread model:
- * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
+ * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in MutableBlobStorage object.
  * Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
  */
 
 class MediaRecorder final : public DOMEventTargetHelper,
                             public nsIDocumentActivity
 {
 public:
   class Session;
@@ -67,17 +68,17 @@ public:
   // If timeSlice isn't provided, UA should call the RequestData to obtain the Blob data, also set the mTimeSlice to zero.
   void Start(const Optional<int32_t>& timeSlice, ErrorResult & aResult);
   // Stop the recording activiy. Including stop the Media Encoder thread, un-hook the mediaStreamListener to encoder.
   void Stop(ErrorResult& aResult);
   // Pause a recording.
   void Pause(ErrorResult& aResult);
   // Resume a paused recording.
   void Resume(ErrorResult& aResult);
-  // Extract encoded data Blob from EncodedBufferCache.
+  // Extract encoded data Blob from MutableBlobStorage.
   void RequestData(ErrorResult& aResult);
   // Return the The DOMMediaStream passed from UA.
   DOMMediaStream* Stream() const { return mDOMStream; }
   // The current state of the MediaRecorder object.
   RecordingState State() const { return mState; }
   // Return the current encoding MIME type selected by the MediaEncoder.
   void GetMimeType(nsString &aMimeType);
 
@@ -116,17 +117,17 @@ public:
   uint32_t GetAudioBitrate() { return mAudioBitsPerSecond; }
   uint32_t GetVideoBitrate() { return mVideoBitsPerSecond; }
   uint32_t GetBitrate() { return mBitsPerSecond; }
 protected:
   virtual ~MediaRecorder();
 
   MediaRecorder& operator = (const MediaRecorder& x) = delete;
   // Create dataavailable event with Blob data and it runs in main thread
-  nsresult CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob);
+  nsresult CreateAndDispatchBlobEvent(Blob* aBlob);
   // Creating a simple event to notify UA simple event.
   void DispatchSimpleEvent(const nsAString & aStr);
   // Creating a error event with message.
   void NotifyError(nsresult aRv);
   // Set encoded MIME type.
   void SetMimeType(const nsString &aMimeType);
   void SetOptions(const MediaRecorderOptions& aInitDict);
 
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -99,17 +99,16 @@ EXPORTS += [
     'AudioStream.h',
     'Benchmark.h',
     'BufferMediaResource.h',
     'ChannelMediaDecoder.h',
     'CubebUtils.h',
     'DecoderDoctorDiagnostics.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
-    'EncodedBufferCache.h',
     'FileBlockCache.h',
     'FrameStatistics.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
     'MediaContainerType.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
@@ -213,17 +212,16 @@ UNIFIED_SOURCES += [
     'Benchmark.cpp',
     'CanvasCaptureMediaStream.cpp',
     'ChannelMediaDecoder.cpp',
     'ChannelMediaResource.cpp',
     'CloneableWithRangeMediaResource.cpp',
     'CubebUtils.cpp',
     'DecoderDoctorDiagnostics.cpp',
     'DOMMediaStream.cpp',
-    'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
     'FileMediaResource.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaContainerType.cpp',
     'MediaData.cpp',