Backed out 15 changesets (bug 1014393) for permafailing at test_mediarecorder_record_gum_video_timeslice_mixed.html a=backout
☠☠ backed out by 3f71769e7099 ☠ ☠
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 03 Aug 2019 19:22:18 +0300
changeset 486098 6954782553c76545a6ac10a2f5373e17b5e577c7
parent 486085 7c5c34ef4efc5fc762398d7111850f71196df5d5
child 486099 3f71769e709908d3bfb428927eedaa307ec83450
push id113829
push userdvarga@mozilla.com
push dateSat, 03 Aug 2019 22:20:04 +0000
treeherdermozilla-inbound@2e97b2d4c199 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1014393
milestone70.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
Backed out 15 changesets (bug 1014393) for permafailing at test_mediarecorder_record_gum_video_timeslice_mixed.html a=backout
dom/file/Blob.cpp
dom/file/Blob.h
dom/media/MediaFormatReader.cpp
dom/media/MediaManager.cpp
dom/media/MediaRecorder.cpp
dom/media/MediaRecorder.h
dom/media/encoder/MediaEncoder.cpp
dom/media/encoder/OpusTrackEncoder.cpp
dom/media/encoder/TrackEncoder.cpp
dom/media/encoder/VP8TrackEncoder.cpp
dom/media/gtest/moz.build
dom/media/ogg/OggCodecState.cpp
dom/media/ogg/OggDemuxer.cpp
dom/media/ogg/OggWriter.cpp
dom/media/ogg/OpusParser.cpp
dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
dom/media/test/test_mediarecorder_record_audionode.html
dom/media/test/test_mediarecorder_record_getdata_afterstart.html
dom/media/webm/WebMDemuxer.cpp
--- a/dom/file/Blob.cpp
+++ b/dom/file/Blob.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "Blob.h"
-#include "EmptyBlobImpl.h"
 #include "File.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/BodyStream.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "MultipartBlobImpl.h"
 #include "nsIInputStream.h"
@@ -69,24 +68,16 @@ void Blob::MakeValidBlobType(nsAString& 
 /* static */
 Blob* Blob::Create(nsISupports* aParent, BlobImpl* aImpl) {
   MOZ_ASSERT(aImpl);
 
   return aImpl->IsFile() ? new File(aParent, aImpl) : new Blob(aParent, aImpl);
 }
 
 /* static */
-already_AddRefed<Blob> Blob::CreateEmptyBlob(nsISupports* aParent,
-                                             const nsAString& aContentType) {
-  RefPtr<Blob> blob = Blob::Create(aParent, new EmptyBlobImpl(aContentType));
-  MOZ_ASSERT(!blob->mImpl->IsFile());
-  return blob.forget();
-}
-
-/* static */
 already_AddRefed<Blob> Blob::CreateStringBlob(nsISupports* aParent,
                                               const nsACString& aData,
                                               const nsAString& aContentType) {
   RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType);
   RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
   MOZ_ASSERT(!blob->mImpl->IsFile());
   return blob.forget();
 }
--- a/dom/file/Blob.h
+++ b/dom/file/Blob.h
@@ -45,19 +45,16 @@ class Blob : public nsIMutable,
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIMutable)
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_BLOB_IID)
 
   typedef OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString BlobPart;
 
   // This creates a Blob or a File based on the type of BlobImpl.
   static Blob* Create(nsISupports* aParent, BlobImpl* aImpl);
 
-  static already_AddRefed<Blob> CreateEmptyBlob(nsISupports* aParent,
-                                                const nsAString& aContentType);
-
   static already_AddRefed<Blob> CreateStringBlob(nsISupports* aParent,
                                                  const nsACString& aData,
                                                  const nsAString& aContentType);
 
   // The returned Blob takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
   static already_AddRefed<Blob> CreateMemoryBlob(nsISupports* aParent,
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -3013,10 +3013,8 @@ void MediaFormatReader::OnFirstDemuxFail
   MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing());
   decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity());
   MaybeResolveMetadataPromise();
 }
 
 }  // namespace mozilla
 
 #undef NS_DispatchToMainThread
-#undef LOGV
-#undef LOG
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -167,16 +167,20 @@ class nsMainThreadPtrHolder<
   // Copy constructor and operator= not implemented. Once constructed, the
   // holder is immutable.
   Holder& operator=(const nsMainThreadPtrHolder& aOther) = delete;
   nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther) = delete;
 };
 
 namespace mozilla {
 
+#ifdef LOG
+#  undef LOG
+#endif
+
 LazyLogModule gMediaManagerLog("MediaManager");
 #define LOG(...) MOZ_LOG(gMediaManagerLog, LogLevel::Debug, (__VA_ARGS__))
 
 class LocalTrackSource;
 
 using dom::CallerType;
 using dom::ConstrainDOMStringParameters;
 using dom::ConstrainDoubleRange;
@@ -4610,11 +4614,9 @@ void GetUserMediaWindowListener::NotifyC
         nsresult rv = MediaManager::NotifyRecordingStatusChange(window);
         if (NS_FAILED(rv)) {
           MOZ_ASSERT_UNREACHABLE("Should be able to notify chrome");
           return;
         }
       }));
 }
 
-#undef LOG
-
 }  // namespace mozilla
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -35,16 +35,20 @@
 #include "mozilla/dom/Document.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsMimeTypes.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
+#ifdef LOG
+#  undef LOG
+#endif
+
 mozilla::LazyLogModule gMediaRecorderLog("MediaRecorder");
 #define LOG(type, msg) MOZ_LOG(gMediaRecorderLog, type, msg)
 
 namespace mozilla {
 
 namespace dom {
 
 using namespace mozilla::media;
@@ -192,25 +196,82 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder,
  *
  * Lifetime of MediaRecorder and Session objects.
  * 1) MediaRecorder creates a Session in MediaRecorder::Start function and holds
  *    a reference to Session. Then the Session registers itself to a
  *    ShutdownBlocker and also holds a reference to MediaRecorder.
  *    Therefore, the reference dependency in gecko is:
  *    ShutdownBlocker -> Session <-> MediaRecorder, note that there is a cycle
  *    reference between Session and MediaRecorder.
- * 2) A Session is destroyed after MediaRecorder::Stop has been called _and_ all
- * encoded media data has been passed to OnDataAvailable handler. 3)
- * MediaRecorder::Stop is called by user or the document is going to inactive or
- * invisible.
+ * 2) A Session is destroyed in DestroyRunnable after MediaRecorder::Stop being
+ * called _and_ all encoded media data been passed to OnDataAvailable handler.
+ * 3) MediaRecorder::Stop is called by user or the document is going to
+ *    inactive or invisible.
  */
 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:
+    // We need to always declare refcounting because
+    // MutableBlobStorageCallback has pure-virtual refcounting.
+    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;
+      }
+
+      if (NS_FAILED(aRv)) {
+        mSession->DoSessionEndTask(aRv);
+        return;
+      }
+
+      nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
+      if (NS_FAILED(rv)) {
+        mSession->DoSessionEndTask(aRv);
+      }
+
+      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"),
@@ -233,16 +294,41 @@ class MediaRecorder::Session : public Pr
           break;
         }
       }
 
       return NS_OK;
     }
   };
 
+  // Notify encoder error, run in main thread task. (Bug 1095381)
+  class EncoderErrorNotifierRunnable : public Runnable {
+   public:
+    explicit EncoderErrorNotifierRunnable(Session* aSession)
+        : Runnable("dom::MediaRecorder::Session::EncoderErrorNotifierRunnable"),
+          mSession(aSession) {}
+
+    NS_IMETHOD Run() override {
+      LOG(LogLevel::Debug,
+          ("Session.ErrorNotifyRunnable s=(%p)", mSession.get()));
+      MOZ_ASSERT(NS_IsMainThread());
+
+      RefPtr<MediaRecorder> recorder = mSession->mRecorder;
+      if (!recorder) {
+        return NS_OK;
+      }
+
+      recorder->NotifyError(NS_ERROR_UNEXPECTED);
+      return NS_OK;
+    }
+
+   private:
+    RefPtr<Session> mSession;
+  };
+
   // Fire a named event, run in main thread task.
   class DispatchEventRunnable : public Runnable {
    public:
     explicit DispatchEventRunnable(Session* aSession,
                                    const nsAString& aEventName)
         : Runnable("dom::MediaRecorder::Session::DispatchEventRunnable"),
           mSession(aSession),
           mEventName(aEventName) {}
@@ -259,16 +345,85 @@ class MediaRecorder::Session : public Pr
       return NS_OK;
     }
 
    private:
     RefPtr<Session> mSession;
     nsString mEventName;
   };
 
+  // Main thread task.
+  // To delete RecordingSession object.
+  class DestroyRunnable : public Runnable {
+   public:
+    explicit DestroyRunnable(Session* aSession)
+        : Runnable("dom::MediaRecorder::Session::DestroyRunnable"),
+          mSession(aSession) {}
+
+    explicit DestroyRunnable(already_AddRefed<Session> aSession)
+        : Runnable("dom::MediaRecorder::Session::DestroyRunnable"),
+          mSession(aSession) {}
+
+    NS_IMETHOD Run() override {
+      LOG(LogLevel::Debug,
+          ("Session.DestroyRunnable session refcnt = (%d) s=(%p)",
+           static_cast<int>(mSession->mRefCnt), mSession.get()));
+      MOZ_ASSERT(NS_IsMainThread() && mSession);
+      RefPtr<MediaRecorder> recorder = mSession->mRecorder;
+      if (!recorder) {
+        return NS_OK;
+      }
+      // SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
+      // Read Thread will be terminate soon.
+      // We need to switch MediaRecorder to "Stop" state first to make sure
+      // MediaRecorder is not associated with this Session anymore, then, it's
+      // safe to delete this Session.
+      // Also avoid to run if this session already call stop before
+      if (mSession->mRunningState.isOk() &&
+          mSession->mRunningState.unwrap() != RunningState::Stopping &&
+          mSession->mRunningState.unwrap() != RunningState::Stopped) {
+        recorder->StopForSessionDestruction();
+        if (NS_FAILED(NS_DispatchToMainThread(
+                new DestroyRunnable(mSession.forget())))) {
+          MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
+        }
+        return NS_OK;
+      }
+
+      if (mSession->mRunningState.isOk()) {
+        mSession->mRunningState = RunningState::Stopped;
+      }
+
+      // Dispatch stop event and clear MIME type.
+      mSession->mMimeType = NS_LITERAL_STRING("");
+      recorder->SetMimeType(mSession->mMimeType);
+      recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
+
+      RefPtr<Session> session = mSession.forget();
+      session->Shutdown()->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [session]() {
+            gSessions.RemoveEntry(session);
+            if (gSessions.Count() == 0 && gMediaRecorderShutdownBlocker) {
+              // All sessions finished before shutdown, no need to keep the
+              // blocker.
+              RefPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
+              barrier->RemoveBlocker(gMediaRecorderShutdownBlocker);
+              gMediaRecorderShutdownBlocker = nullptr;
+            }
+          },
+          []() { MOZ_CRASH("Not reached"); });
+      return NS_OK;
+    }
+
+   private:
+    // Call mSession::Release automatically while DestroyRunnable be destroy.
+    RefPtr<Session> mSession;
+  };
+
   class EncoderListener : public MediaEncoderListener {
    public:
     EncoderListener(TaskQueue* aEncoderThread, Session* aSession)
         : mEncoderThread(aEncoderThread), mSession(aSession) {}
 
     void Forget() {
       MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
       mSession = nullptr;
@@ -302,31 +457,32 @@ class MediaRecorder::Session : public Pr
       }
     }
 
    protected:
     RefPtr<TaskQueue> mEncoderThread;
     RefPtr<Session> mSession;
   };
 
+  friend class EncoderErrorNotifierRunnable;
+  friend class PushBlobRunnable;
+  friend class DestroyRunnable;
+
  public:
   Session(MediaRecorder* aRecorder, uint32_t aTimeSlice)
       : mRecorder(aRecorder),
         mMediaStreamReady(false),
-        mMainThread(mRecorder->GetOwner()->EventTargetFor(TaskCategory::Other)),
         mTimeSlice(aTimeSlice),
-        mStartTime(TimeStamp::Now()),
         mRunningState(RunningState::Idling) {
     MOZ_ASSERT(NS_IsMainThread());
 
     aRecorder->GetMimeType(mMimeType);
     mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
                                       MAX_ALLOW_MEMORY_BUFFER);
-    mLastBlobTimeStamp = mStartTime;
-    Telemetry::ScalarAdd(Telemetry::ScalarID::MEDIARECORDER_RECORDING_COUNT, 1);
+    mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   void PrincipalChanged(MediaStreamTrack* aTrack) override {
     NS_ASSERTION(mMediaStreamTracks.Contains(aTrack),
                  "Principal changed for unrecorded track");
     if (!MediaStreamTracksPrincipalSubsumes()) {
       DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR);
     }
@@ -428,17 +584,17 @@ class MediaRecorder::Session : public Pr
       for (RefPtr<MediaStreamTrack>& track : tracks) {
         track->RemovePrincipalChangeObserver(this);
       }
     }
 
     if (mRunningState.isOk() &&
         mRunningState.unwrap() == RunningState::Idling) {
       LOG(LogLevel::Debug, ("Session.Stop Explicit end task %p", this));
-      // End the Session directly if there is no encoder.
+      // End the Session directly if there is no ExtractRunnable.
       DoSessionEndTask(NS_OK);
     } else if (mRunningState.isOk() &&
                (mRunningState.unwrap() == RunningState::Starting ||
                 mRunningState.unwrap() == RunningState::Running)) {
       mRunningState = RunningState::Stopping;
     }
   }
 
@@ -465,85 +621,44 @@ class MediaRecorder::Session : public Pr
     }
 
     mEncoder->Resume();
     NS_DispatchToMainThread(
         new DispatchEventRunnable(this, NS_LITERAL_STRING("resume")));
     return NS_OK;
   }
 
-  void RequestData() {
+  nsresult RequestData() {
     LOG(LogLevel::Debug, ("Session.RequestData"));
     MOZ_ASSERT(NS_IsMainThread());
 
-    GatherBlob()->Then(
-        mMainThread, __func__,
-        [this, self = RefPtr<Session>(this)](
-            const BlobPromise::ResolveOrRejectValue& aResult) {
-          if (aResult.IsReject()) {
-            LOG(LogLevel::Warning, ("GatherBlob failed for RequestData()"));
-            DoSessionEndTask(aResult.RejectValue());
-            return;
-          }
+    if (NS_FAILED(
+            NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
+      MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
+      return NS_ERROR_FAILURE;
+    }
 
-          nsresult rv =
-              mRecorder->CreateAndDispatchBlobEvent(aResult.ResolveValue());
-          if (NS_FAILED(rv)) {
-            DoSessionEndTask(NS_OK);
-          }
-        });
+    return NS_OK;
   }
 
   void MaybeCreateMutableBlobStorage() {
     if (!mMutableBlobStorage) {
       mMutableBlobStorage = new MutableBlobStorage(
           MutableBlobStorage::eCouldBeInTemporaryFile, nullptr, mMaxMemory);
     }
   }
 
-  static const bool IsExclusive = true;
-  using BlobPromise =
-      MozPromise<nsMainThreadPtrHandle<Blob>, nsresult, IsExclusive>;
-  class BlobStorer : public MutableBlobStorageCallback {
-    MozPromiseHolder<BlobPromise> mHolder;
-
-    virtual ~BlobStorer() = default;
-
-   public:
-    BlobStorer() = default;
-
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override)
+  void GetBlobWhenReady(MutableBlobStorageCallback* aCallback) {
+    MOZ_ASSERT(NS_IsMainThread());
 
-    void BlobStoreCompleted(MutableBlobStorage*, Blob* aBlob,
-                            nsresult aRv) override {
-      MOZ_ASSERT(NS_IsMainThread());
-      if (NS_FAILED(aRv)) {
-        mHolder.Reject(aRv, __func__);
-      } else {
-        mHolder.Resolve(nsMainThreadPtrHandle<Blob>(
-                            MakeAndAddRef<nsMainThreadPtrHolder<Blob>>(
-                                "BlobStorer::ResolveBlob", aBlob)),
-                        __func__);
-      }
-    }
-
-    RefPtr<BlobPromise> Promise() { return mHolder.Ensure(__func__); }
-  };
-
-  // Stops gathering data into the current blob and resolves when the current
-  // blob is available. Future data will be stored in a new blob.
-  RefPtr<BlobPromise> GatherBlob() {
-    MOZ_ASSERT(NS_IsMainThread());
-    RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>();
     MaybeCreateMutableBlobStorage();
-    mMutableBlobStorage->GetBlobWhenReady(
-        mRecorder->GetOwner(), NS_ConvertUTF16toUTF8(mMimeType), storer);
+    mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
+                                          NS_ConvertUTF16toUTF8(mMimeType),
+                                          aCallback);
     mMutableBlobStorage = nullptr;
-
-    return storer->Promise();
   }
 
   RefPtr<SizeOfPromise> SizeOfExcludingThis(
       mozilla::MallocSizeOf aMallocSizeOf) {
     MOZ_ASSERT(NS_IsMainThread());
     size_t encodedBufferSize =
         mMutableBlobStorage ? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
                             : 0;
@@ -558,26 +673,27 @@ class MediaRecorder::Session : public Pr
         [encoder, encodedBufferSize, aMallocSizeOf]() {
           return SizeOfPromise::CreateAndResolve(
               encodedBufferSize + encoder->SizeOfExcludingThis(aMallocSizeOf),
               __func__);
         });
   }
 
  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 MutableBlobStorage.
-  // If the bool aForceFlush is true, we will force a dispatch of a blob to
-  // main thread.
-  void Extract(bool aForceFlush) {
+  // 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, 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
     nsTArray<nsTArray<uint8_t>> encodedBuf;
@@ -595,34 +711,26 @@ class MediaRecorder::Session : public Pr
     // 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) {
-      mLastBlobTimeStamp = TimeStamp::Now();
-      InvokeAsync(mMainThread, this, __func__, &Session::GatherBlob)
-          ->Then(mMainThread, __func__,
-                 [this, self = RefPtr<Session>(this)](
-                     const BlobPromise::ResolveOrRejectValue& aResult) {
-                   if (aResult.IsReject()) {
-                     LOG(LogLevel::Warning,
-                         ("GatherBlob failed for pushing blob"));
-                     DoSessionEndTask(aResult.RejectValue());
-                     return;
-                   }
-
-                   nsresult rv = mRecorder->CreateAndDispatchBlobEvent(
-                       aResult.ResolveValue());
-                   if (NS_FAILED(rv)) {
-                     DoSessionEndTask(NS_OK);
-                   }
-                 });
+      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() {
     if (!mMediaStream) {
       // Already shut down. This can happen because MediaStreamReady is async.
       return;
     }
@@ -666,17 +774,17 @@ class MediaRecorder::Session : public Pr
     }
 
     mMediaStreamReady = true;
 
     if (audioTracks > 1 || videoTracks > 1) {
       // When MediaRecorder supports multiple tracks, we should set up a single
       // MediaInputPort from the input stream, and let main thread check
       // track principals async later.
-      nsPIDOMWindowInner* window = mRecorder->GetOwner();
+      nsPIDOMWindowInner* window = mRecorder->GetParentObject();
       Document* document = window ? window->GetExtantDoc() : nullptr;
       nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                       NS_LITERAL_CSTRING("Media"), document,
                                       nsContentUtils::eDOM_PROPERTIES,
                                       "MediaRecorderMultiTracksNotSupported");
       DoSessionEndTask(NS_ERROR_ABORT);
       return;
     }
@@ -869,187 +977,140 @@ class MediaRecorder::Session : public Pr
     for (auto& track : mMediaStreamTracks) {
       mEncoder->ConnectMediaStreamTrack(track);
     }
 
     // If user defines timeslice interval for video blobs we have to set
     // appropriate video keyframe interval defined in milliseconds.
     mEncoder->SetVideoKeyFrameInterval(mTimeSlice);
 
-    // Set mRunningState to Running so that DoSessionEndTask will
+    // Set mRunningState to Running so that ExtractRunnable/DestroyRunnable will
     // take the responsibility to end the session.
     mRunningState = RunningState::Starting;
   }
 
-  // This is the task that will stop recording per spec:
-  // - Stop gathering data (this is inherently async)
-  // - Set state to "inactive"
-  // - Fire an error event, if NS_FAILED(rv)
-  // - Discard blob data if rv is NS_ERROR_DOM_SECURITY_ERR
-  // - Fire a Blob event
-  // - Fire an event named stop
+  // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv) {
     MOZ_ASSERT(NS_IsMainThread());
     if (mRunningState.isErr()) {
       // We have already ended with an error.
       return;
     }
 
     if (mRunningState.isOk() &&
         mRunningState.unwrap() == RunningState::Stopped) {
       // We have already ended gracefully.
       return;
     }
 
-    bool needsStartEvent = false;
     if (mRunningState.isOk() &&
         (mRunningState.unwrap() == RunningState::Idling ||
          mRunningState.unwrap() == RunningState::Starting)) {
-      needsStartEvent = true;
+      NS_DispatchToMainThread(
+          new DispatchEventRunnable(this, NS_LITERAL_STRING("start")));
     }
 
     if (rv == NS_OK) {
       mRunningState = RunningState::Stopped;
     } else {
       mRunningState = Err(rv);
     }
 
-    GatherBlob()
-        ->Then(mMainThread, __func__,
-               [this, self = RefPtr<Session>(this), rv, needsStartEvent](
-                   const BlobPromise::ResolveOrRejectValue& aResult) {
-                 if (mRecorder->mSessions.LastElement() == this) {
-                   // Set state to inactive, but only if the recorder is not
-                   // controlled by another session already.
-                   mRecorder->ForceInactive();
-                 }
+    if (NS_FAILED(rv)) {
+      mRecorder->ForceInactive();
+      NS_DispatchToMainThread(NewRunnableMethod<nsresult>(
+          "dom::MediaRecorder::NotifyError", mRecorder,
+          &MediaRecorder::NotifyError, rv));
+    }
 
-                 if (needsStartEvent) {
-                   mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
-                 }
-
-                 // If there was an error, Fire the appropriate one
-                 if (NS_FAILED(rv)) {
-                   mRecorder->NotifyError(rv);
-                 }
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
 
-                 // Fire a blob event named dataavailable
-                 RefPtr<Blob> blob;
-                 if (rv == NS_ERROR_DOM_SECURITY_ERR || aResult.IsReject()) {
-                   // In case of SecurityError, the blob data must be discarded.
-                   // We create a new empty one and throw the blob with its data
-                   // away.
-                   // In case we failed to gather blob data, we create an empty
-                   // memory blob instead.
-                   blob = Blob::CreateEmptyBlob(mRecorder->GetParentObject(),
-                                                mMimeType);
-                 } else {
-                   blob = aResult.ResolveValue();
-                 }
-                 if (NS_FAILED(mRecorder->CreateAndDispatchBlobEvent(blob))) {
-                   // Failed to dispatch blob event. That's unexpected. It's
-                   // probably all right to fire an error event if we haven't
-                   // already.
-                   if (NS_SUCCEEDED(rv)) {
-                     mRecorder->NotifyError(NS_ERROR_FAILURE);
-                   }
-                 }
-
-                 // Dispatch stop event and clear MIME type.
-                 mMimeType = NS_LITERAL_STRING("");
-                 mRecorder->SetMimeType(mMimeType);
-
-                 // Fire an event named stop
-                 mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
-
-                 // And finally, Shutdown and destroy the Session
-                 return Shutdown();
-               })
-        ->Then(mMainThread, __func__, [this, self = RefPtr<Session>(this)] {
-          gSessions.RemoveEntry(this);
-          if (gSessions.Count() == 0 && gMediaRecorderShutdownBlocker) {
-            // All sessions finished before shutdown, no need to keep the
-            // blocker.
-            RefPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
-            barrier->RemoveBlocker(gMediaRecorderShutdownBlocker);
-            gMediaRecorderShutdownBlocker = nullptr;
-          }
-        });
+    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, destroyRunnable)))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
+      }
+    } else {
+      if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+      }
+    }
   }
 
   void MediaEncoderInitialized() {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     NS_DispatchToMainThread(NewRunnableFrom([self = RefPtr<Session>(this), this,
                                              mime = mEncoder->MimeType()]() {
       if (mRunningState.isErr()) {
         return NS_OK;
       }
       mMimeType = mime;
       mRecorder->SetMimeType(mime);
       auto state = mRunningState.unwrap();
       if (state == RunningState::Starting || state == RunningState::Stopping) {
+        if (!self->mRecorder) {
+          MOZ_ASSERT_UNREACHABLE("Recorder should be live");
+          return NS_OK;
+        }
         if (state == RunningState::Starting) {
           // We set it to Running in the runnable since we can only assign
           // mRunningState on main thread. We set it before running the start
           // event runnable since that dispatches synchronously (and may cause
           // js calls to methods depending on mRunningState).
           mRunningState = RunningState::Running;
         }
         mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
       }
       return NS_OK;
     }));
 
-    Extract(false);
+    Extract(false, nullptr);
   }
 
   void MediaEncoderDataAvailable() {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
-    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());
 
-    mMainThread->Dispatch(NewRunnableMethod<nsresult>(
-        "MediaRecorder::Session::MediaEncoderShutdown->DoSessionEndTask", this,
-        &Session::DoSessionEndTask, NS_OK));
+    // For the stop event. Let's the creation of the blob to dispatch this
+    // runnable.
+    RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
+
+    // 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);
   }
 
   RefPtr<ShutdownPromise> Shutdown() {
     MOZ_ASSERT(NS_IsMainThread());
     LOG(LogLevel::Debug, ("Session Shutdown %p", this));
 
     if (mShutdownPromise) {
       return mShutdownPromise;
     }
 
-    // This is a coarse calculation and does not reflect the duration of the
-    // final recording for reasons such as pauses. However it allows us an
-    // idea of how long people are running their recorders for.
-    TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
-    Telemetry::Accumulate(Telemetry::MEDIA_RECORDER_RECORDING_DURATION,
-                          timeDelta.ToSeconds());
-
     mShutdownPromise = ShutdownPromise::CreateAndResolve(true, __func__);
     RefPtr<Session> self = this;
 
     if (mEncoder) {
       auto& encoder = mEncoder;
       encoder->Cancel();
 
       MOZ_RELEASE_ASSERT(mEncoderListener);
@@ -1076,26 +1137,29 @@ class MediaRecorder::Session : public Pr
     {
       auto tracks(std::move(mMediaStreamTracks));
       for (RefPtr<MediaStreamTrack>& track : tracks) {
         track->RemovePrincipalChangeObserver(this);
       }
     }
 
     // Break the cycle reference between Session and MediaRecorder.
-    mShutdownPromise = mShutdownPromise->Then(
-        GetCurrentThreadSerialEventTarget(), __func__,
-        [self]() {
-          self->mRecorder->RemoveSession(self);
-          return ShutdownPromise::CreateAndResolve(true, __func__);
-        },
-        []() {
-          MOZ_ASSERT_UNREACHABLE("Unexpected reject");
-          return ShutdownPromise::CreateAndReject(false, __func__);
-        });
+    if (mRecorder) {
+      mShutdownPromise = mShutdownPromise->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [self]() {
+            self->mRecorder->RemoveSession(self);
+            self->mRecorder = nullptr;
+            return ShutdownPromise::CreateAndResolve(true, __func__);
+          },
+          []() {
+            MOZ_ASSERT_UNREACHABLE("Unexpected reject");
+            return ShutdownPromise::CreateAndReject(false, __func__);
+          });
+    }
 
     if (mEncoderThread) {
       RefPtr<TaskQueue>& encoderThread = mEncoderThread;
       mShutdownPromise = mShutdownPromise->Then(
           GetCurrentThreadSerialEventTarget(), __func__,
           [encoderThread]() { return encoderThread->BeginShutdown(); },
           []() {
             MOZ_ASSERT_UNREACHABLE("Unexpected reject");
@@ -1110,31 +1174,30 @@ class MediaRecorder::Session : public Pr
   enum class RunningState {
     Idling,    // Session has been created
     Starting,  // MediaEncoder started, waiting for data
     Running,   // MediaEncoder has produced data
     Stopping,  // Stop() has been called
     Stopped,   // Session has stopped without any error
   };
 
-  // Our associated MediaRecorder.
-  const RefPtr<MediaRecorder> mRecorder;
+  // Hold reference to MediaRecorder that ensure MediaRecorder is alive
+  // if there is an active session. Access ONLY on main thread.
+  RefPtr<MediaRecorder> mRecorder;
 
   // Stream currently recorded.
   RefPtr<DOMMediaStream> mMediaStream;
 
   // True after we have decided on the track set to use for the recording.
   bool mMediaStreamReady;
 
   // Tracks currently recorded. This should be a subset of mMediaStream's track
   // set.
   nsTArray<RefPtr<MediaStreamTrack>> mMediaStreamTracks;
 
-  // Main thread used for MozPromise operations.
-  const RefPtr<nsISerialEventTarget> mMainThread;
   // Runnable thread for reading data from MediaEncoder.
   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;
@@ -1144,24 +1207,24 @@ class MediaRecorder::Session : public Pr
   uint64_t mMaxMemory;
   // Current session mimeType
   nsString mMimeType;
   // Timestamp of the last fired dataavailable event.
   TimeStamp mLastBlobTimeStamp;
   // The interval of passing encoded data from MutableBlobStorage to
   // onDataAvailable handler.
   const uint32_t mTimeSlice;
-  // The time this session started, for telemetry.
-  const TimeStamp mStartTime;
-  // The session's current main thread state. The error type gets set when
-  // ending a recording with an error. An NS_OK error is invalid.
+  // The session's current main thread state. The error type gets setwhen ending
+  // a recording with an error. An NS_OK error is invalid.
   // Main thread only.
   Result<RunningState, nsresult> mRunningState;
 };
 
+NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
+
 MediaRecorder::~MediaRecorder() {
   LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
   UnRegisterActivityObserver();
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aSourceMediaStream,
                              nsPIDOMWindowInner* aOwnerWindow)
     : DOMEventTargetHelper(aOwnerWindow),
@@ -1246,16 +1309,18 @@ void MediaRecorder::Start(const Optional
 
   uint32_t timeSlice = aTimeSlice.WasPassed() ? aTimeSlice.Value() : 0;
   MediaRecorderReporter::AddMediaRecorder(this);
   mState = RecordingState::Recording;
   // Start a session.
   mSessions.AppendElement();
   mSessions.LastElement() = new Session(this, timeSlice);
   mSessions.LastElement()->Start();
+  mStartTime = TimeStamp::Now();
+  Telemetry::ScalarAdd(Telemetry::ScalarID::MEDIARECORDER_RECORDING_COUNT, 1);
 }
 
 void MediaRecorder::Stop(ErrorResult& aResult) {
   LOG(LogLevel::Debug, ("MediaRecorder.Stop %p", this));
   MediaRecorderReporter::RemoveMediaRecorder(this);
   if (mState == RecordingState::Inactive) {
     return;
   }
@@ -1307,17 +1372,20 @@ void MediaRecorder::Resume(ErrorResult& 
 }
 
 void MediaRecorder::RequestData(ErrorResult& aResult) {
   if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mSessions.Length() > 0);
-  mSessions.LastElement()->RequestData();
+  nsresult rv = mSessions.LastElement()->RequestData();
+  if (NS_FAILED(rv)) {
+    NotifyError(rv);
+  }
 }
 
 JSObject* MediaRecorder::WrapObject(JSContext* aCx,
                                     JS::Handle<JSObject*> aGivenProto) {
   return MediaRecorder_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */
@@ -1596,16 +1664,32 @@ void MediaRecorder::NotifyOwnerDocumentA
   }
 }
 
 void MediaRecorder::ForceInactive() {
   LOG(LogLevel::Debug, ("MediaRecorder.ForceInactive %p", this));
   mState = RecordingState::Inactive;
 }
 
+void MediaRecorder::StopForSessionDestruction() {
+  LOG(LogLevel::Debug, ("MediaRecorder.StopForSessionDestruction %p", this));
+  MediaRecorderReporter::RemoveMediaRecorder(this);
+  // We do not perform a mState != RecordingState::Recording) check here as
+  // we may already be inactive due to ForceInactive().
+  mState = RecordingState::Inactive;
+  MOZ_ASSERT(mSessions.Length() > 0);
+  mSessions.LastElement()->Stop();
+  // This is a coarse calculation and does not reflect the duration of the
+  // final recording for reasons such as pauses. However it allows us an idea
+  // of how long people are running their recorders for.
+  TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
+  Telemetry::Accumulate(Telemetry::MEDIA_RECORDER_RECORDING_DURATION,
+                        timeDelta.ToSeconds());
+}
+
 void MediaRecorder::InitializeDomExceptions() {
   mSecurityDomException = DOMException::Create(NS_ERROR_DOM_SECURITY_ERR);
   mUnknownDomException = DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR);
 }
 
 RefPtr<MediaRecorder::SizeOfPromise> MediaRecorder::SizeOfExcludingThis(
     mozilla::MallocSizeOf aMallocSizeOf) {
   MOZ_ASSERT(NS_IsMainThread());
@@ -1634,10 +1718,8 @@ RefPtr<MediaRecorder::SizeOfPromise> Med
 
   return promise;
 }
 
 StaticRefPtr<MediaRecorderReporter> MediaRecorderReporter::sUniqueInstance;
 
 }  // namespace dom
 }  // namespace mozilla
-
-#undef LOG
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -56,16 +56,18 @@ class MediaRecorder final : public DOMEv
                 nsPIDOMWindowInner* aOwnerWindow);
 
   static nsTArray<RefPtr<Session>> GetSessions();
 
   // nsWrapperCache
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
+  nsPIDOMWindowInner* GetParentObject() { return GetOwner(); }
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaRecorder, DOMEventTargetHelper)
 
   // WebIDL
   // Start recording. If timeSlice has been provided, mediaRecorder will
   // raise a dataavailable event containing the Blob of collected data on every
   // timeSlice milliseconds. If timeSlice isn't provided, UA should call the
   // RequestData to obtain the Blob data, also set the mTimeSlice to zero.
@@ -170,16 +172,18 @@ class MediaRecorder final : public DOMEv
   // It specifies the container format as well as the audio and video capture
   // formats.
   nsString mMimeType;
 
   uint32_t mAudioBitsPerSecond;
   uint32_t mVideoBitsPerSecond;
   uint32_t mBitsPerSecond;
 
+  TimeStamp mStartTime;
+
   // DOMExceptions that are created early and possibly thrown in NotifyError.
   // Creating them early allows us to capture the JS stack for which cannot be
   // done at the time the error event is fired.
   RefPtr<DOMException> mSecurityDomException;
   RefPtr<DOMException> mUnknownDomException;
 
  private:
   // Register MediaRecorder into Document to listen the activity changes.
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -34,16 +34,20 @@
 #include "TimeUnits.h"
 #include "Tracing.h"
 
 #ifdef MOZ_WEBM_ENCODER
 #  include "VP8TrackEncoder.h"
 #  include "WebMWriter.h"
 #endif
 
+#ifdef LOG
+#  undef LOG
+#endif
+
 mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder");
 #define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg)
 
 namespace mozilla {
 
 using namespace dom;
 using namespace media;
 
@@ -423,24 +427,17 @@ MediaEncoder::MediaEncoder(TaskQueue* aE
         mEncoderThread->Dispatch(NewRunnableMethod<RefPtr<EncoderListener>>(
             "mozilla::VideoTrackEncoder::RegisterListener", mVideoEncoder,
             &VideoTrackEncoder::RegisterListener, mEncoderListener));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     Unused << rv;
   }
 }
 
-MediaEncoder::~MediaEncoder() {
-  MOZ_ASSERT(mListeners.IsEmpty());
-  MOZ_ASSERT(!mAudioTrack);
-  MOZ_ASSERT(!mVideoTrack);
-  MOZ_ASSERT(!mAudioNode);
-  MOZ_ASSERT(!mInputPort);
-  MOZ_ASSERT(!mPipeStream);
-}
+MediaEncoder::~MediaEncoder() { MOZ_ASSERT(mListeners.IsEmpty()); }
 
 void MediaEncoder::RunOnGraph(already_AddRefed<Runnable> aRunnable) {
   MediaStreamGraphImpl* graph;
   if (mAudioTrack) {
     graph = mAudioTrack->GraphImpl();
   } else if (mVideoTrack) {
     graph = mVideoTrack->GraphImpl();
   } else if (mPipeStream) {
@@ -868,18 +865,16 @@ void MediaEncoder::Shutdown() {
 bool MediaEncoder::IsShutdown() {
   MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
   return mShutdown;
 }
 
 void MediaEncoder::Cancel() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  Stop();
-
   RefPtr<MediaEncoder> self = this;
   nsresult rv = mEncoderThread->Dispatch(NewRunnableFrom([self]() mutable {
     self->mCanceled = true;
 
     if (self->mAudioEncoder) {
       self->mAudioEncoder->Cancel();
     }
     if (self->mVideoEncoder) {
@@ -1025,10 +1020,8 @@ void MediaEncoder::SetVideoKeyFrameInter
   nsresult rv = mEncoderThread->Dispatch(NewRunnableMethod<int32_t>(
       "mozilla::VideoTrackEncoder::SetKeyFrameInterval", mVideoEncoder,
       &VideoTrackEncoder::SetKeyFrameInterval, aVideoKeyFrameInterval));
   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
   Unused << rv;
 }
 
 }  // namespace mozilla
-
-#undef LOG
--- a/dom/media/encoder/OpusTrackEncoder.cpp
+++ b/dom/media/encoder/OpusTrackEncoder.cpp
@@ -5,16 +5,17 @@
 #include "OpusTrackEncoder.h"
 #include "nsString.h"
 #include "GeckoProfiler.h"
 #include "mozilla/CheckedInt.h"
 #include "VideoUtils.h"
 
 #include <opus/opus.h>
 
+#undef LOG
 #define LOG(args, ...)
 
 namespace mozilla {
 
 // The Opus format supports up to 8 channels, and supports multitrack audio up
 // to 255 channels, but the current implementation supports only mono and
 // stereo, and downmixes any more than that.
 static const int MAX_SUPPORTED_AUDIO_CHANNELS = 8;
@@ -428,10 +429,8 @@ nsresult OpusTrackEncoder::GetEncodedTra
     LOG("[Opus] mOutputTimeStamp %lld.", mOutputTimeStamp);
     aData.AppendElement(audiodata);
   }
 
   return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 }  // namespace mozilla
-
-#undef LOG
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -758,10 +758,8 @@ void VideoTrackEncoder::SetKeyFrameInter
   if (aKeyFrameInterval == 0) {
     mKeyFrameInterval = DEFAULT_KEYFRAME_INTERVAL_MS;
     return;
   }
   mKeyFrameInterval = std::min(aKeyFrameInterval, DEFAULT_KEYFRAME_INTERVAL_MS);
 }
 
 }  // namespace mozilla
-
-#undef TRACK_LOG
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -565,10 +565,8 @@ nsresult VP8TrackEncoder::GetEncodedTrac
       }
     }
   }
 
   return NS_OK;
 }
 
 }  // namespace mozilla
-
-#undef VP8LOG
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -33,17 +33,16 @@ UNIFIED_SOURCES += [
     'TestIntervalSet.cpp',
     'TestMediaDataDecoder.cpp',
     'TestMediaDataEncoder.cpp',
     'TestMediaEventSource.cpp',
     'TestMediaMIMETypes.cpp',
     'TestMediaSpan.cpp',
     'TestMP3Demuxer.cpp',
     'TestMP4Demuxer.cpp',
-    'TestMuxer.cpp',
     'TestOpusParser.cpp',
     'TestRust.cpp',
     'TestTimeUnit.cpp',
     'TestVideoSegment.cpp',
     'TestVideoUtils.cpp',
     'TestVPXDecoding.cpp',
     'TestWebMBuffered.cpp',
 ]
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -1670,11 +1670,9 @@ bool SkeletonState::DecodeHeader(OggPack
     return DecodeFisbone(aPacket.get());
   } else if (aPacket->e_o_s) {
     mDoneReadingHeaders = true;
     return true;
   }
   return true;
 }
 
-#undef LOG
-
 }  // namespace mozilla
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -1888,10 +1888,10 @@ nsresult OggDemuxer::SeekBisection(Track
   }
 
   SEEK_LOG(LogLevel::Debug, ("Seek complete in %d bisections.", hops));
 
   return NS_OK;
 }
 
 #undef OGG_DEBUG
-#undef SEEK_LOG
+#undef SEEK_DEBUG
 }  // namespace mozilla
--- a/dom/media/ogg/OggWriter.cpp
+++ b/dom/media/ogg/OggWriter.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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 "OggWriter.h"
 #include "prtime.h"
 #include "GeckoProfiler.h"
 
+#undef LOG
 #define LOG(args, ...)
 
 namespace mozilla {
 
 OggWriter::OggWriter()
     : ContainerWriter(), mOggStreamState(), mOggPage(), mPacket() {
   if (NS_FAILED(Init())) {
     LOG("ERROR! Fail to initialize the OggWriter.");
@@ -189,10 +190,8 @@ nsresult OggWriter::SetMetadata(
     LOG("miss mCommentHeader!");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 }  // namespace mozilla
-
-#undef LOG
--- a/dom/media/ogg/OpusParser.cpp
+++ b/dom/media/ogg/OpusParser.cpp
@@ -207,11 +207,9 @@ bool OpusParser::IsValidMapping2Channels
   if (aChannels < 1 || aChannels > 227) {
     return false;
   }
   double val = sqrt(aChannels);
   int32_t valInt = int32_t(val);
   return val == valInt || valInt * valInt + 2 == aChannels;
 }
 
-#undef OPUS_LOG
-
 }  // namespace mozilla
--- a/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
+++ b/dom/media/test/test_mediarecorder_record_4ch_audiocontext.html
@@ -51,18 +51,17 @@ function startTest() {
     SimpleTest.finish();
   };
   mMediaRecorder.ondataavailable = function (e) {
     if (mMediaRecorder.state == 'recording') {
       ok(e.data.size > 0, 'check blob has data');
     }
     totalBlobSize += e.data.size;
     ok(totalBlobSize > 0, 'check the totalBlobSize');
-    is(e.data.type, expectedMimeType, 'blob should have expected mimetype');
-    is(mMediaRecorder.mimeType, expectedMimeType, 'recorder should have expected mimetype');
+    is(mMediaRecorder.mimeType, expectedMimeType, 'blob should has mimetype, return ' + mMediaRecorder.mimeType);
     if (!stopTriggered) {
       mMediaRecorder.stop();
       stopTriggered = true;
     } else if (onstopTriggered) {
       ok(false, 'ondataavailable should come before onstop event');
     }
   };
   try {
--- a/dom/media/test/test_mediarecorder_record_audionode.html
+++ b/dom/media/test/test_mediarecorder_record_audionode.html
@@ -60,19 +60,17 @@ async function testRecord(source, mimeTy
     recorder.onstop = () => ok(false, "should not fire stop yet");
   }
 
   recorder.start(1000);
   is("recording", recorder.state, "state should become recording after calling start()");
 
   const chunks = [];
   let {data} = await new Promise(r => recorder.ondataavailable = r);
-  if (!isOffline) {
-    is(recorder.state, "recording", "Expected to still be recording");
-  }
+  is(recorder.state, "recording", "Expected to still be recording");
   is(data.type, recorder.mimeType, "Blob has recorder mimetype");
   if (mimeType != "") {
     is(data.type, mimeType, "Blob has given mimetype");
   }
   isnot(data.size, 0, "should get data and its length should be > 0");
   chunks.push(data);
 
   if (isOffline) {
--- a/dom/media/test/test_mediarecorder_record_getdata_afterstart.html
+++ b/dom/media/test/test_mediarecorder_record_getdata_afterstart.html
@@ -33,34 +33,33 @@ function startTest(test, token) {
   mMediaRecorder.onerror = function() {
     ok(false, 'onerror unexpectedly fired');
   };
 
   mMediaRecorder.onstart = function() {
     info('onstart fired successfully');
     hasonstart = true;
     // On audio only case, we produce audio/ogg as mimeType.
-    is('audio/ogg', mMediaRecorder.mimeType, "MediaRecorder mimetype as expected");
+    is('audio/ogg', mMediaRecorder.mimeType, "check the record mimetype return " + mMediaRecorder.mimeType);
     mMediaRecorder.requestData();
   };
 
   mMediaRecorder.onstop = function() {
     info('onstop fired successfully');
-    ok(hasondataavailable, "should have ondataavailable before onstop");
+    ok (hasondataavailable, "should have ondataavailable before onstop");
     is(mMediaRecorder.state, 'inactive', 'check recording status is inactive');
     SimpleTest.finish();
   };
 
   mMediaRecorder.ondataavailable = function (e) {
     info('ondataavailable fired successfully');
     if (mMediaRecorder.state == 'recording') {
       hasondataavailable = true;
-      ok(hasonstart, "should have had start event first");
-      is(e.data.type, mMediaRecorder.mimeType,
-        "blob's mimeType matches the recorder's");
+      ok(hasonstart, "should has onstart event first");
+      ok(e.data.size > 0, 'check blob has data');
       mMediaRecorder.stop();
     }
   };
 
   // Start recording once metadata are parsed.
   element.onloadedmetadata = function() {
     element.oncanplaythrough = null;
     mMediaRecorder.start(0);
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -1254,11 +1254,11 @@ void WebMTrackDemuxer::BreakCycles() { m
 int64_t WebMTrackDemuxer::GetEvictionOffset(const TimeUnit& aTime) {
   int64_t offset;
   if (!mParent->GetOffsetForTime(aTime.ToNanoseconds(), &offset)) {
     return 0;
   }
 
   return offset;
 }
-}  // namespace mozilla
 
 #undef WEBM_DEBUG
+}  // namespace mozilla