Bug 1233650. Part 1 - extract OutputStreamManager to its own file. r=roc. draft
authorJW Wang <jwwang@mozilla.com>
Thu, 24 Dec 2015 10:14:15 +0800
changeset 317473 5cb857abfd6dcc6023d7b5884dbd0fc9654910b4
parent 317472 9d4640cd0c80e16b57f68b0abb6e4fdc2aa98030
child 317474 891c023f4f2fd3b9da4e07de97cc9378a7f742eb
push id8701
push userjwwang@mozilla.com
push dateThu, 24 Dec 2015 02:14:36 +0000
reviewersroc
bugs1233650
milestone46.0a1
Bug 1233650. Part 1 - extract OutputStreamManager to its own file. r=roc.
dom/media/mediasink/DecodedStream.cpp
dom/media/mediasink/DecodedStream.h
dom/media/mediasink/OutputStreamManager.cpp
dom/media/mediasink/OutputStreamManager.h
dom/media/mediasink/moz.build
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -179,125 +179,16 @@ void
 DecodedStreamData::SetPlaying(bool aPlaying)
 {
   if (mPlaying != aPlaying) {
     mPlaying = aPlaying;
     UpdateStreamSuspended(mStream, !mPlaying);
   }
 }
 
-OutputStreamData::~OutputStreamData()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  // Break the connection to the input stream if necessary.
-  if (mPort) {
-    mPort->Destroy();
-  }
-}
-
-void
-OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
-{
-  mOwner = aOwner;
-  mStream = aStream;
-}
-
-void
-OutputStreamData::Connect(MediaStream* aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mPort, "Already connected?");
-  MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
-
-  mPort = mStream->AllocateInputPort(aStream);
-}
-
-bool
-OutputStreamData::Disconnect()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // During cycle collection, DOMMediaStream can be destroyed and send
-  // its Destroy message before this decoder is destroyed. So we have to
-  // be careful not to send any messages after the Destroy().
-  if (mStream->IsDestroyed()) {
-    return false;
-  }
-
-  // Disconnect the existing port if necessary.
-  if (mPort) {
-    mPort->Destroy();
-    mPort = nullptr;
-  }
-  return true;
-}
-
-MediaStreamGraph*
-OutputStreamData::Graph() const
-{
-  return mStream->Graph();
-}
-
-void
-OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  // All streams must belong to the same graph.
-  MOZ_ASSERT(!Graph() || Graph() == aStream->Graph());
-
-  // Ensure that aStream finishes the moment mDecodedStream does.
-  if (aFinishWhenEnded) {
-    aStream->SetAutofinish(true);
-  }
-
-  OutputStreamData* p = mStreams.AppendElement();
-  p->Init(this, aStream);
-
-  // Connect to the input stream if we have one. Otherwise the output stream
-  // will be connected in Connect().
-  if (mInputStream) {
-    p->Connect(mInputStream);
-  }
-}
-
-void
-OutputStreamManager::Remove(MediaStream* aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
-    if (mStreams[i].Equals(aStream)) {
-      mStreams.RemoveElementAt(i);
-      break;
-    }
-  }
-}
-
-void
-OutputStreamManager::Connect(MediaStream* aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mInputStream = aStream;
-  for (auto&& os : mStreams) {
-    os.Connect(aStream);
-  }
-}
-
-void
-OutputStreamManager::Disconnect()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mInputStream = nullptr;
-  for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
-    if (!mStreams[i].Disconnect()) {
-      // Probably the DOMMediaStream was GCed. Clean up.
-      mStreams.RemoveElementAt(i);
-    }
-  }
-}
-
 DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
                              MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue)
   : mOwnerThread(aOwnerThread)
   , mPlaying(false)
   , mSameOrigin(false)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/DecodedStream.h
@@ -2,97 +2,37 @@
 /* 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/. */
 
 #ifndef DecodedStream_h_
 #define DecodedStream_h_
 
-#include "nsTArray.h"
 #include "MediaEventSource.h"
 #include "MediaInfo.h"
 #include "MediaSink.h"
+#include "OutputStreamManager.h"
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
-class DecodedStream;
 class DecodedStreamData;
 class MediaData;
-class MediaInputPort;
 class MediaStream;
-class MediaStreamGraph;
-class OutputStreamManager;
 class ProcessedMediaStream;
 class TimeStamp;
 
 template <class T> class MediaQueue;
 
-class OutputStreamData {
-public:
-  ~OutputStreamData();
-  void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
-
-  // Connect mStream to the input stream.
-  void Connect(MediaStream* aStream);
-  // Disconnect mStream from its input stream.
-  // Return false is mStream is already destroyed, otherwise true.
-  bool Disconnect();
-  // Return true if aStream points to the same object as mStream.
-  // Used by OutputStreamManager to remove an output stream.
-  bool Equals(MediaStream* aStream)
-  {
-    return mStream == aStream;
-  }
-  // Return the graph mStream belongs to.
-  MediaStreamGraph* Graph() const;
-
-private:
-  OutputStreamManager* mOwner;
-  RefPtr<ProcessedMediaStream> mStream;
-  // mPort connects our mStream to an input stream.
-  RefPtr<MediaInputPort> mPort;
-};
-
-class OutputStreamManager {
-public:
-  // Add the output stream to the collection.
-  void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
-  // Remove the output stream from the collection.
-  void Remove(MediaStream* aStream);
-  // Return true if the collection empty.
-  bool IsEmpty() const
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mStreams.IsEmpty();
-  }
-  // Connect all output streams in the collection to the input stream.
-  void Connect(MediaStream* aStream);
-  // Disconnect all output streams from the input stream.
-  void Disconnect();
-  // Return the graph these streams belong to or null if empty.
-  MediaStreamGraph* Graph() const
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return !IsEmpty() ? mStreams[0].Graph() : nullptr;
-  }
-
-private:
-  // Keep the input stream so we can connect the output streams that
-  // are added after Connect().
-  RefPtr<MediaStream> mInputStream;
-  nsTArray<OutputStreamData> mStreams;
-};
-
 class DecodedStream : public media::MediaSink {
   using media::MediaSink::PlaybackParams;
 
 public:
   DecodedStream(AbstractThread* aOwnerThread,
                 MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue);
 
copy from dom/media/mediasink/DecodedStream.cpp
copy to dom/media/mediasink/OutputStreamManager.cpp
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/OutputStreamManager.cpp
@@ -1,194 +1,19 @@
 /* -*- 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 "mozilla/CheckedInt.h"
-#include "mozilla/gfx/Point.h"
-
-#include "AudioSegment.h"
-#include "DecodedStream.h"
-#include "MediaData.h"
-#include "MediaQueue.h"
 #include "MediaStreamGraph.h"
-#include "SharedBuffer.h"
-#include "VideoSegment.h"
-#include "VideoUtils.h"
+#include "OutputStreamManager.h"
 
 namespace mozilla {
 
-class DecodedStreamGraphListener : public MediaStreamListener {
-  typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
-public:
-  DecodedStreamGraphListener(MediaStream* aStream,
-                             MozPromiseHolder<GenericPromise>&& aPromise)
-    : mMutex("DecodedStreamGraphListener::mMutex")
-    , mStream(aStream)
-    , mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
-  {
-    mFinishPromise = Move(aPromise);
-  }
-
-  void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) override
-  {
-    MutexAutoLock lock(mMutex);
-    if (mStream) {
-      mLastOutputTime = mStream->StreamTimeToMicroseconds(
-          mStream->GraphTimeToStreamTime(aCurrentTime));
-    }
-  }
-
-  void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
-  {
-    if (event == EVENT_FINISHED) {
-      nsCOMPtr<nsIRunnable> event =
-        NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
-    }
-  }
-
-  void DoNotifyFinished()
-  {
-    mFinishPromise.ResolveIfExists(true, __func__);
-  }
-
-  int64_t GetLastOutputTime()
-  {
-    MutexAutoLock lock(mMutex);
-    return mLastOutputTime;
-  }
-
-  void Forget()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mFinishPromise.ResolveIfExists(true, __func__);
-    MutexAutoLock lock(mMutex);
-    mStream = nullptr;
-  }
-
-private:
-  Mutex mMutex;
-  // Members below are protected by mMutex.
-  RefPtr<MediaStream> mStream;
-  int64_t mLastOutputTime; // microseconds
-  // Main thread only.
-  MozPromiseHolder<GenericPromise> mFinishPromise;
-};
-
-static void
-UpdateStreamSuspended(MediaStream* aStream, bool aBlocking)
-{
-  if (NS_IsMainThread()) {
-    if (aBlocking) {
-      aStream->Suspend();
-    } else {
-      aStream->Resume();
-    }
-  } else {
-    nsCOMPtr<nsIRunnable> r;
-    if (aBlocking) {
-      r = NS_NewRunnableMethod(aStream, &MediaStream::Suspend);
-    } else {
-      r = NS_NewRunnableMethod(aStream, &MediaStream::Resume);
-    }
-    AbstractThread::MainThread()->Dispatch(r.forget());
-  }
-}
-
-/*
- * All MediaStream-related data is protected by the decoder's monitor.
- * We have at most one DecodedStreamDaata per MediaDecoder. Its stream
- * is used as the input for each ProcessedMediaStream created by calls to
- * captureStream(UntilEnded). Seeking creates a new source stream, as does
- * replaying after the input as ended. In the latter case, the new source is
- * not connected to streams created by captureStreamUntilEnded.
- */
-class DecodedStreamData {
-public:
-  DecodedStreamData(SourceMediaStream* aStream,
-                    MozPromiseHolder<GenericPromise>&& aPromise);
-  ~DecodedStreamData();
-  int64_t GetPosition() const;
-  void SetPlaying(bool aPlaying);
-
-  /* The following group of fields are protected by the decoder's monitor
-   * and can be read or written on any thread.
-   */
-  // Count of audio frames written to the stream
-  int64_t mAudioFramesWritten;
-  // mNextVideoTime is the end timestamp for the last packet sent to the stream.
-  // Therefore video packets starting at or after this time need to be copied
-  // to the output stream.
-  int64_t mNextVideoTime; // microseconds
-  int64_t mNextAudioTime; // microseconds
-  // The last video image sent to the stream. Useful if we need to replicate
-  // the image.
-  RefPtr<layers::Image> mLastVideoImage;
-  gfx::IntSize mLastVideoImageDisplaySize;
-  // This is set to true when the stream is initialized (audio and
-  // video tracks added).
-  bool mStreamInitialized;
-  bool mHaveSentFinish;
-  bool mHaveSentFinishAudio;
-  bool mHaveSentFinishVideo;
-
-  // The decoder is responsible for calling Destroy() on this stream.
-  const RefPtr<SourceMediaStream> mStream;
-  RefPtr<DecodedStreamGraphListener> mListener;
-  bool mPlaying;
-  // True if we need to send a compensation video frame to ensure the
-  // StreamTime going forward.
-  bool mEOSVideoCompensation;
-};
-
-DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream,
-                                     MozPromiseHolder<GenericPromise>&& aPromise)
-  : mAudioFramesWritten(0)
-  , mNextVideoTime(-1)
-  , mNextAudioTime(-1)
-  , mStreamInitialized(false)
-  , mHaveSentFinish(false)
-  , mHaveSentFinishAudio(false)
-  , mHaveSentFinishVideo(false)
-  , mStream(aStream)
-  , mPlaying(true)
-  , mEOSVideoCompensation(false)
-{
-  // DecodedStreamGraphListener will resolve this promise.
-  mListener = new DecodedStreamGraphListener(mStream, Move(aPromise));
-  mStream->AddListener(mListener);
-
-  // mPlaying is initially true because MDSM won't start playback until playing
-  // becomes true. This is consistent with the settings of AudioSink.
-}
-
-DecodedStreamData::~DecodedStreamData()
-{
-  mListener->Forget();
-  mStream->Destroy();
-}
-
-int64_t
-DecodedStreamData::GetPosition() const
-{
-  return mListener->GetLastOutputTime();
-}
-
-void
-DecodedStreamData::SetPlaying(bool aPlaying)
-{
-  if (mPlaying != aPlaying) {
-    mPlaying = aPlaying;
-    UpdateStreamSuspended(mStream, !mPlaying);
-  }
-}
-
 OutputStreamData::~OutputStreamData()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Break the connection to the input stream if necessary.
   if (mPort) {
     mPort->Destroy();
   }
 }
@@ -225,16 +50,22 @@ OutputStreamData::Disconnect()
   // Disconnect the existing port if necessary.
   if (mPort) {
     mPort->Destroy();
     mPort = nullptr;
   }
   return true;
 }
 
+bool
+OutputStreamData::Equals(MediaStream* aStream) const
+{
+  return mStream == aStream;
+}
+
 MediaStreamGraph*
 OutputStreamData::Graph() const
 {
   return mStream->Graph();
 }
 
 void
 OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
@@ -288,609 +119,9 @@ OutputStreamManager::Disconnect()
   for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
     if (!mStreams[i].Disconnect()) {
       // Probably the DOMMediaStream was GCed. Clean up.
       mStreams.RemoveElementAt(i);
     }
   }
 }
 
-DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
-                             MediaQueue<MediaData>& aAudioQueue,
-                             MediaQueue<MediaData>& aVideoQueue)
-  : mOwnerThread(aOwnerThread)
-  , mPlaying(false)
-  , mSameOrigin(false)
-  , mAudioQueue(aAudioQueue)
-  , mVideoQueue(aVideoQueue)
-{
-}
-
-DecodedStream::~DecodedStream()
-{
-  MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
-}
-
-const media::MediaSink::PlaybackParams&
-DecodedStream::GetPlaybackParams() const
-{
-  AssertOwnerThread();
-  return mParams;
-}
-
-void
-DecodedStream::SetPlaybackParams(const PlaybackParams& aParams)
-{
-  AssertOwnerThread();
-  mParams = aParams;
-}
-
-RefPtr<GenericPromise>
-DecodedStream::OnEnded(TrackType aType)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(mStartTime.isSome());
-
-  if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio()) {
-    // TODO: we should return a promise which is resolved when the audio track
-    // is finished. For now this promise is resolved when the whole stream is
-    // finished.
-    return mFinishPromise;
-  } else if (aType == TrackInfo::kVideoTrack && mInfo.HasVideo()) {
-    return mFinishPromise;
-  }
-  return nullptr;
-}
-
-void
-DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
-
-  mStartTime.emplace(aStartTime);
-  mInfo = aInfo;
-  mPlaying = true;
-  ConnectListener();
-
-  class R : public nsRunnable {
-    typedef MozPromiseHolder<GenericPromise> Promise;
-    typedef void(DecodedStream::*Method)(Promise&&);
-  public:
-    R(DecodedStream* aThis, Method aMethod, Promise&& aPromise)
-      : mThis(aThis), mMethod(aMethod)
-    {
-      mPromise = Move(aPromise);
-    }
-    NS_IMETHOD Run() override
-    {
-      (mThis->*mMethod)(Move(mPromise));
-      return NS_OK;
-    }
-  private:
-    RefPtr<DecodedStream> mThis;
-    Method mMethod;
-    Promise mPromise;
-  };
-
-  MozPromiseHolder<GenericPromise> promise;
-  mFinishPromise = promise.Ensure(__func__);
-  nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::CreateData, Move(promise));
-  AbstractThread::MainThread()->Dispatch(r.forget());
-}
-
-void
-DecodedStream::Stop()
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(mStartTime.isSome(), "playback not started.");
-
-  mStartTime.reset();
-  DisconnectListener();
-  mFinishPromise = nullptr;
-
-  // Clear mData immediately when this playback session ends so we won't
-  // send data to the wrong stream in SendData() in next playback session.
-  DestroyData(Move(mData));
-}
-
-bool
-DecodedStream::IsStarted() const
-{
-  AssertOwnerThread();
-  return mStartTime.isSome();
-}
-
-bool
-DecodedStream::IsPlaying() const
-{
-  AssertOwnerThread();
-  return IsStarted() && mPlaying;
-}
-
-void
-DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData)
-{
-  AssertOwnerThread();
-
-  if (!aData) {
-    return;
-  }
-
-  DecodedStreamData* data = aData.release();
-  RefPtr<DecodedStream> self = this;
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-    self->mOutputStreamManager.Disconnect();
-    delete data;
-  });
-  AbstractThread::MainThread()->Dispatch(r.forget());
-}
-
-void
-DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // No need to create a source stream when there are no output streams. This
-  // happens when RemoveOutput() is called immediately after StartPlayback().
-  if (!mOutputStreamManager.Graph()) {
-    // Resolve the promise to indicate the end of playback.
-    aPromise.Resolve(true, __func__);
-    return;
-  }
-
-  auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
-  auto data = new DecodedStreamData(source, Move(aPromise));
-  mOutputStreamManager.Connect(data->mStream);
-
-  class R : public nsRunnable {
-    typedef void(DecodedStream::*Method)(UniquePtr<DecodedStreamData>);
-  public:
-    R(DecodedStream* aThis, Method aMethod, DecodedStreamData* aData)
-      : mThis(aThis), mMethod(aMethod), mData(aData) {}
-    NS_IMETHOD Run() override
-    {
-      (mThis->*mMethod)(Move(mData));
-      return NS_OK;
-    }
-  private:
-    virtual ~R()
-    {
-      // mData is not transferred when dispatch fails and Run() is not called.
-      // We need to dispatch a task to ensure DecodedStreamData is destroyed
-      // properly on the main thread.
-      if (mData) {
-        DecodedStreamData* data = mData.release();
-        RefPtr<DecodedStream> self = mThis.forget();
-        nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-          self->mOutputStreamManager.Disconnect();
-          delete data;
-        });
-        // We are in tail dispatching phase. Don't call
-        // AbstractThread::MainThread()->Dispatch() to avoid reentrant
-        // AutoTaskDispatcher.
-        NS_DispatchToMainThread(r.forget());
-      }
-    }
-    RefPtr<DecodedStream> mThis;
-    Method mMethod;
-    UniquePtr<DecodedStreamData> mData;
-  };
-
-  // Post a message to ensure |mData| is only updated on the worker thread.
-  // Note this could fail when MDSM begin to shut down the worker thread.
-  nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::OnDataCreated, data);
-  mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
-}
-
-bool
-DecodedStream::HasConsumers() const
-{
-  return !mOutputStreamManager.IsEmpty();
-}
-
-void
-DecodedStream::OnDataCreated(UniquePtr<DecodedStreamData> aData)
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(!mData, "Already created.");
-
-  // Start to send data to the stream immediately
-  if (mStartTime.isSome()) {
-    aData->SetPlaying(mPlaying);
-    mData = Move(aData);
-    SendData();
-    return;
-  }
-
-  // Playback has ended. Destroy aData which is not needed anymore.
-  DestroyData(Move(aData));
-}
-
-void
-DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
-{
-  mOutputStreamManager.Add(aStream, aFinishWhenEnded);
-}
-
-void
-DecodedStream::RemoveOutput(MediaStream* aStream)
-{
-  mOutputStreamManager.Remove(aStream);
-}
-
-void
-DecodedStream::SetPlaying(bool aPlaying)
-{
-  AssertOwnerThread();
-
-  // Resume/pause matters only when playback started.
-  if (mStartTime.isNothing()) {
-    return;
-  }
-
-  mPlaying = aPlaying;
-  if (mData) {
-    mData->SetPlaying(aPlaying);
-  }
-}
-
-void
-DecodedStream::SetVolume(double aVolume)
-{
-  AssertOwnerThread();
-  mParams.mVolume = aVolume;
-}
-
-void
-DecodedStream::SetPlaybackRate(double aPlaybackRate)
-{
-  AssertOwnerThread();
-  mParams.mPlaybackRate = aPlaybackRate;
-}
-
-void
-DecodedStream::SetPreservesPitch(bool aPreservesPitch)
-{
-  AssertOwnerThread();
-  mParams.mPreservesPitch = aPreservesPitch;
-}
-
-void
-DecodedStream::SetSameOrigin(bool aSameOrigin)
-{
-  AssertOwnerThread();
-  mSameOrigin = aSameOrigin;
-}
-
-void
-DecodedStream::InitTracks()
-{
-  AssertOwnerThread();
-
-  if (mData->mStreamInitialized) {
-    return;
-  }
-
-  SourceMediaStream* sourceStream = mData->mStream;
-
-  if (mInfo.HasAudio()) {
-    TrackID audioTrackId = mInfo.mAudio.mTrackId;
-    AudioSegment* audio = new AudioSegment();
-    sourceStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
-                                SourceMediaStream::ADDTRACK_QUEUED);
-    mData->mNextAudioTime = mStartTime.ref();
-  }
-
-  if (mInfo.HasVideo()) {
-    TrackID videoTrackId = mInfo.mVideo.mTrackId;
-    VideoSegment* video = new VideoSegment();
-    sourceStream->AddTrack(videoTrackId, 0, video,
-                           SourceMediaStream::ADDTRACK_QUEUED);
-    mData->mNextVideoTime = mStartTime.ref();
-  }
-
-  sourceStream->FinishAddTracks();
-  mData->mStreamInitialized = true;
-}
-
-static void
-SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
-                MediaData* aData, AudioSegment* aOutput,
-                uint32_t aRate, double aVolume)
-{
-  // The amount of audio frames that is used to fuzz rounding errors.
-  static const int64_t AUDIO_FUZZ_FRAMES = 1;
-
-  MOZ_ASSERT(aData);
-  AudioData* audio = aData->As<AudioData>();
-  // This logic has to mimic AudioSink closely to make sure we write
-  // the exact same silences
-  CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten +
-                                    UsecsToFrames(aStartTime, aRate);
-  CheckedInt64 frameOffset = UsecsToFrames(audio->mTime, aRate);
-
-  if (!audioWrittenOffset.isValid() ||
-      !frameOffset.isValid() ||
-      // ignore packet that we've already processed
-      audio->GetEndTime() <= aStream->mNextAudioTime) {
-    return;
-  }
-
-  if (audioWrittenOffset.value() + AUDIO_FUZZ_FRAMES < frameOffset.value()) {
-    int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value();
-    // Write silence to catch up
-    AudioSegment silence;
-    silence.InsertNullDataAtStart(silentFrames);
-    aStream->mAudioFramesWritten += silentFrames;
-    audioWrittenOffset += silentFrames;
-    aOutput->AppendFrom(&silence);
-  }
-
-  // Always write the whole sample without truncation to be consistent with
-  // DecodedAudioDataSink::PlayFromAudioQueue()
-  audio->EnsureAudioBuffer();
-  RefPtr<SharedBuffer> buffer = audio->mAudioBuffer;
-  AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
-  nsAutoTArray<const AudioDataValue*, 2> channels;
-  for (uint32_t i = 0; i < audio->mChannels; ++i) {
-    channels.AppendElement(bufferData + i * audio->mFrames);
-  }
-  aOutput->AppendFrames(buffer.forget(), channels, audio->mFrames);
-  aStream->mAudioFramesWritten += audio->mFrames;
-  aOutput->ApplyVolume(aVolume);
-
-  aStream->mNextAudioTime = audio->GetEndTime();
-}
-
-void
-DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
-{
-  AssertOwnerThread();
-
-  if (!mInfo.HasAudio()) {
-    return;
-  }
-
-  AudioSegment output;
-  uint32_t rate = mInfo.mAudio.mRate;
-  nsAutoTArray<RefPtr<MediaData>,10> audio;
-  TrackID audioTrackId = mInfo.mAudio.mTrackId;
-  SourceMediaStream* sourceStream = mData->mStream;
-
-  // It's OK to hold references to the AudioData because AudioData
-  // is ref-counted.
-  mAudioQueue.GetElementsAfter(mData->mNextAudioTime, &audio);
-  for (uint32_t i = 0; i < audio.Length(); ++i) {
-    SendStreamAudio(mData.get(), mStartTime.ref(), audio[i], &output, rate, aVolume);
-  }
-
-  if (!aIsSameOrigin) {
-    output.ReplaceWithDisabled();
-  }
-
-  // |mNextAudioTime| is updated as we process each audio sample in
-  // SendStreamAudio(). This is consistent with how |mNextVideoTime|
-  // is updated for video samples.
-  if (output.GetDuration() > 0) {
-    sourceStream->AppendToTrack(audioTrackId, &output);
-  }
-
-  if (mAudioQueue.IsFinished() && !mData->mHaveSentFinishAudio) {
-    sourceStream->EndTrack(audioTrackId);
-    mData->mHaveSentFinishAudio = true;
-  }
-}
-
-static void
-WriteVideoToMediaStream(MediaStream* aStream,
-                        layers::Image* aImage,
-                        int64_t aEndMicroseconds,
-                        int64_t aStartMicroseconds,
-                        const mozilla::gfx::IntSize& aIntrinsicSize,
-                        VideoSegment* aOutput)
-{
-  RefPtr<layers::Image> image = aImage;
-  StreamTime duration =
-      aStream->MicrosecondsToStreamTimeRoundDown(aEndMicroseconds) -
-      aStream->MicrosecondsToStreamTimeRoundDown(aStartMicroseconds);
-  aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize);
-}
-
-static bool
-ZeroDurationAtLastChunk(VideoSegment& aInput)
-{
-  // Get the last video frame's start time in VideoSegment aInput.
-  // If the start time is equal to the duration of aInput, means the last video
-  // frame's duration is zero.
-  StreamTime lastVideoStratTime;
-  aInput.GetLastFrame(&lastVideoStratTime);
-  return lastVideoStratTime == aInput.GetDuration();
-}
-
-void
-DecodedStream::SendVideo(bool aIsSameOrigin)
-{
-  AssertOwnerThread();
-
-  if (!mInfo.HasVideo()) {
-    return;
-  }
-
-  VideoSegment output;
-  TrackID videoTrackId = mInfo.mVideo.mTrackId;
-  nsAutoTArray<RefPtr<MediaData>, 10> video;
-  SourceMediaStream* sourceStream = mData->mStream;
-
-  // It's OK to hold references to the VideoData because VideoData
-  // is ref-counted.
-  mVideoQueue.GetElementsAfter(mData->mNextVideoTime, &video);
-
-  for (uint32_t i = 0; i < video.Length(); ++i) {
-    VideoData* v = video[i]->As<VideoData>();
-
-    if (mData->mNextVideoTime < v->mTime) {
-      // Write last video frame to catch up. mLastVideoImage can be null here
-      // which is fine, it just means there's no video.
-
-      // TODO: |mLastVideoImage| should come from the last image rendered
-      // by the state machine. This will avoid the black frame when capture
-      // happens in the middle of playback (especially in th middle of a
-      // video frame). E.g. if we have a video frame that is 30 sec long
-      // and capture happens at 15 sec, we'll have to append a black frame
-      // that is 15 sec long.
-      WriteVideoToMediaStream(sourceStream, mData->mLastVideoImage, v->mTime,
-          mData->mNextVideoTime, mData->mLastVideoImageDisplaySize, &output);
-      mData->mNextVideoTime = v->mTime;
-    }
-
-    if (mData->mNextVideoTime < v->GetEndTime()) {
-      WriteVideoToMediaStream(sourceStream, v->mImage,
-          v->GetEndTime(), mData->mNextVideoTime, v->mDisplay, &output);
-      mData->mNextVideoTime = v->GetEndTime();
-      mData->mLastVideoImage = v->mImage;
-      mData->mLastVideoImageDisplaySize = v->mDisplay;
-    }
-  }
-
-  // Check the output is not empty.
-  if (output.GetLastFrame()) {
-    mData->mEOSVideoCompensation = ZeroDurationAtLastChunk(output);
-  }
-
-  if (!aIsSameOrigin) {
-    output.ReplaceWithDisabled();
-  }
-
-  if (output.GetDuration() > 0) {
-    sourceStream->AppendToTrack(videoTrackId, &output);
-  }
-
-  if (mVideoQueue.IsFinished() && !mData->mHaveSentFinishVideo) {
-    if (mData->mEOSVideoCompensation) {
-      VideoSegment endSegment;
-      // Calculate the deviation clock time from DecodedStream.
-      int64_t deviation_usec = sourceStream->StreamTimeToMicroseconds(1);
-      WriteVideoToMediaStream(sourceStream, mData->mLastVideoImage,
-          mData->mNextVideoTime + deviation_usec, mData->mNextVideoTime,
-          mData->mLastVideoImageDisplaySize, &endSegment);
-      mData->mNextVideoTime += deviation_usec;
-      MOZ_ASSERT(endSegment.GetDuration() > 0);
-      if (!aIsSameOrigin) {
-        endSegment.ReplaceWithDisabled();
-      }
-      sourceStream->AppendToTrack(videoTrackId, &endSegment);
-    }
-    sourceStream->EndTrack(videoTrackId);
-    mData->mHaveSentFinishVideo = true;
-  }
-}
-
-void
-DecodedStream::AdvanceTracks()
-{
-  AssertOwnerThread();
-
-  StreamTime endPosition = 0;
-
-  if (mInfo.HasAudio()) {
-    StreamTime audioEnd = mData->mStream->TicksToTimeRoundDown(
-        mInfo.mAudio.mRate, mData->mAudioFramesWritten);
-    endPosition = std::max(endPosition, audioEnd);
-  }
-
-  if (mInfo.HasVideo()) {
-    StreamTime videoEnd = mData->mStream->MicrosecondsToStreamTimeRoundDown(
-        mData->mNextVideoTime - mStartTime.ref());
-    endPosition = std::max(endPosition, videoEnd);
-  }
-
-  if (!mData->mHaveSentFinish) {
-    mData->mStream->AdvanceKnownTracksTime(endPosition);
-  }
-}
-
-void
-DecodedStream::SendData()
-{
-  AssertOwnerThread();
-  MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
-
-  // Not yet created on the main thread. MDSM will try again later.
-  if (!mData) {
-    return;
-  }
-
-  // Nothing to do when the stream is finished.
-  if (mData->mHaveSentFinish) {
-    return;
-  }
-
-  InitTracks();
-  SendAudio(mParams.mVolume, mSameOrigin);
-  SendVideo(mSameOrigin);
-  AdvanceTracks();
-
-  bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) &&
-                  (!mInfo.HasVideo() || mVideoQueue.IsFinished());
-
-  if (finished && !mData->mHaveSentFinish) {
-    mData->mHaveSentFinish = true;
-    mData->mStream->Finish();
-  }
-}
-
-int64_t
-DecodedStream::GetEndTime(TrackType aType) const
-{
-  AssertOwnerThread();
-  if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio() && mData) {
-    CheckedInt64 t = mStartTime.ref() +
-      FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate);
-    if (t.isValid()) {
-      return t.value();
-    }
-  } else if (aType == TrackInfo::kVideoTrack && mData) {
-    return mData->mNextVideoTime;
-  }
-  return -1;
-}
-
-int64_t
-DecodedStream::GetPosition(TimeStamp* aTimeStamp) const
-{
-  AssertOwnerThread();
-  // This is only called after MDSM starts playback. So mStartTime is
-  // guaranteed to be something.
-  MOZ_ASSERT(mStartTime.isSome());
-  if (aTimeStamp) {
-    *aTimeStamp = TimeStamp::Now();
-  }
-  return mStartTime.ref() + (mData ? mData->GetPosition() : 0);
-}
-
-void
-DecodedStream::ConnectListener()
-{
-  AssertOwnerThread();
-
-  mAudioPushListener = mAudioQueue.PushEvent().Connect(
-    mOwnerThread, this, &DecodedStream::SendData);
-  mAudioFinishListener = mAudioQueue.FinishEvent().Connect(
-    mOwnerThread, this, &DecodedStream::SendData);
-  mVideoPushListener = mVideoQueue.PushEvent().Connect(
-    mOwnerThread, this, &DecodedStream::SendData);
-  mVideoFinishListener = mVideoQueue.FinishEvent().Connect(
-    mOwnerThread, this, &DecodedStream::SendData);
-}
-
-void
-DecodedStream::DisconnectListener()
-{
-  AssertOwnerThread();
-
-  mAudioPushListener.Disconnect();
-  mVideoPushListener.Disconnect();
-  mAudioFinishListener.Disconnect();
-  mVideoFinishListener.Disconnect();
-}
-
 } // namespace mozilla
copy from dom/media/mediasink/DecodedStream.h
copy to dom/media/mediasink/OutputStreamManager.h
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/OutputStreamManager.h
@@ -1,58 +1,41 @@
 /* -*- 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/. */
 
-#ifndef DecodedStream_h_
-#define DecodedStream_h_
+#ifndef OutputStreamManager_h
+#define OutputStreamManager_h
 
+#include "mozilla/RefPtr.h"
 #include "nsTArray.h"
-#include "MediaEventSource.h"
-#include "MediaInfo.h"
-#include "MediaSink.h"
-
-#include "mozilla/AbstractThread.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
-class DecodedStream;
-class DecodedStreamData;
-class MediaData;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class OutputStreamManager;
 class ProcessedMediaStream;
-class TimeStamp;
-
-template <class T> class MediaQueue;
 
 class OutputStreamData {
 public:
   ~OutputStreamData();
   void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
 
   // Connect mStream to the input stream.
   void Connect(MediaStream* aStream);
   // Disconnect mStream from its input stream.
   // Return false is mStream is already destroyed, otherwise true.
   bool Disconnect();
   // Return true if aStream points to the same object as mStream.
   // Used by OutputStreamManager to remove an output stream.
-  bool Equals(MediaStream* aStream)
-  {
-    return mStream == aStream;
-  }
+  bool Equals(MediaStream* aStream) const;
   // Return the graph mStream belongs to.
   MediaStreamGraph* Graph() const;
 
 private:
   OutputStreamManager* mOwner;
   RefPtr<ProcessedMediaStream> mStream;
   // mPort connects our mStream to an input stream.
   RefPtr<MediaInputPort> mPort;
@@ -83,98 +66,11 @@ public:
 
 private:
   // Keep the input stream so we can connect the output streams that
   // are added after Connect().
   RefPtr<MediaStream> mInputStream;
   nsTArray<OutputStreamData> mStreams;
 };
 
-class DecodedStream : public media::MediaSink {
-  using media::MediaSink::PlaybackParams;
-
-public:
-  DecodedStream(AbstractThread* aOwnerThread,
-                MediaQueue<MediaData>& aAudioQueue,
-                MediaQueue<MediaData>& aVideoQueue);
-
-  // MediaSink functions.
-  const PlaybackParams& GetPlaybackParams() const override;
-  void SetPlaybackParams(const PlaybackParams& aParams) override;
-
-  RefPtr<GenericPromise> OnEnded(TrackType aType) override;
-  int64_t GetEndTime(TrackType aType) const override;
-  int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
-  bool HasUnplayedFrames(TrackType aType) const override
-  {
-    // TODO: implement this.
-    return false;
-  }
-
-  void SetVolume(double aVolume) override;
-  void SetPlaybackRate(double aPlaybackRate) override;
-  void SetPreservesPitch(bool aPreservesPitch) override;
-  void SetPlaying(bool aPlaying) override;
-
-  void Start(int64_t aStartTime, const MediaInfo& aInfo) override;
-  void Stop() override;
-  bool IsStarted() const override;
-  bool IsPlaying() const override;
-
-  // TODO: fix these functions that don't fit into the interface of MediaSink.
-  void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
-  void RemoveOutput(MediaStream* aStream);
-  void SetSameOrigin(bool aSameOrigin);
-  bool HasConsumers() const;
-
-protected:
-  virtual ~DecodedStream();
-
-private:
-  void CreateData(MozPromiseHolder<GenericPromise>&& aPromise);
-  void DestroyData(UniquePtr<DecodedStreamData> aData);
-  void OnDataCreated(UniquePtr<DecodedStreamData> aData);
-  void InitTracks();
-  void AdvanceTracks();
-  void SendAudio(double aVolume, bool aIsSameOrigin);
-  void SendVideo(bool aIsSameOrigin);
-  void SendData();
-
-  void AssertOwnerThread() const {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  }
-
-  void ConnectListener();
-  void DisconnectListener();
-
-  const RefPtr<AbstractThread> mOwnerThread;
-
-  /*
-   * Main thread only members.
-   */
-  // Data about MediaStreams that are being fed by the decoder.
-  OutputStreamManager mOutputStreamManager;
-
-  /*
-   * Worker thread only members.
-   */
-  UniquePtr<DecodedStreamData> mData;
-  RefPtr<GenericPromise> mFinishPromise;
-
-  bool mPlaying;
-  bool mSameOrigin;
-  PlaybackParams mParams;
-
-  Maybe<int64_t> mStartTime;
-  MediaInfo mInfo;
-
-  MediaQueue<MediaData>& mAudioQueue;
-  MediaQueue<MediaData>& mVideoQueue;
-
-  MediaEventListener mAudioPushListener;
-  MediaEventListener mVideoPushListener;
-  MediaEventListener mAudioFinishListener;
-  MediaEventListener mVideoFinishListener;
-};
-
 } // namespace mozilla
 
-#endif // DecodedStream_h_
+#endif // OutputStreamManager_h
--- a/dom/media/mediasink/moz.build
+++ b/dom/media/mediasink/moz.build
@@ -3,12 +3,13 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'AudioSinkWrapper.cpp',
     'DecodedAudioDataSink.cpp',
     'DecodedStream.cpp',
+    'OutputStreamManager.cpp',
     'VideoSink.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'