Bug 921622 - AudioStream rework. r=padenot, a=lsblakk
💩💩 backed out by b7913b826440 💩 💩
authorRandell Jesup <rjesup@jesup.org>
Wed, 21 May 2014 03:26:41 -0400
changeset 192353 eaa2b716ce89
parent 192352 4363817b56ca
child 192354 b7913b826440
push id3580
push userryanvm@gmail.com
push date2014-05-21 20:48 +0000
Treeherderresults
reviewerspadenot, lsblakk
bugs921622
milestone30.0
Bug 921622 - AudioStream rework. r=padenot, a=lsblakk
content/media/AudioStream.cpp
content/media/AudioStream.h
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -180,22 +180,16 @@ AudioStream::~AudioStream()
 
   StaticMutexAutoLock lock(sMutex);
   if (sCubebContext) {
     cubeb_destroy(sCubebContext);
     sCubebContext = nullptr;
   }
 }
 
-nsresult AudioStream::EnsureTimeStretcherInitialized()
-{
-  MonitorAutoLock mon(mMonitor);
-  return EnsureTimeStretcherInitializedUnlocked();
-}
-
 nsresult AudioStream::EnsureTimeStretcherInitializedUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mTimeStretcher) {
     // SoundTouch does not support a number of channels > 2
     if (mOutChannels > 2) {
       return NS_ERROR_FAILURE;
     }
@@ -207,25 +201,29 @@ nsresult AudioStream::EnsureTimeStretche
   return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
   NS_ASSERTION(aPlaybackRate > 0.0,
                "Can't handle negative or null playbackrate in the AudioStream.");
   // Avoid instantiating the resampler if we are not changing the playback rate.
+  // GetPreservesPitch/SetPreservesPitch don't need locking before calling
   if (aPlaybackRate == mAudioClock.GetPlaybackRate()) {
     return NS_OK;
   }
 
-  if (EnsureTimeStretcherInitialized() != NS_OK) {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+  if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
-  mAudioClock.SetPlaybackRate(aPlaybackRate);
+  mAudioClock.SetPlaybackRateUnlocked(aPlaybackRate);
   mOutRate = mInRate / aPlaybackRate;
 
   if (mAudioClock.GetPreservesPitch()) {
     mTimeStretcher->setTempo(aPlaybackRate);
     mTimeStretcher->setRate(1.0f);
   } else {
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(aPlaybackRate);
@@ -235,17 +233,20 @@ nsresult AudioStream::SetPlaybackRate(do
 
 nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
 {
   // Avoid instantiating the timestretcher instance if not needed.
   if (aPreservesPitch == mAudioClock.GetPreservesPitch()) {
     return NS_OK;
   }
 
-  if (EnsureTimeStretcherInitialized() != NS_OK) {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+  if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   if (aPreservesPitch == true) {
     mTimeStretcher->setTempo(mAudioClock.GetPlaybackRate());
     mTimeStretcher->setRate(1.0f);
   } else {
     mTimeStretcher->setTempo(1.0f);
@@ -600,17 +601,18 @@ AudioStream::Resume()
   if (mState != ERRORED && r == CUBEB_OK) {
     mState = STARTED;
   }
 }
 
 int64_t
 AudioStream::GetPosition()
 {
-  return mAudioClock.GetPosition();
+  MonitorAutoLock mon(mMonitor);
+  return mAudioClock.GetPositionUnlocked();
 }
 
 // This function is miscompiled by PGO with MSVC 2010.  See bug 768333.
 #ifdef _MSC_VER
 #pragma optimize("", off)
 #endif
 int64_t
 AudioStream::GetPositionInFrames()
@@ -890,19 +892,20 @@ void AudioClock::Init()
   mOldOutRate = mOutRate;
 }
 
 void AudioClock::UpdateWritePosition(uint32_t aCount)
 {
   mWritten += aCount;
 }
 
-uint64_t AudioClock::GetPosition()
+uint64_t AudioClock::GetPositionUnlocked()
 {
-  int64_t position = mAudioStream->GetPositionInFramesInternal();
+  // GetPositionInFramesUnlocked() asserts it owns the monitor
+  int64_t position = mAudioStream->GetPositionInFramesUnlocked();
   int64_t diffOffset;
   NS_ASSERTION(position < 0 || (mInRate != 0 && mOutRate != 0), "AudioClock not initialized.");
   if (position >= 0) {
     if (position < mPlaybackRateChangeOffset) {
       // See if we are still playing frames pushed with the old playback rate in
       // the backend. If we are, use the old output rate to compute the
       // position.
       mCompensatingLatency = true;
@@ -924,35 +927,36 @@ uint64_t AudioClock::GetPosition()
       (static_cast<float>(USECS_PER_S * diffOffset) / mOutRate));
     return position;
   }
   return UINT64_MAX;
 }
 
 uint64_t AudioClock::GetPositionInFrames()
 {
-  return (GetPosition() * mOutRate) / USECS_PER_S;
+  return (GetPositionUnlocked() * mOutRate) / USECS_PER_S;
 }
 
-void AudioClock::SetPlaybackRate(double aPlaybackRate)
+void AudioClock::SetPlaybackRateUnlocked(double aPlaybackRate)
 {
-  int64_t position = mAudioStream->GetPositionInFramesInternal();
+  // GetPositionInFramesUnlocked() asserts it owns the monitor
+  int64_t position = mAudioStream->GetPositionInFramesUnlocked();
   if (position > mPlaybackRateChangeOffset) {
     mOldBasePosition = mBasePosition;
-    mBasePosition = GetPosition();
+    mBasePosition = GetPositionUnlocked();
     mOldBaseOffset = mPlaybackRateChangeOffset;
     mBaseOffset = position;
     mPlaybackRateChangeOffset = mWritten;
     mOldOutRate = mOutRate;
     mOutRate = static_cast<int>(mInRate / aPlaybackRate);
   } else {
     // The playbackRate has been changed before the end of the latency
     // compensation phase. We don't update the mOld* variable. That way, the
     // last playbackRate set is taken into account.
-    mBasePosition = GetPosition();
+    mBasePosition = GetPositionUnlocked();
     mBaseOffset = position;
     mPlaybackRateChangeOffset = mWritten;
     mOutRate = static_cast<int>(mInRate / aPlaybackRate);
   }
 }
 
 double AudioClock::GetPlaybackRate()
 {
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -38,23 +38,27 @@ public:
   // Initialize the clock with the current AudioStream. Need to be called
   // before querying the clock. Called on the audio thread.
   void Init();
   // Update the number of samples that has been written in the audio backend.
   // Called on the state machine thread.
   void UpdateWritePosition(uint32_t aCount);
   // Get the read position of the stream, in microseconds.
   // Called on the state machine thead.
-  uint64_t GetPosition();
+  // Assumes the AudioStream lock is held and thus calls Unlocked versions
+  // of AudioStream funcs.
+  uint64_t GetPositionUnlocked();
   // Get the read position of the stream, in frames.
   // Called on the state machine thead.
   uint64_t GetPositionInFrames();
   // Set the playback rate.
   // Called on the audio thread.
-  void SetPlaybackRate(double aPlaybackRate);
+  // Assumes the AudioStream lock is held and thus calls Unlocked versions
+  // of AudioStream funcs.
+  void SetPlaybackRateUnlocked(double aPlaybackRate);
   // Get the current playback rate.
   // Called on the audio thread.
   double GetPlaybackRate();
   // Set if we are preserving the pitch.
   // Called on the audio thread.
   void SetPreservesPitch(bool aPreservesPitch);
   // Get the current pitch preservation state.
   // Called on the audio thread.
@@ -249,24 +253,29 @@ public:
 
   // Returns true when the audio stream is paused.
   bool IsPaused();
 
   int GetRate() { return mOutRate; }
   int GetChannels() { return mChannels; }
   int GetOutChannels() { return mOutChannels; }
 
-  // This should be called before attempting to use the time stretcher.
-  nsresult EnsureTimeStretcherInitialized();
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
+protected:
+  friend class AudioClock;
+
+  // Shared implementation of underflow adjusted position calculation.
+  // Caller must own the monitor.
+  int64_t GetPositionInFramesUnlocked();
+
 private:
   static void PrefChanged(const char* aPref, void* aClosure);
   static double GetVolumeScale();
   static cubeb* GetCubebContext();
   static cubeb* GetCubebContextUnlocked();
   static uint32_t GetCubebLatency();
   static bool CubebLatencyPrefSet();
 
@@ -285,20 +294,16 @@ private:
 
   nsresult EnsureTimeStretcherInitializedUnlocked();
 
   // aTime is the time in ms the samples were inserted into MediaStreamGraph
   long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
   long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
   long GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t &aTime);
 
-  // Shared implementation of underflow adjusted position calculation.
-  // Caller must own the monitor.
-  int64_t GetPositionInFramesUnlocked();
-
   int64_t GetLatencyInFrames();
   void GetBufferInsertTime(int64_t &aTimeMs);
 
   void StartUnlocked();
 
   // The monitor is held to protect all access to member variables.  Write()
   // waits while mBuffer is full; DataCallback() notifies as it consumes
   // data from mBuffer.  Drain() waits while mState is DRAINING;