Bug 1188268 - Make AudioSink a base class, create DecodedAudioDataSink to act as original AudioSink, and move sink-related files to dom/media/mediasink. r=jwwang, r=cpearce
authorKilik Kuo <kikuo@mozilla.com>
Tue, 18 Aug 2015 11:55:01 +0800
changeset 291030 f4091e0478f86c1adb8fbcce0bddd8346b285e4c
parent 291029 2ffca89296dea559cdfc32820c1271c2e80239a8
child 291031 cb6c34882e41f318dd8cdb993461fdea31f038b6
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang, cpearce
bugs1188268
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1188268 - Make AudioSink a base class, create DecodedAudioDataSink to act as original AudioSink, and move sink-related files to dom/media/mediasink. r=jwwang, r=cpearce
dom/media/AudioSink.cpp
dom/media/AudioSink.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/mediasink/AudioSink.h
dom/media/mediasink/DecodedAudioDataSink.cpp
dom/media/mediasink/DecodedAudioDataSink.h
dom/media/mediasink/moz.build
dom/media/moz.build
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -9,17 +9,17 @@
 #include "mmsystem.h"
 #endif
 
 #include "mozilla/DebugOnly.h"
 #include <stdint.h>
 
 #include "MediaDecoderStateMachine.h"
 #include "MediaTimer.h"
-#include "AudioSink.h"
+#include "mediasink/DecodedAudioDataSink.h"
 #include "nsTArray.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/mozalloc.h"
 #include "VideoUtils.h"
 #include "TimeUnits.h"
 #include "nsDeque.h"
@@ -1067,17 +1067,17 @@ void MediaDecoderStateMachine::MaybeStar
   }
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
 
   mDecoder->DispatchPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
-  StartAudioThread();
+  StartAudioSink();
   StartDecodedStream();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -1456,17 +1456,17 @@ MediaDecoderStateMachine::Seek(SeekTarge
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
   SetState(DECODER_STATE_SEEKING);
   ScheduleStateMachine();
 
   return mPendingSeek.mPromise.Ensure(__func__);
 }
 
-void MediaDecoderStateMachine::StopAudioThread()
+void MediaDecoderStateMachine::StopAudioSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (mAudioSink) {
     DECODER_LOG("Shutdown audio thread");
     mAudioSink->Shutdown();
     mAudioSink = nullptr;
@@ -1749,30 +1749,30 @@ MediaDecoderStateMachine::RequestVideoDa
       ->CompletionPromise()
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnVideoDecoded,
              &MediaDecoderStateMachine::OnVideoNotDecoded));
   }
 }
 
 void
-MediaDecoderStateMachine::StartAudioThread()
+MediaDecoderStateMachine::StartAudioSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (mAudioCaptured) {
     MOZ_ASSERT(!mAudioSink);
     return;
   }
 
   if (HasAudio() && !mAudioSink) {
     mAudioCompleted = false;
-    mAudioSink = new AudioSink(mAudioQueue,
-                               GetMediaTime(), mInfo.mAudio,
-                               mDecoder->GetAudioChannel());
+    mAudioSink = new DecodedAudioDataSink(mAudioQueue,
+                                          GetMediaTime(), mInfo.mAudio,
+                                          mDecoder->GetAudioChannel());
 
     mAudioSinkPromise.Begin(
       mAudioSink->Init()->Then(
         OwnerThread(), __func__, this,
         &MediaDecoderStateMachine::OnAudioSinkComplete,
         &MediaDecoderStateMachine::OnAudioSinkError));
 
     mAudioSink->SetVolume(mVolume);
@@ -2364,22 +2364,22 @@ nsresult MediaDecoderStateMachine::RunSt
 
     case DECODER_STATE_COMPLETED: {
       if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING && IsPlaying()) {
         StopPlayback();
       }
       // Play the remaining media. We want to run AdvanceFrame() at least
       // once to ensure the current playback position is advanced to the
       // end of the media, and so that we update the readyState.
+        MaybeStartPlayback();
       if (VideoQueue().GetSize() > 1 ||
           (HasAudio() && !mAudioCompleted) ||
           (mAudioCaptured && !mDecodedStream->IsFinished()))
       {
         // Start playback if necessary to play the remaining media.
-        MaybeStartPlayback();
         UpdateRenderedVideoFrames();
         NS_ASSERTION(!IsPlaying() ||
                      mLogicallySeeking ||
                      IsStateMachineScheduled(),
                      "Must have timer scheduled");
         return NS_OK;
       }
 
@@ -2404,17 +2404,17 @@ nsresult MediaDecoderStateMachine::RunSt
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
 
         // Stop audio sink after call to AudioEndTime() above, otherwise it will
         // return an incorrect value due to a null mAudioSink.
-        StopAudioThread();
+        StopAudioSink();
         StopDecodedStream();
       }
 
       return NS_OK;
     }
   }
 
   return NS_OK;
@@ -2434,17 +2434,17 @@ MediaDecoderStateMachine::Reset()
   MOZ_ASSERT(IsShutdown() ||
              mState == DECODER_STATE_SEEKING ||
              mState == DECODER_STATE_DORMANT ||
              mState == DECODER_STATE_DECODING_NONE);
 
   // Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue
   // outside of the decoder monitor while we are clearing the queue and causes
   // crash for no samples to be popped.
-  StopAudioThread();
+  StopAudioSink();
   StopDecodedStream();
 
   mVideoFrameEndTime = -1;
   mDecodedVideoEndTime = -1;
   mDecodedAudioEndTime = -1;
   mAudioCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
@@ -3049,17 +3049,17 @@ MediaDecoderStateMachine::AudioEndTime()
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (mAudioSink) {
     return mAudioSink->GetEndTime();
   } else if (mAudioCaptured) {
     return mDecodedStream->AudioEndTime();
   }
   // Don't call this after mAudioSink becomes null since we can't distinguish
-  // "before StartAudioThread" and "after StopAudioThread" where mAudioSink
+  // "before StartAudioSink" and "after StopAudioSink" where mAudioSink
   // is null in both cases.
   MOZ_ASSERT(!mAudioCompleted);
   return -1;
 }
 
 void MediaDecoderStateMachine::OnAudioSinkComplete()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -3130,17 +3130,17 @@ void MediaDecoderStateMachine::DispatchA
 {
   nsRefPtr<MediaDecoderStateMachine> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
   {
     MOZ_ASSERT(self->OnTaskQueue());
     ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
     if (!self->mAudioCaptured) {
       // Stop the audio sink if it's running.
-      self->StopAudioThread();
+      self->StopAudioSink();
       self->mAudioCaptured = true;
       // Start DecodedStream if we are already playing. Otherwise it will be
       // handled in MaybeStartPlayback().
       if (self->IsPlaying()) {
         self->StartDecodedStream();
       }
       self->ScheduleStateMachine();
     }
@@ -3155,17 +3155,17 @@ void MediaDecoderStateMachine::DispatchA
   {
     MOZ_ASSERT(self->OnTaskQueue());
     ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
     if (self->mAudioCaptured) {
       self->StopDecodedStream();
       // Start again the audio sink.
       self->mAudioCaptured = false;
       if (self->IsPlaying()) {
-        self->StartAudioThread();
+        self->StartAudioSink();
       }
       self->ScheduleStateMachine();
     }
   });
   OwnerThread()->Dispatch(r.forget());
 }
 
 void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -94,18 +94,21 @@ hardware (via AudioStream).
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaTimer.h"
 #include "DecodedStream.h"
 #include "ImageContainer.h"
 
 namespace mozilla {
 
+namespace media {
+class AudioSink;
+}
+
 class AudioSegment;
-class AudioSink;
 class TaskQueue;
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 extern PRLogModuleInfo* gMediaSampleLog;
 
 /*
   The state machine class. This manages the decoding and seeking in the
   MediaDecoderReader on the decode task queue, and A/V sync on the shared
@@ -505,23 +508,25 @@ protected:
 
   // If we have video, display a video frame if it's time for display has
   // arrived, otherwise sleep until it's time for the next frame. Update the
   // current frame time as appropriate, and trigger ready state update.  The
   // decoder monitor must be held with exactly one lock count. Called on the
   // state machine thread.
   void UpdateRenderedVideoFrames();
 
-  // Stops the audio thread. The decoder monitor must be held with exactly
-  // one lock count. Called on the state machine thread.
-  void StopAudioThread();
+  // Stops the audio sink and shut it down.
+  // The decoder monitor must be held with exactly one lock count.
+  // Called on the state machine thread.
+  void StopAudioSink();
 
-  // Starts the audio thread. The decoder monitor must be held with exactly
-  // one lock count. Called on the state machine thread.
-  void StartAudioThread();
+  // Create and start the audio sink.
+  // The decoder monitor must be held with exactly one lock count.
+  // Called on the state machine thread.
+  void StartAudioSink();
 
   void StopDecodedStream();
 
   void StartDecodedStream();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
@@ -992,17 +997,17 @@ private:
 
   // The position that we're currently seeking to.
   SeekJob mCurrentSeek;
 
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
   // The audio sink resource.  Used on state machine and audio threads.
-  RefPtr<AudioSink> mAudioSink;
+  RefPtr<media::AudioSink> mAudioSink;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the state machine's constructor.
   nsRefPtr<MediaDecoderReader> mReader;
 
   // The end time of the last audio frame that's been pushed onto the audio sink
   // or DecodedStream in microseconds. This will approximately be the end time
   // of the audio stream, unless another frame is pushed to the hardware.
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasink/AudioSink.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#if !defined(AudioSink_h__)
+#define AudioSink_h__
+
+#include "mozilla/nsRefPtr.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class MediaData;
+template <class T> class MediaQueue;
+
+namespace media {
+
+/*
+ * Define basic APIs for derived class instance to operate or obtain
+ * information from it.
+ */
+class AudioSink {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
+  AudioSink(MediaQueue<MediaData>& aAudioQueue)
+    : mAudioQueue(aAudioQueue)
+  {}
+
+  // Return a promise which will be resolved when AudioSink finishes playing,
+  // or rejected if any error.
+  virtual nsRefPtr<GenericPromise> Init() = 0;
+
+  virtual int64_t GetEndTime() const = 0;
+  virtual int64_t GetPosition() = 0;
+
+  // Check whether we've pushed more frames to the audio
+  // hardware than it has played.
+  virtual bool HasUnplayedFrames() = 0;
+
+  // Shut down the AudioSink's resources.
+  virtual void Shutdown() = 0;
+
+  // Change audio playback setting.
+  virtual void SetVolume(double aVolume) = 0;
+  virtual void SetPlaybackRate(double aPlaybackRate) = 0;
+  virtual void SetPreservesPitch(bool aPreservesPitch) = 0;
+
+  // Change audio playback status pause/resume.
+  virtual void SetPlaying(bool aPlaying) = 0;
+
+protected:
+  virtual ~AudioSink() {}
+
+  virtual MediaQueue<MediaData>& AudioQueue() const {
+    return mAudioQueue;
+  }
+
+  // To queue audio data (no matter it's plain or encoded or encrypted, depends
+  // on the subclass)
+  MediaQueue<MediaData>& mAudioQueue;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif
rename from dom/media/AudioSink.cpp
rename to dom/media/mediasink/DecodedAudioDataSink.cpp
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -1,171 +1,175 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "AudioSink.h"
 #include "AudioStream.h"
 #include "MediaQueue.h"
+#include "DecodedAudioDataSink.h"
 #include "VideoUtils.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 
 namespace mozilla {
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define SINK_LOG(msg, ...) \
-  MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
+  MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
+    ("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__))
 #define SINK_LOG_V(msg, ...) \
-  MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
+  MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \
+  ("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__))
+
+namespace media {
 
 // The amount of audio frames that is used to fuzz rounding errors.
 static const int64_t AUDIO_FUZZ_FRAMES = 1;
 
-AudioSink::AudioSink(MediaQueue<MediaData>& aAudioQueue,
-                     int64_t aStartTime,
-                     const AudioInfo& aInfo,
-                     dom::AudioChannel aChannel)
-  : mAudioQueue(aAudioQueue)
-  , mMonitor("AudioSink::mMonitor")
+DecodedAudioDataSink::DecodedAudioDataSink(MediaQueue<MediaData>& aAudioQueue,
+                                           int64_t aStartTime,
+                                           const AudioInfo& aInfo,
+                                           dom::AudioChannel aChannel)
+  : AudioSink(aAudioQueue)
+  , mMonitor("DecodedAudioDataSink::mMonitor")
   , mState(AUDIOSINK_STATE_INIT)
   , mAudioLoopScheduled(false)
   , mStartTime(aStartTime)
   , mWritten(0)
   , mLastGoodPosition(0)
   , mInfo(aInfo)
   , mChannel(aChannel)
   , mStopAudioThread(false)
   , mPlaying(true)
 {
 }
 
 void
-AudioSink::SetState(State aState)
+DecodedAudioDataSink::SetState(State aState)
 {
   AssertOnAudioThread();
   mPendingState = Some(aState);
 }
 
 void
-AudioSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
+DecodedAudioDataSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
 {
   DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
   // There isn't much we can do if Dispatch() fails.
   // Just assert it to keep things simple.
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 void
-AudioSink::OnAudioQueueEvent()
+DecodedAudioDataSink::OnAudioQueueEvent()
 {
   AssertOnAudioThread();
   if (!mAudioLoopScheduled) {
     AudioLoop();
   }
 }
 
 void
-AudioSink::ConnectListener()
+DecodedAudioDataSink::ConnectListener()
 {
   AssertOnAudioThread();
   mPushListener = AudioQueue().PushEvent().Connect(
-    mThread, this, &AudioSink::OnAudioQueueEvent);
+    mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent);
   mFinishListener = AudioQueue().FinishEvent().Connect(
-    mThread, this, &AudioSink::OnAudioQueueEvent);
+    mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent);
 }
 
 void
-AudioSink::DisconnectListener()
+DecodedAudioDataSink::DisconnectListener()
 {
   AssertOnAudioThread();
   mPushListener.Disconnect();
   mFinishListener.Disconnect();
 }
 
 void
-AudioSink::ScheduleNextLoop()
+DecodedAudioDataSink::ScheduleNextLoop()
 {
   AssertOnAudioThread();
   if (mAudioLoopScheduled) {
     return;
   }
   mAudioLoopScheduled = true;
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &DecodedAudioDataSink::AudioLoop);
   DispatchTask(r.forget());
 }
 
 void
-AudioSink::ScheduleNextLoopCrossThread()
+DecodedAudioDataSink::ScheduleNextLoopCrossThread()
 {
   AssertNotOnAudioThread();
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
     // Do nothing if there is already a pending task waiting for its turn.
     if (!self->mAudioLoopScheduled) {
       self->AudioLoop();
     }
   });
   DispatchTask(r.forget());
 }
 
 nsRefPtr<GenericPromise>
-AudioSink::Init()
+DecodedAudioDataSink::Init()
 {
   nsRefPtr<GenericPromise> p = mEndPromise.Ensure(__func__);
   nsresult rv = NS_NewNamedThread("Media Audio",
                                   getter_AddRefs(mThread),
                                   nullptr,
                                   SharedThreadPool::kStackSize);
   if (NS_FAILED(rv)) {
     mEndPromise.Reject(rv, __func__);
     return p;
   }
 
   ScheduleNextLoopCrossThread();
   return p;
 }
 
 int64_t
-AudioSink::GetPosition()
+DecodedAudioDataSink::GetPosition()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   int64_t pos;
   if (mAudioStream &&
       (pos = mAudioStream->GetPosition()) >= 0) {
     // Update the last good position when we got a good one.
     mLastGoodPosition = pos;
   }
 
   return mStartTime + mLastGoodPosition;
 }
 
 bool
-AudioSink::HasUnplayedFrames()
+DecodedAudioDataSink::HasUnplayedFrames()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   // Experimentation suggests that GetPositionInFrames() is zero-indexed,
   // so we need to add 1 here before comparing it to mWritten.
   return mAudioStream && mAudioStream->GetPositionInFrames() + 1 < mWritten;
 }
 
 void
-AudioSink::Shutdown()
+DecodedAudioDataSink::Shutdown()
 {
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     if (mAudioStream) {
       mAudioStream->Cancel();
     }
   }
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     self->mStopAudioThread = true;
     if (!self->mAudioLoopScheduled) {
       self->AudioLoop();
     }
   });
   DispatchTask(r.forget());
 
@@ -179,60 +183,60 @@ AudioSink::Shutdown()
   // Should've reached the final state after shutdown.
   MOZ_ASSERT(mState == AUDIOSINK_STATE_SHUTDOWN ||
              mState == AUDIOSINK_STATE_ERROR);
   // Should have no pending state change.
   MOZ_ASSERT(mPendingState.isNothing());
 }
 
 void
-AudioSink::SetVolume(double aVolume)
+DecodedAudioDataSink::SetVolume(double aVolume)
 {
   AssertNotOnAudioThread();
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mState == AUDIOSINK_STATE_PLAYING) {
       self->mAudioStream->SetVolume(aVolume);
     }
   });
   DispatchTask(r.forget());
 }
 
 void
-AudioSink::SetPlaybackRate(double aPlaybackRate)
+DecodedAudioDataSink::SetPlaybackRate(double aPlaybackRate)
 {
   AssertNotOnAudioThread();
   MOZ_ASSERT(aPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream");
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mState == AUDIOSINK_STATE_PLAYING) {
       self->mAudioStream->SetPlaybackRate(aPlaybackRate);
     }
   });
   DispatchTask(r.forget());
 }
 
 void
-AudioSink::SetPreservesPitch(bool aPreservesPitch)
+DecodedAudioDataSink::SetPreservesPitch(bool aPreservesPitch)
 {
   AssertNotOnAudioThread();
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mState == AUDIOSINK_STATE_PLAYING) {
       self->mAudioStream->SetPreservesPitch(aPreservesPitch);
     }
   });
   DispatchTask(r.forget());
 }
 
 void
-AudioSink::SetPlaying(bool aPlaying)
+DecodedAudioDataSink::SetPlaying(bool aPlaying)
 {
   AssertNotOnAudioThread();
-  nsRefPtr<AudioSink> self = this;
+  nsRefPtr<DecodedAudioDataSink> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     if (self->mState != AUDIOSINK_STATE_PLAYING ||
         self->mPlaying == aPlaying) {
       return;
     }
     self->mPlaying = aPlaying;
     // pause/resume AudioStream as necessary.
     if (!aPlaying && !self->mAudioStream->IsPaused()) {
@@ -244,17 +248,17 @@ AudioSink::SetPlaying(bool aPlaying)
     if (aPlaying && !self->mAudioLoopScheduled) {
       self->AudioLoop();
     }
   });
   DispatchTask(r.forget());
 }
 
 nsresult
-AudioSink::InitializeAudioStream()
+DecodedAudioDataSink::InitializeAudioStream()
 {
   // AudioStream initialization can block for extended periods in unusual
   // circumstances, so we take care to drop the decoder monitor while
   // initializing.
   RefPtr<AudioStream> audioStream(new AudioStream());
   nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
                                   mChannel, AudioStream::HighLatency);
   if (NS_FAILED(rv)) {
@@ -264,69 +268,69 @@ AudioSink::InitializeAudioStream()
 
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mAudioStream = audioStream;
 
   return NS_OK;
 }
 
 void
-AudioSink::Drain()
+DecodedAudioDataSink::Drain()
 {
   AssertOnAudioThread();
   MOZ_ASSERT(mPlaying && !mAudioStream->IsPaused());
   // If the media was too short to trigger the start of the audio stream,
   // start it now.
   mAudioStream->Start();
   mAudioStream->Drain();
 }
 
 void
-AudioSink::Cleanup()
+DecodedAudioDataSink::Cleanup()
 {
   AssertOnAudioThread();
   mEndPromise.Resolve(true, __func__);
   // Since the promise if resolved asynchronously, we don't shutdown
   // AudioStream here so MDSM::ResyncAudioClock can get the correct
   // audio position.
 }
 
 bool
-AudioSink::ExpectMoreAudioData()
+DecodedAudioDataSink::ExpectMoreAudioData()
 {
   return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
 }
 
 bool
-AudioSink::WaitingForAudioToPlay()
+DecodedAudioDataSink::WaitingForAudioToPlay()
 {
   AssertOnAudioThread();
   // Return true if we're not playing, and we're not shutting down, or we're
   // playing and we've got no audio to play.
   if (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
     return true;
   }
   return false;
 }
 
 bool
-AudioSink::IsPlaybackContinuing()
+DecodedAudioDataSink::IsPlaybackContinuing()
 {
   AssertOnAudioThread();
   // If we're shutting down, captured, or at EOS, break out and exit the audio
   // thread.
   if (mStopAudioThread || AudioQueue().AtEndOfStream()) {
     return false;
   }
 
   return true;
 }
 
 void
-AudioSink::AudioLoop()
+DecodedAudioDataSink::AudioLoop()
 {
   AssertOnAudioThread();
   mAudioLoopScheduled = false;
 
   switch (mState) {
     case AUDIOSINK_STATE_INIT: {
       SINK_LOG("AudioLoop started");
       nsresult rv = InitializeAudioStream();
@@ -381,17 +385,17 @@ AudioSink::AudioLoop()
     mState = mPendingState.ref();
     mPendingState.reset();
     // Schedule next loop when state changes.
     ScheduleNextLoop();
   }
 }
 
 bool
-AudioSink::PlayAudio()
+DecodedAudioDataSink::PlayAudio()
 {
   // See if there's a gap in the audio. If there is, push silence into the
   // audio hardware, so we can play across the gap.
   // Calculate the timestamp of the next chunk of audio in numbers of
   // samples.
   NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
   CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
 
@@ -415,30 +419,30 @@ AudioSink::PlayAudio()
   } else {
     mWritten += PlayFromAudioQueue();
   }
 
   return true;
 }
 
 void
-AudioSink::FinishAudioLoop()
+DecodedAudioDataSink::FinishAudioLoop()
 {
   AssertOnAudioThread();
   MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
   if (!mStopAudioThread && mPlaying) {
     Drain();
   }
   SINK_LOG("AudioLoop complete");
   Cleanup();
   SINK_LOG("AudioLoop exit");
 }
 
 uint32_t
-AudioSink::PlaySilence(uint32_t aFrames)
+DecodedAudioDataSink::PlaySilence(uint32_t aFrames)
 {
   // Maximum number of bytes we'll allocate and write at once to the audio
   // hardware when the audio stream contains missing frames and we're
   // writing silence in order to fill the gap. We limit our silence-writes
   // to 32KB in order to avoid allocating an impossibly large chunk of
   // memory if we encounter a large chunk of silence.
   const uint32_t SILENCE_BYTES_CHUNK = 32 * 1024;
 
@@ -447,17 +451,17 @@ AudioSink::PlaySilence(uint32_t aFrames)
   uint32_t maxFrames = SILENCE_BYTES_CHUNK / mInfo.mChannels / sizeof(AudioDataValue);
   uint32_t frames = std::min(aFrames, maxFrames);
   SINK_LOG_V("playing %u frames of silence", aFrames);
   WriteSilence(frames);
   return frames;
 }
 
 uint32_t
-AudioSink::PlayFromAudioQueue()
+DecodedAudioDataSink::PlayFromAudioQueue()
 {
   AssertOnAudioThread();
   NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
   nsRefPtr<AudioData> audio =
     dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
 
   SINK_LOG_V("playing %u frames of audio at time %lld",
              audio->mFrames, audio->mTime);
@@ -470,56 +474,57 @@ AudioSink::PlayFromAudioQueue()
   }
 
   StartAudioStreamPlaybackIfNeeded();
 
   return audio->mFrames;
 }
 
 void
-AudioSink::StartAudioStreamPlaybackIfNeeded()
+DecodedAudioDataSink::StartAudioStreamPlaybackIfNeeded()
 {
   // This value has been chosen empirically.
   const uint32_t MIN_WRITE_BEFORE_START_USECS = 200000;
 
   // We want to have enough data in the buffer to start the stream.
   if (static_cast<double>(mAudioStream->GetWritten()) / mAudioStream->GetRate() >=
       static_cast<double>(MIN_WRITE_BEFORE_START_USECS) / USECS_PER_S) {
     mAudioStream->Start();
   }
 }
 
 void
-AudioSink::WriteSilence(uint32_t aFrames)
+DecodedAudioDataSink::WriteSilence(uint32_t aFrames)
 {
   uint32_t numSamples = aFrames * mInfo.mChannels;
   nsAutoTArray<AudioDataValue, 1000> buf;
   buf.SetLength(numSamples);
   memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue));
   mAudioStream->Write(buf.Elements(), aFrames);
 
   StartAudioStreamPlaybackIfNeeded();
 }
 
 int64_t
-AudioSink::GetEndTime() const
+DecodedAudioDataSink::GetEndTime() const
 {
   CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime;
   if (!playedUsecs.isValid()) {
     NS_WARNING("Int overflow calculating audio end time");
     return -1;
   }
   return playedUsecs.value();
 }
 
 void
-AudioSink::AssertOnAudioThread()
+DecodedAudioDataSink::AssertOnAudioThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
 
 void
-AudioSink::AssertNotOnAudioThread()
+DecodedAudioDataSink::AssertNotOnAudioThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() != mThread);
 }
 
+} // namespace media
 } // namespace mozilla
rename from dom/media/AudioSink.h
rename to dom/media/mediasink/DecodedAudioDataSink.h
--- a/dom/media/AudioSink.h
+++ b/dom/media/mediasink/DecodedAudioDataSink.h
@@ -1,73 +1,70 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#if !defined(AudioSink_h__)
-#define AudioSink_h__
+#if !defined(DecodedAudioDataSink_h__)
+#define DecodedAudioDataSink_h__
 
+#include "AudioSink.h"
 #include "MediaInfo.h"
 #include "mozilla/nsRefPtr.h"
 #include "nsISupportsImpl.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 
 namespace mozilla {
+namespace media {
 
-class AudioData;
-class AudioStream;
-template <class T> class MediaQueue;
-
-class AudioSink {
+class DecodedAudioDataSink : public AudioSink {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
 
-  AudioSink(MediaQueue<MediaData>& aAudioQueue,
-            int64_t aStartTime,
-            const AudioInfo& aInfo,
-            dom::AudioChannel aChannel);
+  DecodedAudioDataSink(MediaQueue<MediaData>& aAudioQueue,
+                       int64_t aStartTime,
+                       const AudioInfo& aInfo,
+                       dom::AudioChannel aChannel);
 
-  // Return a promise which will be resolved when AudioSink finishes playing,
-  // or rejected if any error.
-  nsRefPtr<GenericPromise> Init();
+  // Return a promise which will be resolved when DecodedAudioDataSink
+  // finishes playing, or rejected if any error.
+  nsRefPtr<GenericPromise> Init() override;
 
   /*
    * All public functions below are thread-safe.
    */
-  int64_t GetPosition();
-  int64_t GetEndTime() const;
+  int64_t GetPosition() override;
+  int64_t GetEndTime() const override;
 
   // Check whether we've pushed more frames to the audio hardware than it has
   // played.
-  bool HasUnplayedFrames();
+  bool HasUnplayedFrames() override;
 
-  // Shut down the AudioSink's resources.
-  void Shutdown();
+  // Shut down the DecodedAudioDataSink's resources.
+  void Shutdown() override;
 
-  void SetVolume(double aVolume);
-  void SetPlaybackRate(double aPlaybackRate);
-  void SetPreservesPitch(bool aPreservesPitch);
-  void SetPlaying(bool aPlaying);
+  void SetVolume(double aVolume) override;
+  void SetPlaybackRate(double aPlaybackRate) override;
+  void SetPreservesPitch(bool aPreservesPitch) override;
+  void SetPlaying(bool aPlaying) override;
 
 private:
   enum State {
     AUDIOSINK_STATE_INIT,
     AUDIOSINK_STATE_PLAYING,
     AUDIOSINK_STATE_COMPLETE,
     AUDIOSINK_STATE_SHUTDOWN,
     AUDIOSINK_STATE_ERROR
   };
 
-  ~AudioSink() {}
+  virtual ~DecodedAudioDataSink() {}
 
   void DispatchTask(already_AddRefed<nsIRunnable>&& event);
   void SetState(State aState);
   void ScheduleNextLoop();
   void ScheduleNextLoopCrossThread();
 
   void OnAudioQueueEvent();
   void ConnectListener();
@@ -114,32 +111,27 @@ private:
   // audio data to the audio hardware.  Called on the audio thread.
   uint32_t PlayFromAudioQueue();
 
   // If we have already written enough frames to the AudioStream, start the
   // playback.
   void StartAudioStreamPlaybackIfNeeded();
   void WriteSilence(uint32_t aFrames);
 
-  MediaQueue<MediaData>& AudioQueue() const {
-    return mAudioQueue;
-  }
-
   ReentrantMonitor& GetReentrantMonitor() const {
     return mMonitor;
   }
 
   void AssertCurrentThreadInMonitor() const {
     GetReentrantMonitor().AssertCurrentThreadIn();
   }
 
   void AssertOnAudioThread();
   void AssertNotOnAudioThread();
 
-  MediaQueue<MediaData>& mAudioQueue;
   mutable ReentrantMonitor mMonitor;
 
   // There members are accessed on the audio thread only.
   State mState;
   Maybe<State> mPendingState;
   bool mAudioLoopScheduled;
 
   // Thread for pushing audio onto the audio hardware.
@@ -163,23 +155,24 @@ private:
 
   // Keep the last good position returned from the audio stream. Used to ensure
   // position returned by GetPosition() is mono-increasing in spite of audio
   // stream error.
   int64_t mLastGoodPosition;
 
   const AudioInfo mInfo;
 
-  dom::AudioChannel mChannel;
+  const dom::AudioChannel mChannel;
 
   bool mStopAudioThread;
 
   bool mPlaying;
 
   MozPromiseHolder<GenericPromise> mEndPromise;
 
   MediaEventListener mPushListener;
   MediaEventListener mFinishListener;
 };
 
+} // namespace media
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasink/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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 += [
+    'DecodedAudioDataSink.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+FAIL_ON_WARNINGS = True
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -20,16 +20,17 @@ with Files('GetUserMedia*'):
     BUG_COMPONENT = component_av
 
 DIRS += [
     'encoder',
     'gmp',
     'gmp-plugin',
     'gmp-plugin-openh264',
     'imagecapture',
+    'mediasink',
     'mediasource',
     'ogg',
     'platforms',
     'systemservices',
     'webaudio',
     'webrtc',
     'webspeech',
     'webvtt',
@@ -188,17 +189,16 @@ EXPORTS.mozilla.dom += [
     'VideoTrackList.h',
 ]
 
 UNIFIED_SOURCES += [
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
     'AudioSegment.cpp',
-    'AudioSink.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'AudioTrack.cpp',
     'AudioTrackList.cpp',
     'CanvasCaptureMediaStream.cpp',
     'CubebUtils.cpp',
     'DecodedStream.cpp',
     'DOMMediaStream.cpp',