Bug 934425 - Set device info in MediaSink and switch sink device. r=jya
authorAlex Chronopoulos <achronop@gmail.com>
Fri, 12 Oct 2018 08:44:35 +0000
changeset 499288 923d8e25777eb5cfb88269eeac04addbcb88a419
parent 499287 ae5bc4f62937a66d6201ea83b4dc22c5a4744fc8
child 499289 87921c31f0b70062ce1fa0bb3464107dd4a8370d
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs934425
milestone64.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 934425 - Set device info in MediaSink and switch sink device. r=jya Differential Revision: https://phabricator.services.mozilla.com/D5871
dom/media/AudioStream.cpp
dom/media/AudioStream.h
dom/media/mediasink/AudioSink.cpp
dom/media/mediasink/AudioSink.h
dom/media/mediasink/AudioSinkWrapper.cpp
dom/media/mediasink/AudioSinkWrapper.h
dom/media/mediasink/DecodedStream.cpp
dom/media/mediasink/DecodedStream.h
dom/media/mediasink/MediaSink.h
dom/media/mediasink/VideoSink.cpp
dom/media/mediasink/VideoSink.h
dom/media/moz.build
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -332,26 +332,29 @@ int AudioStream::InvokeCubeb(Function aF
 {
   MonitorAutoUnlock mon(mMonitor);
   return aFunction(mCubebStream.get(), std::forward<Args>(aArgs)...);
 }
 
 nsresult
 AudioStream::Init(uint32_t aNumChannels,
                   AudioConfig::ChannelLayout::ChannelMap aChannelMap,
-                  uint32_t aRate)
+                  uint32_t aRate,
+                  AudioDeviceInfo* aSinkInfo)
 {
   auto startTime = TimeStamp::Now();
 
   LOG("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate);
   mChannels = aNumChannels;
   mOutChannels = aNumChannels;
 
   mDumpFile = OpenDumpFile(aNumChannels, aRate);
 
+  mSinkInfo = aSinkInfo;
+
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
   params.layout = static_cast<uint32_t>(aChannelMap);
   params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
   params.prefs = CubebUtils::GetDefaultStreamPrefs();
 
   mAudioClock.Init(aRate);
@@ -375,18 +378,22 @@ AudioStream::OpenCubeb(cubeb* aContext, 
                        TimeStamp aStartTime, bool aIsFirst)
 {
   MOZ_ASSERT(aContext);
 
   cubeb_stream* stream = nullptr;
   /* Convert from milliseconds to frames. */
   uint32_t latency_frames =
     CubebUtils::GetCubebPlaybackLatencyInMilliseconds() * aParams.rate / 1000;
+  cubeb_devid deviceID = nullptr;
+  if (mSinkInfo && mSinkInfo->DeviceID()) {
+    deviceID = mSinkInfo->DeviceID();
+  }
   if (cubeb_stream_init(aContext, &stream, "AudioStream",
-                        nullptr, nullptr, nullptr, &aParams,
+                        nullptr, nullptr, deviceID, &aParams,
                         latency_frames,
                         DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
     mCubebStream.reset(stream);
     CubebUtils::ReportCubebBackendUsed();
   } else {
     LOGE("OpenCubeb() failed to init cubeb");
     CubebUtils::ReportCubebStreamInitFailure(aIsFirst);
     return NS_ERROR_FAILURE;
@@ -406,27 +413,31 @@ AudioStream::SetVolume(double aVolume)
 {
   MOZ_ASSERT(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
 
   if (cubeb_stream_set_volume(mCubebStream.get(), aVolume * CubebUtils::GetVolumeScale()) != CUBEB_OK) {
     LOGE("Could not change volume on cubeb stream.");
   }
 }
 
-void
+nsresult
 AudioStream::Start()
 {
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState == INITIALIZED);
   mState = STARTED;
   auto r = InvokeCubeb(cubeb_stream_start);
   if (r != CUBEB_OK) {
     mState = ERRORED;
   }
   LOG("started, state %s", mState == STARTED ? "STARTED" : mState == DRAINED ? "DRAINED" : "ERRORED");
+  if (mState == STARTED || mState == DRAINED) {
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
 }
 
 void
 AudioStream::Pause()
 {
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState != INITIALIZED, "Must be Start()ed.");
   MOZ_ASSERT(mState != STOPPED, "Already Pause()ed.");
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -195,29 +195,30 @@ public:
   explicit AudioStream(DataSource& aSource);
 
   // Initialize the audio stream. aNumChannels is the number of audio
   // channels (1 for mono, 2 for stereo, etc), aChannelMap is the indicator for
   // channel layout(mono, stereo, 5.1 or 7.1 ) and aRate is the sample rate
   // (22050Hz, 44100Hz, etc).
   nsresult Init(uint32_t aNumChannels,
                 AudioConfig::ChannelLayout::ChannelMap aChannelMap,
-                uint32_t aRate);
+                uint32_t aRate,
+                AudioDeviceInfo* aSinkInfo);
 
   // Closes the stream. All future use of the stream is an error.
   void Shutdown();
 
   void Reset();
 
   // Set the current volume of the audio playback. This is a value from
   // 0 (meaning muted) to 1 (meaning full volume).  Thread-safe.
   void SetVolume(double aVolume);
 
   // Start the stream.
-  void Start();
+  nsresult Start();
 
   // Pause audio playback.
   void Pause();
 
   // Resume audio playback.
   void Resume();
 
 #if defined(XP_WIN)
@@ -312,13 +313,18 @@ private:
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
 
   DataSource& mDataSource;
 
   bool mPrefillQuirk;
+
+  // The device info of the current sink. If null
+  // the default device is used. It is set
+  // during the Init() in decoder thread.
+  RefPtr<AudioDeviceInfo> mSinkInfo;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasink/AudioSink.cpp
+++ b/dom/media/mediasink/AudioSink.cpp
@@ -66,37 +66,37 @@ AudioSink::AudioSink(AbstractThread* aTh
 
   mOutputChannels = DecideAudioPlaybackChannels(mInfo);
 }
 
 AudioSink::~AudioSink()
 {
 }
 
-RefPtr<GenericPromise>
-AudioSink::Init(const PlaybackParams& aParams)
+nsresult
+AudioSink::Init(const PlaybackParams& aParams, RefPtr<GenericPromise>& aEndPromise)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
   mAudioQueueListener = mAudioQueue.PushEvent().Connect(
     mOwnerThread, this, &AudioSink::OnAudioPushed);
   mAudioQueueFinishListener = mAudioQueue.FinishEvent().Connect(
     mOwnerThread, this, &AudioSink::NotifyAudioNeeded);
   mProcessedQueueListener = mProcessedQueue.PopEvent().Connect(
     mOwnerThread, this, &AudioSink::OnAudioPopped);
 
   // To ensure at least one audio packet will be popped from AudioQueue and
   // ready to be played.
   NotifyAudioNeeded();
-  RefPtr<GenericPromise> p = mEndPromise.Ensure(__func__);
+  aEndPromise = mEndPromise.Ensure(__func__);
   nsresult rv = InitializeAudioStream(aParams);
   if (NS_FAILED(rv)) {
     mEndPromise.Reject(rv, __func__);
   }
-  return p;
+  return rv;
 }
 
 TimeUnit
 AudioSink::GetPosition()
 {
   int64_t tmp;
   if (mAudioStream &&
       (tmp = mAudioStream->GetPosition()) >= 0) {
@@ -192,31 +192,30 @@ AudioSink::InitializeAudioStream(const P
   // the coming audio data, so we use the predefined channel map instead.
   AudioConfig::ChannelLayout::ChannelMap channelMap =
     mConverter ? mConverter->OutputConfig().Layout().Map()
                : AudioConfig::ChannelLayout(mOutputChannels).Map();
   // The layout map used here is already processed by mConverter with
   // mOutputChannels into SMPTE format, so there is no need to worry if
   // StaticPrefs::accessibility_monoaudio_enable() or
   // StaticPrefs::MediaForcestereoEnabled() is applied.
-  nsresult rv = mAudioStream->Init(mOutputChannels, channelMap, mOutputRate);
+  nsresult rv = mAudioStream->Init(mOutputChannels, channelMap,
+                                   mOutputRate, aParams.mSink);
   if (NS_FAILED(rv)) {
     mAudioStream->Shutdown();
     mAudioStream = nullptr;
     return rv;
   }
 
   // Set playback params before calling Start() so they can take effect
   // as soon as the 1st DataCallback of the AudioStream fires.
   mAudioStream->SetVolume(aParams.mVolume);
   mAudioStream->SetPlaybackRate(aParams.mPlaybackRate);
   mAudioStream->SetPreservesPitch(aParams.mPreservesPitch);
-  mAudioStream->Start();
-
-  return NS_OK;
+  return mAudioStream->Start();
 }
 
 TimeUnit
 AudioSink::GetEndTime() const
 {
   int64_t written;
   {
     MonitorAutoLock mon(mMonitor);
--- a/dom/media/mediasink/AudioSink.h
+++ b/dom/media/mediasink/AudioSink.h
@@ -33,17 +33,17 @@ public:
             MediaQueue<AudioData>& aAudioQueue,
             const TimeUnit& aStartTime,
             const AudioInfo& aInfo);
 
   ~AudioSink();
 
   // Return a promise which will be resolved when AudioSink
   // finishes playing, or rejected if any error.
-  RefPtr<GenericPromise> Init(const PlaybackParams& aParams);
+  nsresult Init(const PlaybackParams& aParams, RefPtr<GenericPromise>& aEndPromise);
 
   /*
    * All public functions are not thread-safe.
    * Called on the task queue of MDSM only.
    */
   TimeUnit GetPosition();
   TimeUnit GetEndTime() const;
 
--- a/dom/media/mediasink/AudioSinkWrapper.cpp
+++ b/dom/media/mediasink/AudioSinkWrapper.cpp
@@ -173,39 +173,41 @@ AudioSinkWrapper::SetPlaying(bool aPlayi
     // Remember how long we've played.
     mPlayDuration = GetPosition();
     // mPlayStartTime must be updated later since GetPosition()
     // depends on the value of mPlayStartTime.
     mPlayStartTime = TimeStamp();
   }
 }
 
-void
+nsresult
 AudioSinkWrapper::Start(const TimeUnit& aStartTime, const MediaInfo& aInfo)
 {
   AssertOwnerThread();
   MOZ_ASSERT(!mIsStarted, "playback already started.");
 
   mIsStarted = true;
   mPlayDuration = aStartTime;
   mPlayStartTime = TimeStamp::Now();
 
   // no audio is equivalent to audio ended before video starts.
   mAudioEnded = !aInfo.HasAudio();
 
+  nsresult rv = NS_OK;
   if (aInfo.HasAudio()) {
     mAudioSink.reset(mCreator->Create());
-    mEndPromise = mAudioSink->Init(mParams);
+    rv = mAudioSink->Init(mParams, mEndPromise);
 
     mEndPromise->Then(
       mOwnerThread.get(), __func__, this,
       &AudioSinkWrapper::OnAudioEnded,
       &AudioSinkWrapper::OnAudioEnded
     )->Track(mAudioSinkPromise);
   }
+  return rv;
 }
 
 void
 AudioSinkWrapper::Stop()
 {
   AssertOwnerThread();
   MOZ_ASSERT(mIsStarted, "playback not started.");
 
--- a/dom/media/mediasink/AudioSinkWrapper.h
+++ b/dom/media/mediasink/AudioSinkWrapper.h
@@ -62,17 +62,17 @@ public:
   TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
   bool HasUnplayedFrames(TrackType aType) const override;
 
   void SetVolume(double aVolume) override;
   void SetPlaybackRate(double aPlaybackRate) override;
   void SetPreservesPitch(bool aPreservesPitch) override;
   void SetPlaying(bool aPlaying) override;
 
-  void Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) override;
+  nsresult Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) override;
   void Stop() override;
   bool IsStarted() const override;
   bool IsPlaying() const override;
 
   void Shutdown() override;
 
   nsCString GetDebugInfo() override;
 
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -299,17 +299,17 @@ DecodedStream::OnEnded(TrackType aType)
     // finished.
     return mFinishPromise;
   } else if (aType == TrackInfo::kVideoTrack && mInfo.HasVideo()) {
     return mFinishPromise;
   }
   return nullptr;
 }
 
-void
+nsresult
 DecodedStream::Start(const TimeUnit& aStartTime, const MediaInfo& aInfo)
 {
   AssertOwnerThread();
   MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
 
   mStartTime.emplace(aStartTime);
   mLastOutputTime = TimeUnit::Zero();
   mInfo = aInfo;
@@ -366,16 +366,17 @@ DecodedStream::Start(const TimeUnit& aSt
   mData = static_cast<R*>(r.get())->ReleaseData();
 
   if (mData) {
     mOutputListener = mData->OnOutput().Connect(
       mOwnerThread, this, &DecodedStream::NotifyOutput);
     mData->SetPlaying(mPlaying);
     SendData();
   }
+  return NS_OK;
 }
 
 void
 DecodedStream::Stop()
 {
   AssertOwnerThread();
   MOZ_ASSERT(mStartTime.isSome(), "playback not started.");
 
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/DecodedStream.h
@@ -56,17 +56,17 @@ public:
     return false;
   }
 
   void SetVolume(double aVolume) override;
   void SetPlaybackRate(double aPlaybackRate) override;
   void SetPreservesPitch(bool aPreservesPitch) override;
   void SetPlaying(bool aPlaying) override;
 
-  void Start(const media::TimeUnit& aStartTime, const MediaInfo& aInfo) override;
+  nsresult Start(const media::TimeUnit& aStartTime, const MediaInfo& aInfo) override;
   void Stop() override;
   bool IsStarted() const override;
   bool IsPlaying() const override;
 
   nsCString GetDebugInfo() override;
 
 protected:
   virtual ~DecodedStream();
--- a/dom/media/mediasink/MediaSink.h
+++ b/dom/media/mediasink/MediaSink.h
@@ -2,20 +2,21 @@
 /* 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 MediaSink_h_
 #define MediaSink_h_
 
+#include "AudioDeviceInfo.h"
+#include "MediaInfo.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/MozPromise.h"
 #include "nsISupportsImpl.h"
-#include "MediaInfo.h"
 
 namespace mozilla {
 
 class TimeStamp;
 
 namespace media {
 
 /**
@@ -39,16 +40,17 @@ public:
   typedef mozilla::TrackInfo::TrackType TrackType;
 
   struct PlaybackParams {
     PlaybackParams()
       : mVolume(1.0) , mPlaybackRate(1.0) , mPreservesPitch(true) {}
     double mVolume;
     double mPlaybackRate;
     bool mPreservesPitch;
+    RefPtr<AudioDeviceInfo> mSink;
   };
 
   // Return the playback parameters of this sink.
   // Can be called in any state.
   virtual const PlaybackParams& GetPlaybackParams() const = 0;
 
   // Set the playback parameters of this sink.
   // Can be called in any state.
@@ -95,17 +97,17 @@ public:
 
   // Single frame rendering operation may need to be done before playback
   // started (1st frame) or right after seek completed or playback stopped.
   // Do nothing if this sink has no video track. Can be called in any state.
   virtual void Redraw(const VideoInfo& aInfo) {};
 
   // Begin a playback session with the provided start time and media info.
   // Must be called when playback is stopped.
-  virtual void Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) = 0;
+  virtual nsresult Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) = 0;
 
   // Finish a playback session.
   // Must be called after playback starts.
   virtual void Stop() = 0;
 
   // Return true if playback has started.
   // Can be called in any state.
   virtual bool IsStarted() const = 0;
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -200,23 +200,23 @@ VideoSink::SetPlaying(bool aPlaying)
     // rendering while becoming playing status. because the VideoQueue may be
     // full already.
     TryUpdateRenderedVideoFrames();
   }
 
   EnsureHighResTimersOnOnlyIfPlaying();
 }
 
-void
+nsresult
 VideoSink::Start(const TimeUnit& aStartTime, const MediaInfo& aInfo)
 {
   AssertOwnerThread();
   VSINK_LOG("[%s]", __func__);
 
-  mAudioSink->Start(aStartTime, aInfo);
+  nsresult rv = mAudioSink->Start(aStartTime, aInfo);
 
   mHasVideo = aInfo.HasVideo();
 
   if (mHasVideo) {
     mEndPromise = mEndPromiseHolder.Ensure(__func__);
 
     // If the underlying MediaSink has an end promise for the video track (which
     // happens when mAudioSink refers to a DecodedStream), we must wait for it
@@ -242,16 +242,17 @@ VideoSink::Start(const TimeUnit& aStartT
         ->Track(mVideoSinkEndRequest);
     }
 
     ConnectListener();
     // Run the render loop at least once so we can resolve the end promise
     // when video duration is 0.
     UpdateRenderedVideoFrames();
   }
+  return rv;
 }
 
 void
 VideoSink::Stop()
 {
   AssertOwnerThread();
   MOZ_ASSERT(mAudioSink->IsStarted(), "playback not started.");
   VSINK_LOG("[%s]", __func__);
--- a/dom/media/mediasink/VideoSink.h
+++ b/dom/media/mediasink/VideoSink.h
@@ -53,17 +53,17 @@ public:
   void SetVolume(double aVolume) override;
 
   void SetPreservesPitch(bool aPreservesPitch) override;
 
   void SetPlaying(bool aPlaying) override;
 
   void Redraw(const VideoInfo& aInfo) override;
 
-  void Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) override;
+  nsresult Start(const TimeUnit& aStartTime, const MediaInfo& aInfo) override;
 
   void Stop() override;
 
   bool IsStarted() const override;
 
   bool IsPlaying() const override;
 
   void Shutdown() override;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -92,16 +92,17 @@ EXPORTS += [
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
     'AsyncLogger.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioConfig.h',
     'AudioConverter.h',
+    'AudioDeviceInfo.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'AutoplayPolicy.h',
     'BackgroundVideoDecodingPermissionObserver.h',
     'Benchmark.h',