Bug 1259788 - Add a new disabled mode for MSG tracks. r?jesup draft
authorAndreas Pehrson <pehrsons@gmail.com>
Mon, 15 Aug 2016 14:19:42 +0200
changeset 404930 4de8f6c6f7531b2a9692d02fc06a33ed25de89d7
parent 404929 ceb24561f996b3712f7d76ab4a1493b27714e92b
child 404931 d39aa548af21a9a6a8b25bf579f039a128f94de2
push id27359
push userpehrsons@gmail.com
push dateWed, 24 Aug 2016 14:53:56 +0000
reviewersjesup
bugs1259788
milestone51.0a1
Bug 1259788 - Add a new disabled mode for MSG tracks. r?jesup MozReview-Commit-ID: 1dMTR4Wmcd8
dom/html/HTMLMediaElement.cpp
dom/media/MediaSegment.h
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/MediaStreamTrack.cpp
dom/media/TrackUnionStream.cpp
dom/media/TrackUnionStream.h
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webaudio/AudioNodeStream.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2419,17 +2419,18 @@ HTMLMediaElement::SetCapturedOutputStrea
     for (auto pair : ms.mTrackPorts) {
       MediaStream* outputSource = ms.mStream->GetInputStream();
       if (!outputSource) {
         NS_ERROR("No output source stream");
         return;
       }
 
       TrackID id = pair.second()->GetDestinationTrackId();
-      outputSource->SetTrackEnabled(id, aEnabled);
+      outputSource->SetTrackEnabled(id, aEnabled ? DisabledTrackMode::ENABLED
+                                                 : DisabledTrackMode::SILENCE_FREEZE);
 
       LOG(LogLevel::Debug,
           ("%s track %d for captured MediaStream %p",
            aEnabled ? "Enabled" : "Disabled", id, ms.mStream.get()));
     }
   }
 }
 
@@ -2492,26 +2493,28 @@ HTMLMediaElement::AddCaptureMediaTrackTo
       NewRunnableMethod<StorensRefPtrPassByPtr<MediaStreamTrack>>(
         aOutputStream.mStream, &DOMMediaStream::AddTrackInternal, track));
   } else {
     aOutputStream.mStream->AddTrackInternal(track);
   }
 
   // Track is muted initially, so we don't leak data if it's added while paused
   // and an MSG iteration passes before the mute comes into effect.
-  processedOutputSource->SetTrackEnabled(destinationTrackID, false);
+  processedOutputSource->SetTrackEnabled(destinationTrackID,
+                                         DisabledTrackMode::SILENCE_FREEZE);
   RefPtr<MediaInputPort> port =
     inputTrack->ForwardTrackContentsTo(processedOutputSource,
                                        destinationTrackID);
 
   Pair<nsString, RefPtr<MediaInputPort>> p(aTrack->GetId(), port);
   aOutputStream.mTrackPorts.AppendElement(Move(p));
 
   if (mSrcStreamIsPlaying) {
-    processedOutputSource->SetTrackEnabled(destinationTrackID, true);
+    processedOutputSource->SetTrackEnabled(destinationTrackID,
+                                           DisabledTrackMode::ENABLED);
   }
 
   LOG(LogLevel::Debug,
       ("Created %s track %p with id %d from track %p through MediaInputPort %p",
        inputTrack->AsAudioStreamTrack() ? "audio" : "video",
        track.get(), destinationTrackID, inputTrack, port.get()));
 }
 
--- a/dom/media/MediaSegment.h
+++ b/dom/media/MediaSegment.h
@@ -169,20 +169,24 @@ public:
    * Insert aDuration of null data at the start of the segment.
    */
   virtual void InsertNullDataAtStart(StreamTime aDuration) = 0;
   /**
    * Insert aDuration of null data at the end of the segment.
    */
   virtual void AppendNullData(StreamTime aDuration) = 0;
   /**
-   * Replace contents with disabled data of the same duration
+   * Replace contents with disabled (silence/black) data of the same duration
    */
   virtual void ReplaceWithDisabled() = 0;
   /**
+   * Replace contents with null data of the same duration
+   */
+  virtual void ReplaceWithNull() = 0;
+  /**
    * Remove all contents, setting duration to 0.
    */
   virtual void Clear() = 0;
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     return 0;
   }
@@ -308,16 +312,20 @@ public:
     }
     mDuration += aDuration;
   }
   void ReplaceWithDisabled() override
   {
     if (GetType() != AUDIO) {
       MOZ_CRASH("Disabling unknown segment type");
     }
+    ReplaceWithNull();
+  }
+  void ReplaceWithNull() override
+  {
     StreamTime duration = GetDuration();
     Clear();
     AppendNullData(duration);
   }
   void Clear() override
   {
     mDuration = 0;
     mChunks.Clear();
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1821,17 +1821,17 @@ MediaStream::SizeOfExcludingThis(MallocS
   // - mListeners - elements
   // - mAudioOutputStream - elements
 
   amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
   amount += mAudioOutputs.ShallowSizeOfExcludingThis(aMallocSizeOf);
   amount += mVideoOutputs.ShallowSizeOfExcludingThis(aMallocSizeOf);
   amount += mListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
   amount += mMainThreadListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  amount += mDisabledTrackIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  amount += mDisabledTracks.ShallowSizeOfExcludingThis(aMallocSizeOf);
   amount += mConsumers.ShallowSizeOfExcludingThis(aMallocSizeOf);
 
   return amount;
 }
 
 size_t
 MediaStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
@@ -2434,53 +2434,85 @@ MediaStream::RunAfterPendingUpdates(alre
   private:
     nsCOMPtr<nsIRunnable> mRunnable;
   };
 
   graph->AppendMessage(MakeUnique<Message>(this, runnable.forget()));
 }
 
 void
-MediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
+MediaStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode)
 {
-  if (aEnabled) {
-    mDisabledTrackIDs.RemoveElement(aTrackID);
+  if (aMode == DisabledTrackMode::ENABLED) {
+    for (int32_t i = mDisabledTracks.Length() - 1; i >= 0; --i) {
+      if (aTrackID == mDisabledTracks[i].mTrackID) {
+        mDisabledTracks.RemoveElementAt(i);
+        return;
+      }
+    }
   } else {
-    if (!mDisabledTrackIDs.Contains(aTrackID)) {
-      mDisabledTrackIDs.AppendElement(aTrackID);
+    for (const DisabledTrack& t : mDisabledTracks) {
+      if (aTrackID == t.mTrackID) {
+        NS_ERROR("Changing disabled track mode for a track is not allowed");
+        return;
+      }
+    }
+    mDisabledTracks.AppendElement(Move(DisabledTrack(aTrackID, aMode)));
+  }
+}
+
+DisabledTrackMode
+MediaStream::GetDisabledTrackMode(TrackID aTrackID)
+{
+  for (const DisabledTrack& t : mDisabledTracks) {
+    if (t.mTrackID == aTrackID) {
+      return t.mMode;
     }
   }
+  return DisabledTrackMode::ENABLED;
 }
 
 void
-MediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
+MediaStream::SetTrackEnabled(TrackID aTrackID, DisabledTrackMode aMode)
 {
   class Message : public ControlMessage {
   public:
-    Message(MediaStream* aStream, TrackID aTrackID, bool aEnabled) :
-      ControlMessage(aStream), mTrackID(aTrackID), mEnabled(aEnabled) {}
+    Message(MediaStream* aStream, TrackID aTrackID, DisabledTrackMode aMode) :
+      ControlMessage(aStream),
+      mTrackID(aTrackID),
+      mMode(aMode) {}
     void Run() override
     {
-      mStream->SetTrackEnabledImpl(mTrackID, mEnabled);
+      mStream->SetTrackEnabledImpl(mTrackID, mMode);
     }
     TrackID mTrackID;
-    bool mEnabled;
+    DisabledTrackMode mMode;
   };
-  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackID, aEnabled));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackID, aMode));
 }
 
 void
 MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment)
 {
-  if (!mDisabledTrackIDs.Contains(aTrackID)) {
+  DisabledTrackMode mode = GetDisabledTrackMode(aTrackID);
+  if (mode == DisabledTrackMode::ENABLED) {
     return;
   }
-  aSegment->ReplaceWithDisabled();
-  if (aRawSegment) {
-    aRawSegment->ReplaceWithDisabled();
+  if (mode == DisabledTrackMode::SILENCE_BLACK) {
+    aSegment->ReplaceWithDisabled();
+    if (aRawSegment) {
+      aRawSegment->ReplaceWithDisabled();
+    }
+  } else if (mode == DisabledTrackMode::SILENCE_FREEZE) {
+    aSegment->ReplaceWithNull();
+    if (aRawSegment) {
+      aRawSegment->ReplaceWithNull();
+    }
+  } else {
+    MOZ_CRASH("Unsupported mode");
   }
 }
 
 void
 MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aListener);
@@ -2888,38 +2920,40 @@ SourceMediaStream::FinishWithLockHeld()
   mMutex.AssertCurrentThreadOwns();
   mUpdateFinished = true;
   if (auto graph = GraphImpl()) {
     graph->EnsureNextIteration();
   }
 }
 
 void
-SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
+SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode)
 {
   {
     MutexAutoLock lock(mMutex);
     for (TrackBound<DirectMediaStreamTrackListener>& l: mDirectTrackListeners) {
       if (l.mTrackID == aTrackID) {
-        bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
-        if (!oldEnabled && aEnabled) {
+        MOZ_ASSERT(l.mListener->mMedia->GetType() == MediaSegment::AUDIO,
+                   "Must implement DisabledTrackMode support for MediaStreamTrackDirectListener for video");
+        bool oldEnabled = GetDisabledTrackMode(aTrackID) == DisabledTrackMode::ENABLED;
+        if (!oldEnabled && aMode == DisabledTrackMode::ENABLED) {
           STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p track %d setting "
                                        "direct listener enabled",
                                        this, aTrackID));
           l.mListener->DecreaseDisabled();
-        } else if (oldEnabled && !aEnabled) {
+        } else if (oldEnabled && aMode != DisabledTrackMode::ENABLED) {
           STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p track %d setting "
                                        "direct listener disabled",
                                        this, aTrackID));
           l.mListener->IncreaseDisabled();
         }
       }
     }
   }
-  MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
+  MediaStream::SetTrackEnabledImpl(aTrackID, aMode);
 }
 
 void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -180,16 +180,34 @@ enum TrackEventCommand : uint32_t;
 template<typename Listener>
 struct TrackBound
 {
   RefPtr<Listener> mListener;
   TrackID mTrackID;
 };
 
 /**
+ * Describes how a track should be disabled.
+ *
+ * ENABLED        Not disabled.
+ * SILENCE_BLACK  Audio data is turned into silence, video frames are made black.
+ * SILENCE_FREEZE Audio data is turned into silence, video freezes at last frame.
+ */
+enum class DisabledTrackMode
+{
+  ENABLED, SILENCE_BLACK, SILENCE_FREEZE
+};
+struct DisabledTrack {
+  DisabledTrack(TrackID aTrackID, DisabledTrackMode aMode)
+    : mTrackID(aTrackID), mMode(aMode) {}
+  TrackID mTrackID;
+  DisabledTrackMode mMode;
+};
+
+/**
  * A stream of synchronized audio and video data. All (not blocked) streams
  * progress at the same rate --- "real time". Streams cannot seek. The only
  * operation readers can perform on a stream is to read the next data.
  *
  * Consumers of a stream can be reading from it at different offsets, but that
  * should only happen due to the order in which consumers are being run.
  * Those offsets must not diverge in the long term, otherwise we would require
  * unbounded buffering.
@@ -333,17 +351,17 @@ public:
    * the source cannot be found, or when the listener had already been removed
    * does nothing.
    */
   virtual void RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
                                          TrackID aTrackID);
 
   // A disabled track has video replaced by black, and audio replaced by
   // silence.
-  void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
+  void SetTrackEnabled(TrackID aTrackID, DisabledTrackMode aMode);
 
   // Finish event will be notified by calling methods of aListener. It is the
   // responsibility of the caller to remove aListener before it is destroyed.
   void AddMainThreadListener(MainThreadMediaStreamListener* aListener);
   // It's safe to call this even if aListener is not currently a listener;
   // the call will be ignored.
   void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener)
   {
@@ -437,17 +455,18 @@ public:
   virtual void AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener,
                                     TrackID aTrackID);
   virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
                                        TrackID aTrackID);
   virtual void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                           TrackID aTrackID);
   virtual void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                              TrackID aTrackID);
-  virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
+  virtual void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode);
+  DisabledTrackMode GetDisabledTrackMode(TrackID aTrackID);
 
   void AddConsumer(MediaInputPort* aPort)
   {
     mConsumers.AppendElement(aPort);
   }
   void RemoveConsumer(MediaInputPort* aPort)
   {
     mConsumers.RemoveElement(aPort);
@@ -593,17 +612,20 @@ protected:
   nsTArray<AudioOutput> mAudioOutputs;
   nsTArray<TrackBound<MediaStreamVideoSink>> mVideoOutputs;
   // We record the last played video frame to avoid playing the frame again
   // with a different frame id.
   VideoFrame mLastPlayedVideoFrame;
   nsTArray<RefPtr<MediaStreamListener> > mListeners;
   nsTArray<TrackBound<MediaStreamTrackListener>> mTrackListeners;
   nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
-  nsTArray<TrackID> mDisabledTrackIDs;
+  // List of disabled TrackIDs and their associated disabled mode.
+  // They can either by disabled by frames being replaced by black, or by
+  // retaining the previous frame.
+  nsTArray<DisabledTrack> mDisabledTracks;
 
   // GraphTime at which this stream starts blocking.
   // This is only valid up to mStateComputedTime. The stream is considered to
   // have not been blocked before mCurrentTime (its mTracksStartTime is increased
   // as necessary to account for that time instead).
   GraphTime mStartBlocking;
 
   // MediaInputPorts to which this is connected
@@ -777,17 +799,17 @@ public:
   void FinishWithLockHeld();
   void Finish()
   {
     MutexAutoLock lock(mMutex);
     FinishWithLockHeld();
   }
 
   // Overriding allows us to hold the mMutex lock while changing the track enable status
-  void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override;
+  void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) override;
 
   // Overriding allows us to ensure mMutex is locked while changing the track enable status
   void
   ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment,
                       MediaSegment* aRawSegment = nullptr) override {
     mMutex.AssertCurrentThreadOwns();
     MediaStream::ApplyTrackDisabling(aTrackID, aSegment, aRawSegment);
   }
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -207,17 +207,18 @@ MediaStreamTrack::GetId(nsAString& aID) 
 
 void
 MediaStreamTrack::SetEnabled(bool aEnabled)
 {
   LOG(LogLevel::Info, ("MediaStreamTrack %p %s",
                        this, aEnabled ? "Enabled" : "Disabled"));
 
   mEnabled = aEnabled;
-  GetOwnedStream()->SetTrackEnabled(mTrackID, aEnabled);
+  GetOwnedStream()->SetTrackEnabled(mTrackID, mEnabled ? DisabledTrackMode::ENABLED
+                                                       : DisabledTrackMode::SILENCE_BLACK);
 }
 
 void
 MediaStreamTrack::Stop()
 {
   LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
 
   if (Ended()) {
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -224,17 +224,17 @@ TrackUnionStream::TrackUnionStream() :
     for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) {
       TrackBound<DirectMediaStreamTrackListener>& bound =
         mPendingDirectTrackListeners[i];
       if (bound.mTrackID != map->mOutputTrackID) {
         continue;
       }
       MediaStream* source = map->mInputPort->GetSource();
       map->mOwnedDirectListeners.AppendElement(bound.mListener);
-      if (mDisabledTrackIDs.Contains(bound.mTrackID)) {
+      if (GetDisabledTrackMode(bound.mTrackID) != DisabledTrackMode::ENABLED) {
         bound.mListener->IncreaseDisabled();
       }
       STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener "
                                    "%p for track %d. Forwarding to input "
                                    "stream %p track %d.",
                                    this, bound.mListener.get(), bound.mTrackID,
                                    source, map->mInputTrackID));
       source->AddDirectTrackListenerImpl(bound.mListener.forget(),
@@ -340,38 +340,39 @@ TrackUnionStream::TrackUnionStream() :
         }
         b.mListener->NotifyQueuedChanges(Graph(), outputStart, *segment);
       }
       outputTrack->GetSegment()->AppendFrom(segment);
     }
   }
 
 void
-TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
+TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) {
+  bool enabled = aMode == DisabledTrackMode::ENABLED;
   for (TrackMapEntry& entry : mTrackMap) {
     if (entry.mOutputTrackID == aTrackID) {
       STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s",
-                                   this, aTrackID, aEnabled ? "enabled" : "disabled"));
+                                   this, aTrackID, enabled ? "enabled" : "disabled"));
       for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) {
-        bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
-        if (!oldEnabled && aEnabled) {
+        bool oldEnabled = GetDisabledTrackMode(aTrackID) == DisabledTrackMode::ENABLED;
+        if (!oldEnabled && enabled) {
           STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
                                        "direct listener enabled",
                                        this, aTrackID));
           listener->DecreaseDisabled();
-        } else if (oldEnabled && !aEnabled) {
+        } else if (oldEnabled && !enabled) {
           STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
                                        "direct listener disabled",
                                        this, aTrackID));
           listener->IncreaseDisabled();
         }
       }
     }
   }
-  MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
+  MediaStream::SetTrackEnabledImpl(aTrackID, aMode);
 }
 
 MediaStream*
 TrackUnionStream::GetInputStreamFor(TrackID aTrackID)
 {
   for (TrackMapEntry& entry : mTrackMap) {
     if (entry.mOutputTrackID == aTrackID && entry.mInputPort) {
       return entry.mInputPort->GetSource();
@@ -403,17 +404,17 @@ TrackUnionStream::AddDirectTrackListener
     if (entry.mOutputTrackID == aTrackID) {
       MediaStream* source = entry.mInputPort->GetSource();
       STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener "
                                    "%p for track %d. Forwarding to input "
                                    "stream %p track %d.",
                                    this, listener.get(), aTrackID, source,
                                    entry.mInputTrackID));
       entry.mOwnedDirectListeners.AppendElement(listener);
-      if (mDisabledTrackIDs.Contains(aTrackID)) {
+      if (GetDisabledTrackMode(aTrackID) != DisabledTrackMode::ENABLED) {
         listener->IncreaseDisabled();
       }
       source->AddDirectTrackListenerImpl(listener.forget(),
                                          entry.mInputTrackID);
       return;
     }
   }
 
@@ -435,17 +436,17 @@ TrackUnionStream::RemoveDirectTrackListe
     for (size_t i = 0; i < entry.mOwnedDirectListeners.Length(); ++i) {
       if (entry.mOwnedDirectListeners[i] == aListener) {
         STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing direct "
                                      "listener %p for track %d, forwarding "
                                      "to input stream %p track %d",
                                      this, aListener, aTrackID,
                                      entry.mInputPort->GetSource(),
                                      entry.mInputTrackID));
-        if (mDisabledTrackIDs.Contains(aTrackID)) {
+        if (GetDisabledTrackMode(aTrackID) != DisabledTrackMode::ENABLED) {
           // Reset the listener's state.
           aListener->DecreaseDisabled();
         }
         entry.mOwnedDirectListeners.RemoveElementAt(i);
         break;
       }
     }
     // Forward to the input
--- a/dom/media/TrackUnionStream.h
+++ b/dom/media/TrackUnionStream.h
@@ -20,17 +20,17 @@ public:
   explicit TrackUnionStream();
 
   virtual TrackUnionStream* AsTrackUnionStream() override { return this; }
   friend class DOMMediaStream;
 
   void RemoveInput(MediaInputPort* aPort) override;
   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
-  void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override;
+  void SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) override;
 
   MediaStream* GetInputStreamFor(TrackID aTrackID) override;
   TrackID GetInputTrackIDFor(TrackID aTrackID) override;
 
   friend class MediaStreamGraphImpl;
 
 protected:
   // Only non-ended tracks are allowed to persist in this map.
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -519,17 +519,19 @@ AudioDestinationNode::WindowSuspendChang
     return NS_OK;
   }
 
   mAudioChannelSuspended = suspended;
   Context()->DispatchTrustedEvent(!suspended ?
     NS_LITERAL_STRING("mozinterruptend") :
     NS_LITERAL_STRING("mozinterruptbegin"));
 
-  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !suspended);
+  DisabledTrackMode disabledMode = suspended ? DisabledTrackMode::SILENCE_BLACK
+                                             : DisabledTrackMode::ENABLED;
+  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, disabledMode);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowAudioCaptureChanged(bool aCapture)
 {
   MOZ_ASSERT(mAudioChannelAgent);
 
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -577,17 +577,17 @@ AudioNodeStream::ProcessInput(GraphTime 
     }
     if (finished) {
       mMarkAsFinishedAfterThisBlock = true;
       if (mIsActive) {
         ScheduleCheckForInactive();
       }
     }
 
-    if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
+    if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
       for (uint32_t i = 0; i < outputCount; ++i) {
         mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
 
   if (!mFinished) {
     // Don't output anything while finished
@@ -616,17 +616,17 @@ AudioNodeStream::ProduceOutputBeforeInpu
   MOZ_ASSERT(mLastChunks.Length() == 1);
 
   if (!mIsActive) {
     mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
   } else {
     mEngine->ProduceBlockBeforeInput(this, aFrom, &mLastChunks[0]);
     NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
                  "Invalid WebAudio chunk size");
-    if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
+    if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
       mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   }
 }
 
 void
 AudioNodeStream::AdvanceOutputSegment()
 {