Bug 592833 - Move audio stream management to audio thread. r=?
authorChris Pearce <chris@pearce.org.nz>
Tue, 12 Jul 2011 15:39:30 +1200
changeset 73065 45974f65fc7eaa8591d923d2d91f82ddd9819294
parent 73064 01fc3692c8883999a610472fe774be73149dcb00
child 73066 a73193e9b9d80967db5c6862ec27fea7b5dede1c
push id313
push usereakhgari@mozilla.com
push dateTue, 16 Aug 2011 19:58:41 +0000
treeherdermozilla-aurora@ef9d1c90dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs592833
milestone8.0a1
Bug 592833 - Move audio stream management to audio thread. r=?
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsBuiltinDecoderStateMachine.h
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -170,17 +170,16 @@ public:
   const PRUint32 mChannels;
   const PRUint32 mRate;
 };
 
 nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
                                                            nsBuiltinDecoderReader* aReader) :
   mDecoder(aDecoder),
   mState(DECODER_STATE_DECODING_METADATA),
-  mAudioReentrantMonitor("media.audiostream"),
   mCbCrSize(0),
   mPlayDuration(0),
   mStartTime(-1),
   mEndTime(-1),
   mSeekTime(0),
   mReader(aReader),
   mCurrentFrameTime(0),
   mAudioStartTime(-1),
@@ -438,62 +437,71 @@ void nsBuiltinDecoderStateMachine::Audio
   PRInt64 samplesAtLastSleep = 0;
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioCompleted = PR_FALSE;
     audioStartTime = mAudioStartTime;
     channels = mInfo.mAudioChannels;
     rate = mInfo.mAudioRate;
     NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
+
+    // We must hold the monitor while creating or destroying the audio stream,
+    // or whenever we use it off the audio thread.
+    mAudioStream = nsAudioStream::AllocateStream();
+    mAudioStream->Init(channels,
+                       rate,
+                       MOZ_SOUND_DATA_FORMAT);
+    volume = mVolume;
+    mAudioStream->SetVolume(volume);
   }
   while (1) {
 
     // Wait while we're not playing, and we're not shutting down, or we're
     // playing and we've got no audio to play.
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       NS_ASSERTION(mState != DECODER_STATE_DECODING_METADATA,
                    "Should have meta data before audio started playing.");
       while (mState != DECODER_STATE_SHUTDOWN &&
              !mStopAudioThread &&
              (!IsPlaying() ||
               mState == DECODER_STATE_BUFFERING ||
               (mReader->mAudioQueue.GetSize() == 0 &&
                !mReader->mAudioQueue.AtEndOfStream())))
       {
+        if (!IsPlaying() && !mAudioStream->IsPaused()) {
+          mAudioStream->Pause();
+        }
         samplesAtLastSleep = audioDuration;
         mon.Wait();
       }
 
       // If we're shutting down, break out and exit the audio thread.
       if (mState == DECODER_STATE_SHUTDOWN ||
           mStopAudioThread ||
           mReader->mAudioQueue.AtEndOfStream())
       {
         break;
       }
 
-      // We only want to go to the expense of taking the audio monitor and
-      // changing the volume if it's the first time we've entered the loop
-      // (as we must sync the volume in case it's changed since the
-      // nsAudioStream was created) or if the volume has changed.
+      // We only want to go to the expense of changing the volume if
+      // the volume has changed.
       setVolume = volume != mVolume;
       volume = mVolume;
+
+      if (IsPlaying() && mAudioStream->IsPaused()) {
+        mAudioStream->Resume();
+      }
     }
 
-    if (setVolume || minWriteSamples == -1) {
-      ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-      if (mAudioStream) {
-        if (setVolume) {
-          mAudioStream->SetVolume(volume);
-        }
-        if (minWriteSamples == -1) {
-          minWriteSamples = mAudioStream->GetMinWriteSamples();
-        }
-      }
+    if (setVolume) {
+      mAudioStream->SetVolume(volume);
+    }
+    if (minWriteSamples == -1) {
+      minWriteSamples = mAudioStream->GetMinWriteSamples();
     }
     NS_ASSERTION(mReader->mAudioQueue.GetSize() > 0,
                  "Should have data to play");
     // See if there's missing samples in the audio stream. If there is, push
     // silence into the audio hardware, so we can play across the gap.
     const SoundData* s = mReader->mAudioQueue.PeekFront();
 
     // Calculate the number of samples that have been pushed onto the audio
@@ -565,200 +573,157 @@ void nsBuiltinDecoderStateMachine::Audio
     }
   }
   if (mReader->mAudioQueue.AtEndOfStream() &&
       mState != DECODER_STATE_SHUTDOWN &&
       !mStopAudioThread)
   {
     // Last sample pushed to audio hardware, wait for the audio to finish,
     // before the audio thread terminates.
-    ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-    if (mAudioStream) {
-      PRBool seeking = PR_FALSE;
+    PRBool seeking = PR_FALSE;
+    {
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       PRInt64 oldPosition = -1;
-
+      PRInt64 position = GetMediaTime();
+      while (oldPosition != position &&
+             mAudioEndTime - position > 0 &&
+             mState != DECODER_STATE_SEEKING &&
+             mState != DECODER_STATE_SHUTDOWN)
       {
-        ReentrantMonitorAutoExit audioExit(mAudioReentrantMonitor);
-        ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-        PRInt64 position = GetMediaTime();
-        while (oldPosition != position &&
-               mAudioEndTime - position > 0 &&
-               mState != DECODER_STATE_SEEKING &&
-               mState != DECODER_STATE_SHUTDOWN)
-        {
-          const PRInt64 DRAIN_BLOCK_USECS = 100000;
-          Wait(NS_MIN(mAudioEndTime - position, DRAIN_BLOCK_USECS));
-          oldPosition = position;
-          position = GetMediaTime();
-        }
-        if (mState == DECODER_STATE_SEEKING) {
-          seeking = PR_TRUE;
-        }
+        const PRInt64 DRAIN_BLOCK_USECS = 100000;
+        Wait(NS_MIN(mAudioEndTime - position, DRAIN_BLOCK_USECS));
+        oldPosition = position;
+        position = GetMediaTime();
       }
-
-      if (!seeking && mAudioStream && !mAudioStream->IsPaused()) {
-        mAudioStream->Drain();
+      seeking = mState == DECODER_STATE_SEEKING;
+    }
 
-        // Fire one last event for any extra samples that didn't fill a framebuffer.
-        mEventManager.Drain(mAudioEndTime);
-      }
+    if (!seeking && !mAudioStream->IsPaused()) {
+      mAudioStream->Drain();
+      // Fire one last event for any extra samples that didn't fill a framebuffer.
+      mEventManager.Drain(mAudioEndTime);
     }
-    LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder));
   }
+  LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder));
   {
+    // Must hold lock while shutting down and anulling audio stream to prevent
+    // state machine thread trying to use it while we're destroying it.
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    mAudioStream->Shutdown();
+    mAudioStream = nsnull;
+    mEventManager.Clear();
     mAudioCompleted = PR_TRUE;
     UpdateReadyState();
     // Kick the decode and state machine threads; they may be sleeping waiting
     // for this to finish.
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
   LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder));
 }
 
 PRUint32 nsBuiltinDecoderStateMachine::PlaySilence(PRUint32 aSamples,
                                                    PRUint32 aChannels,
                                                    PRUint64 aSampleOffset)
 
 {
-  ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-  if (!mAudioStream || mAudioStream->IsPaused()) {
-    // The state machine has paused since we've released the decoder
-    // monitor and acquired the audio monitor. Don't write any audio.
-    return 0;
-  }
+  NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
+  NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
   PRUint32 maxSamples = SILENCE_BYTES_CHUNK / aChannels;
   PRUint32 samples = NS_MIN(aSamples, maxSamples);
   PRUint32 numValues = samples * aChannels;
   nsAutoArrayPtr<SoundDataValue> buf(new SoundDataValue[numValues]);
   memset(buf.get(), 0, sizeof(SoundDataValue) * numValues);
   mAudioStream->Write(buf, numValues, PR_TRUE);
   // Dispatch events to the DOM for the audio just written.
   mEventManager.QueueWrittenAudioData(buf.get(), numValues,
                                       (aSampleOffset + samples) * aChannels);
   return samples;
 }
 
 PRUint32 nsBuiltinDecoderStateMachine::PlayFromAudioQueue(PRUint64 aSampleOffset,
                                                           PRUint32 aChannels)
 {
+  NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
+  NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
   nsAutoPtr<SoundData> sound(mReader->mAudioQueue.PopFront());
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     NS_WARN_IF_FALSE(IsPlaying(), "Should be playing");
     // Awaken the decode loop if it's waiting for space to free up in the
     // audio queue.
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
   PRInt64 offset = -1;
   PRUint32 samples = 0;
-  {
-    ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-    if (!mAudioStream) {
-      return 0;
-    }
-    // The state machine could have paused since we've released the decoder
-    // monitor and acquired the audio monitor. Rather than acquire both
-    // monitors, the audio stream also maintains whether its paused or not.
-    // This prevents us from doing a blocking write while holding the audio
-    // monitor while paused; we would block, and the state machine won't be
-    // able to acquire the audio monitor in order to resume or destroy the
-    // audio stream.
-    if (!mAudioStream->IsPaused()) {
-      mAudioStream->Write(sound->mAudioData,
-                          sound->AudioDataLength(),
-                          PR_TRUE);
+  // The state machine could have paused since we've released the decoder
+  // monitor and acquired the audio monitor. Rather than acquire both
+  // monitors, the audio stream also maintains whether its paused or not.
+  // This prevents us from doing a blocking write while holding the audio
+  // monitor while paused; we would block, and the state machine won't be
+  // able to acquire the audio monitor in order to resume or destroy the
+  // audio stream.
+  if (!mAudioStream->IsPaused()) {
+    mAudioStream->Write(sound->mAudioData,
+                        sound->AudioDataLength(),
+                        PR_TRUE);
 
-      offset = sound->mOffset;
-      samples = sound->mSamples;
+    offset = sound->mOffset;
+    samples = sound->mSamples;
 
-      // Dispatch events to the DOM for the audio just written.
-      mEventManager.QueueWrittenAudioData(sound->mAudioData.get(),
-                                          sound->AudioDataLength(),
-                                          (aSampleOffset + samples) * aChannels);
-    } else {
-      mReader->mAudioQueue.PushFront(sound);
-      sound.forget();
-    }
+    // Dispatch events to the DOM for the audio just written.
+    mEventManager.QueueWrittenAudioData(sound->mAudioData.get(),
+                                        sound->AudioDataLength(),
+                                        (aSampleOffset + samples) * aChannels);
+  } else {
+    mReader->mAudioQueue.PushFront(sound);
+    sound.forget();
   }
   if (offset != -1) {
     mDecoder->UpdatePlaybackOffset(offset);
   }
   return samples;
 }
 
 nsresult nsBuiltinDecoderStateMachine::Init(nsDecoderStateMachine* aCloneDonor)
 {
   nsBuiltinDecoderReader* cloneReader = nsnull;
   if (aCloneDonor) {
     cloneReader = static_cast<nsBuiltinDecoderStateMachine*>(aCloneDonor)->mReader;
   }
   return mReader->Init(cloneReader);
 }
 
-void nsBuiltinDecoderStateMachine::StopPlayback(eStopMode aMode)
+void nsBuiltinDecoderStateMachine::StopPlayback()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   mDecoder->mPlaybackStatistics.Stop(TimeStamp::Now());
 
   // Reset mPlayStartTime before we pause/shutdown the nsAudioStream. This is
   // so that if the audio loop is about to write audio, it will have the chance
   // to check to see if we're paused and not write the audio. If not, the
   // audio thread can block in the write, and we deadlock trying to acquire
   // the audio monitor upon resume playback.
   if (IsPlaying()) {
     mPlayDuration += DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
     mPlayStartTime = TimeStamp();
   }
-  if (HasAudio()) {
-    ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-    ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-    if (mAudioStream) {
-      if (aMode == AUDIO_PAUSE) {
-        mAudioStream->Pause();
-      } else if (aMode == AUDIO_SHUTDOWN) {
-        mAudioStream->Shutdown();
-        mAudioStream = nsnull;
-        mEventManager.Clear();
-      }
-    }
-  }
+  NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
 }
 
 void nsBuiltinDecoderStateMachine::StartPlayback()
 {
-  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
-               "Should be on state machine thread.");
   NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   LOG(PR_LOG_DEBUG, ("%p StartPlayback", mDecoder));
   mDecoder->mPlaybackStatistics.Start(TimeStamp::Now());
-  if (HasAudio()) {
-    PRInt32 rate = mInfo.mAudioRate;
-    PRInt32 channels = mInfo.mAudioChannels;
-
-    {
-      ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-      ReentrantMonitorAutoEnter audioMon(mAudioReentrantMonitor);
-      if (mAudioStream) {
-        // We have an audiostream, so it must have been paused the last time
-        // StopPlayback() was called.
-        mAudioStream->Resume();
-      } else {
-        // No audiostream, create one.
-        mAudioStream = nsAudioStream::AllocateStream();
-        mAudioStream->Init(channels, rate, MOZ_SOUND_DATA_FORMAT);
-        mAudioStream->SetVolume(mVolume);
-      }
-    }
-  }
   mPlayStartTime = TimeStamp::Now();
+  NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void nsBuiltinDecoderStateMachine::UpdatePlaybackPositionInternal(PRInt64 aTime)
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
@@ -963,18 +928,16 @@ void nsBuiltinDecoderStateMachine::StopD
     }
     mDecodeThread = nsnull;
     mDecodeThreadIdle = PR_FALSE;
   }
 }
 
 void nsBuiltinDecoderStateMachine::StopAudioThread()
 {
-  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
-               "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   mStopAudioThread = PR_TRUE;
   mDecoder->GetReentrantMonitor().NotifyAll();
   if (mAudioThread) {
     LOG(PR_LOG_DEBUG, ("%p Shutdown audio thread", mDecoder));
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       mAudioThread->Shutdown();
@@ -1201,30 +1164,30 @@ void nsBuiltinDecoderStateMachine::Decod
       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStarted);
     NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
   }
 
   if (currentTimeChanged) {
     // The seek target is different than the current playback position,
     // we'll need to seek the playback position, so shutdown our decode
     // and audio threads.
-    StopPlayback(AUDIO_SHUTDOWN);
+    StopPlayback();
     StopAudioThread();
     ResetPlayback();
     nsresult res;
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       // Now perform the seek. We must not hold the state machine monitor
-      // while we seek, since the seek decodes.
+      // while we seek, since the seek reads, which could block on I/O.
       res = mReader->Seek(seekTime,
                           mStartTime,
                           mEndTime,
                           mediaTime);
     }
-    if (NS_SUCCEEDED(res)){
+    if (NS_SUCCEEDED(res)) {
       SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull;
       NS_ASSERTION(!audio || (audio->mTime <= seekTime &&
                               seekTime <= audio->mTime + audio->mDuration),
                     "Seek target should lie inside the first audio block after seek");
       PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
       mAudioStartTime = startTime;
       mPlayDuration = startTime - mStartTime;
       if (HasVideo()) {
@@ -1287,17 +1250,17 @@ nsresult nsBuiltinDecoderStateMachine::R
   nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
 
   while (PR_TRUE) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     switch (mState) {
     case DECODER_STATE_SHUTDOWN:
       if (IsPlaying()) {
-        StopPlayback(AUDIO_SHUTDOWN);
+        StopPlayback();
       }
       StopAudioThread();
       StopDecodeThread();
       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
                    "How did we escape from the shutdown state???");
       return NS_OK;
 
     case DECODER_STATE_DECODING_METADATA:
@@ -1324,19 +1287,16 @@ nsresult nsBuiltinDecoderStateMachine::R
 
     case DECODER_STATE_DECODING:
       {
         if (NS_FAILED(StartDecodeThread())) {
           continue;
         }
 
         AdvanceFrame();
-
-        if (mState != DECODER_STATE_DECODING)
-          continue;
       }
       break;
 
     case DECODER_STATE_SEEKING:
       {
         // Ensure decode thread is alive and well...
         if (NS_FAILED(StartDecodeThread())) {
           continue;
@@ -1347,17 +1307,17 @@ nsresult nsBuiltinDecoderStateMachine::R
           mon.Wait();
         }
       }
       break;
 
     case DECODER_STATE_BUFFERING:
       {
         if (IsPlaying()) {
-          StopPlayback(AUDIO_PAUSE);
+          StopPlayback();
           mDecoder->GetReentrantMonitor().NotifyAll();
         }
 
         TimeStamp now = TimeStamp::Now();
         NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
         // We will remain in the buffering state if we've not decoded enough
         // data to begin playback, or if we've not downloaded a reasonable
@@ -1414,22 +1374,19 @@ nsresult nsBuiltinDecoderStateMachine::R
         // once to ensure the current playback position is advanced to the
         // end of the media, and so that we update the readyState.
         do {
           AdvanceFrame();
         } while (mState == DECODER_STATE_COMPLETED &&
                  (mReader->mVideoQueue.GetSize() > 0 ||
                  (HasAudio() && !mAudioCompleted)));
 
-        if (mAudioStream) {
-          // Close the audio stream so that next time audio is used a new stream
-          // is created. The StopPlayback call also resets the IsPlaying() state
-          // so audio is restarted correctly.
-          StopPlayback(AUDIO_SHUTDOWN);
-        }
+        // StopPlayback in order to reset the IsPlaying() state so audio
+        // is restarted correctly.
+        StopPlayback();
 
         if (mState != DECODER_STATE_COMPLETED)
           continue;
 
         StopAudioThread();
 
         if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
           PRInt64 videoTime = HasVideo() ? mVideoFrameEndTime : 0;
@@ -1477,19 +1434,23 @@ void nsBuiltinDecoderStateMachine::Rende
     mDecoder->SetVideoData(aData->mDisplay, image, aTarget);
   }
 }
 
 PRInt64
 nsBuiltinDecoderStateMachine::GetAudioClock()
 {
   NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
-  if (!mAudioStream || !HasAudio())
+  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+  if (!HasAudio())
     return -1;
-  PRInt64 t = mAudioStream->GetPosition();
+  PRInt64 t = -1;
+  if (!mAudioStream)
+    return -1;
+  t = mAudioStream->GetPosition();
   return (t == -1) ? -1 : t + mAudioStartTime;
 }
 
 void nsBuiltinDecoderStateMachine::AdvanceFrame()
 {
   NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
@@ -1618,37 +1579,36 @@ void nsBuiltinDecoderStateMachine::Advan
     // this function popping and playing a video sample, or by the audio
     // thread popping and playing an audio sample, we may need to update our
     // ready state. Post an update to do so.
     UpdateReadyState();
 
     if (remainingTime > 0) {
       Wait(remainingTime);
     }
-  } else {
-    if (IsPlaying()) {
-      StopPlayback(AUDIO_PAUSE);
-      mDecoder->GetReentrantMonitor().NotifyAll();
-    }
-
-    if (mState == DECODER_STATE_DECODING ||
-        mState == DECODER_STATE_COMPLETED) {
-      mDecoder->GetReentrantMonitor().Wait();
-    }
+  } else if (mState == DECODER_STATE_DECODING ||
+             mState == DECODER_STATE_COMPLETED)
+  {
+    StopPlayback();
+    mDecoder->GetReentrantMonitor().Wait();
   }
 }
 
 void nsBuiltinDecoderStateMachine::Wait(PRInt64 aUsecs) {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   TimeStamp end = TimeStamp::Now() + UsecsToDuration(NS_MAX<PRInt64>(USECS_PER_MS, aUsecs));
   TimeStamp now;
   while ((now = TimeStamp::Now()) < end &&
          mState != DECODER_STATE_SHUTDOWN &&
-         mState != DECODER_STATE_SEEKING)
+         mState != DECODER_STATE_SEEKING &&
+         (!OnAudioThread() || !mStopAudioThread))
   {
+    if (OnAudioThread() && !IsPlaying()) {
+      break;
+    }
     PRInt64 ms = static_cast<PRInt64>(NS_round((end - now).ToSeconds() * 1000));
     if (ms == 0 || ms > PR_UINT32_MAX) {
       break;
     }
     mDecoder->GetReentrantMonitor().Wait(PR_MillisecondsToInterval(static_cast<PRUint32>(ms)));
   }
 }
 
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -351,33 +351,24 @@ protected:
   // one lock count. Called on the state machine thread.
   nsresult StartAudioThread();
 
   // The main loop for the audio thread. Sent to the thread as
   // an nsRunnableMethod. This continually does blocking writes to
   // to audio stream to play audio data.
   void AudioLoop();
 
-  // Stop or pause playback of media. This has two modes, denoted by
-  // aMode being either AUDIO_PAUSE or AUDIO_SHUTDOWN.
-  //
-  // AUDIO_PAUSE: Suspends the audio stream to be resumed later.
-  // This does not close the OS based audio stream 
-  //
-  // AUDIO_SHUTDOWN: Closes and destroys the audio stream and
-  // releases any OS resources.
-  //
-  // The decoder monitor must be held with exactly one lock count. Called
-  // on the state machine thread.
-  enum eStopMode {AUDIO_PAUSE, AUDIO_SHUTDOWN};
-  void StopPlayback(eStopMode aMode);
+  // Sets internal state which causes playback of media to pause.
+  // The decoder monitor must be held. Called on the main, state machine,
+  // and decode threads.
+  void StopPlayback();
 
-  // Resume playback of media. Must be called with the decode monitor held.
-  // This resumes a paused audio stream. The decoder monitor must be held with
-  // exactly one lock count. Called on the state machine thread.
+  // Sets internal state which causes playback of media to begin or resume.
+  // Must be called with the decode monitor held. Called on the state machine
+  // and decode threads.
   void StartPlayback();
 
   // Moves the decoder into decoding state. Called on the state machine
   // thread. The decoder monitor must be held.
   void StartDecoding();
 
   // Returns PR_TRUE if we're currently playing. The decoder monitor must
   // be held.
@@ -413,22 +404,16 @@ protected:
   // Decode loop, decodes data until EOF or shutdown.
   // Called on the decode thread.
   void DecodeLoop();
 
   // Decode thread run function. Determines which of the Decode*() functions
   // to call.
   void DecodeThreadRun();
 
-  // ReentrantMonitor on mAudioStream. This monitor must be held in
-  // order to delete or use the audio stream. This stops us destroying
-  // the audio stream while it's being used on another thread
-  // (typically when it's being written to on the audio thread).
-  ReentrantMonitor mAudioReentrantMonitor;
-
   // The size of the decoded YCbCr frame.
   // Accessed on state machine thread.
   PRUint32 mCbCrSize;
 
   // Accessed on state machine thread.
   nsAutoArrayPtr<unsigned char> mCbCrBuffer;
 
   // Thread for pushing audio onto the audio hardware.
@@ -465,19 +450,20 @@ protected:
   // machine and main thread. Access controlled by decoder monitor.
   PRInt64 mEndTime;
 
   // Position to seek to in microseconds when the seek state transition occurs.
   // The decoder monitor lock must be obtained before reading or writing
   // this value. Accessed on main and state machine thread.
   PRInt64 mSeekTime;
 
-  // The audio stream resource. Used on the state machine, audio, and
-  // main threads. You must hold the mAudioReentrantMonitor, and must
-  // NOT hold the decoder monitor when using the audio stream!
+  // The audio stream resource. Used on the state machine, and audio threads.
+  // This is created and destroyed on the audio thread, while holding the
+  // decoder monitor, so if this is used off the audio thread, you must
+  // first acquire the decoder monitor and check that it is non-null.
   nsRefPtr<nsAudioStream> mAudioStream;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the play state machine's constructor, and destroyed
   // in the play state machine's destructor.
   nsAutoPtr<nsBuiltinDecoderReader> mReader;
 
   // The time of the current frame in microseconds. This is referenced from