Bug 592833 - Move metadata decoding to decode thread. r=roc
authorChris Pearce <chris@pearce.org.nz>
Tue, 12 Jul 2011 15:39:23 +1200
changeset 73062 786f3a0c29b53a67b60834bb1bdc2d69a0b879f0
parent 73061 24360f70b743c9272734700ddfa9b78a74cf6628
child 73063 61d23b08b595a95808345e6dc458cac85fb086c7
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)
reviewersroc
bugs592833
milestone8.0a1
Bug 592833 - Move metadata decoding to decode thread. r=roc
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderReader.cpp
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsBuiltinDecoderStateMachine.h
content/media/ogg/nsOggReader.cpp
content/media/wave/nsWaveReader.cpp
content/media/webm/nsWebMReader.cpp
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -866,26 +866,30 @@ void nsBuiltinDecoder::Resume(PRBool aFo
   if (aForceBuffering) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mDecoderStateMachine->StartBuffering();
   }
 }
 
 void nsBuiltinDecoder::StopProgressUpdates()
 {
-  NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
+  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
+               "Should be on state machine or decode thread.");
+  GetReentrantMonitor().AssertCurrentThreadIn();
   mIgnoreProgressData = PR_TRUE;
   if (mStream) {
     mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
   }
 }
 
 void nsBuiltinDecoder::StartProgressUpdates()
 {
-  NS_ASSERTION(IsCurrentThread(mStateMachineThread), "Should be on state machine thread.");
+  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
+               "Should be on state machine or decode thread.");
+  GetReentrantMonitor().AssertCurrentThreadIn();
   mIgnoreProgressData = PR_FALSE;
   if (mStream) {
     mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
     mDecoderPosition = mPlaybackPosition = mStream->Tell();
   }
 }
 
 void nsBuiltinDecoder::MoveLoadsToBackground()
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -430,17 +430,17 @@ class nsBuiltinDecoder : public nsMediaD
   void Stop();
 
   void AudioAvailable(float* aFrameBuffer, PRUint32 aFrameBufferLength, float aTime);
 
   // Called by the state machine to notify the decoder that the duration
   // has changed.
   void DurationChanged();
 
-  PRBool OnStateMachineThread() {
+  PRBool OnStateMachineThread() const {
     return IsCurrentThread(mStateMachineThread);
   }
 
   PRBool OnDecodeThread() const {
     return mDecoderStateMachine->OnDecodeThread();
   }
 
   // Returns the monitor for other threads to synchronise access to
--- a/content/media/nsBuiltinDecoderReader.cpp
+++ b/content/media/nsBuiltinDecoderReader.cpp
@@ -209,17 +209,18 @@ nsresult nsBuiltinDecoderReader::ResetDe
   mVideoQueue.Reset();
   mAudioQueue.Reset();
 
   return res;
 }
 
 VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64& aOutStartTime)
 {
-  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine or decode thread.");
 
   // Extract the start times of the bitstreams in order to calculate
   // the duration.
   PRInt64 videoStartTime = PR_INT64_MAX;
   PRInt64 audioStartTime = PR_INT64_MAX;
   VideoData* videoData = nsnull;
 
   if (HasVideo()) {
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -265,20 +265,26 @@ void nsBuiltinDecoderStateMachine::Decod
   // be greater than ampleAudioThreshold, else we'd stop decoding!).
   PRInt64 ampleAudioThreshold = AMPLE_AUDIO_USECS;
 
   MediaQueue<VideoData>& videoQueue = mReader->mVideoQueue;
   MediaQueue<SoundData>& audioQueue = mReader->mAudioQueue;
 
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
+  if (mState == DECODER_STATE_DECODING_METADATA) {
+    if (NS_FAILED(DecodeMetadata())) {
+      LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
+    }
+    mDecoder->GetReentrantMonitor().NotifyAll();
+  }
+
+  // Main decode loop.
   PRBool videoPlaying = HasVideo();
   PRBool audioPlaying = HasAudio();
-
-  // Main decode loop.
   while (mState != DECODER_STATE_SHUTDOWN &&
          !mStopDecodeThread &&
          (videoPlaying || audioPlaying))
   {
     // We don't want to consider skipping to the next keyframe if we've
     // only just started up the decode loop, so wait until we've decoded
     // some frames before enabling the keyframe skip logic on video.
     if (videoPump &&
@@ -789,17 +795,17 @@ void nsBuiltinDecoderStateMachine::SetVo
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mVolume = volume;
 }
 
 double nsBuiltinDecoderStateMachine::GetCurrentTime() const
 {
   NS_ASSERTION(NS_IsMainThread() ||
-               mDecoder->OnStateMachineThread() ||
+               OnStateMachineThread() ||
                OnDecodeThread(),
                "Should be on main, decode, or state machine thread.");
 
   return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S);
 }
 
 PRInt64 nsBuiltinDecoderStateMachine::GetDuration()
 {
@@ -807,35 +813,35 @@ PRInt64 nsBuiltinDecoderStateMachine::Ge
 
   if (mEndTime == -1 || mStartTime == -1)
     return -1;
   return mEndTime - mStartTime;
 }
 
 void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
 {
-  NS_ASSERTION(NS_IsMainThread() || mDecoder->OnStateMachineThread(),
-    "Should be on main or state machine thread.");
+  NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
+               "Should be on main or decode thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (aDuration == -1) {
     return;
   }
 
   if (mStartTime != -1) {
     mEndTime = mStartTime + aDuration;
   } else {
     mStartTime = 0;
     mEndTime = aDuration;
   }
 }
 
 void nsBuiltinDecoderStateMachine::SetEndTime(PRInt64 aEndTime)
 {
-  NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread");
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   mEndTime = aEndTime;
 }
 
 void nsBuiltinDecoderStateMachine::SetSeekable(PRBool aSeekable)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@@ -855,18 +861,18 @@ void nsBuiltinDecoderStateMachine::Shutd
   // threads can start exiting cleanly during the Shutdown call.
   LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder));
   mState = DECODER_STATE_SHUTDOWN;
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void nsBuiltinDecoderStateMachine::StartDecoding()
 {
-  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
-               "Should be on state machine thread.");
+  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
+               "Should be on state machine or decode thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState != DECODER_STATE_DECODING) {
     mDecodeStartTime = TimeStamp::Now();
   }
   mState = DECODER_STATE_DECODING;
 }
 
 void nsBuiltinDecoderStateMachine::Play()
@@ -1064,16 +1070,82 @@ PRInt64 nsBuiltinDecoderStateMachine::Ge
 void nsBuiltinDecoderStateMachine::SetFrameBufferLength(PRUint32 aLength)
 {
   NS_ASSERTION(aLength >= 512 && aLength <= 16384,
                "The length must be between 512 and 16384");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   mEventManager.SetSignalBufferLength(aLength);
 }
 
+nsresult nsBuiltinDecoderStateMachine::DecodeMetadata()
+{
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
+  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
+
+  LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder));
+  nsresult res;
+  nsVideoInfo info;
+  {
+    ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
+    res = mReader->ReadMetadata(&info);
+  }
+  mInfo = info;
+
+  if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
+    mState = DECODER_STATE_SHUTDOWN;      
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
+    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    return NS_ERROR_FAILURE;
+  }
+  mDecoder->StartProgressUpdates();
+  mGotDurationFromMetaData = (GetDuration() != -1);
+
+  VideoData* videoData = FindStartTime();
+  if (videoData) {
+    ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
+    RenderVideoFrame(videoData, TimeStamp::Now());
+  }
+
+  if (mState == DECODER_STATE_SHUTDOWN) {
+    return NS_ERROR_FAILURE;
+  }
+
+  NS_ASSERTION(mStartTime != -1, "Must have start time");
+  NS_ASSERTION((!HasVideo() && !HasAudio()) ||
+                !mSeekable || mEndTime != -1,
+                "Active seekable media should have end time");
+  NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration");
+  LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld) seekable=%d",
+                      mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable));
+
+  // Inform the element that we've loaded the metadata and the first frame,
+  // setting the default framebuffer size for audioavailable events.  Also,
+  // if there is audio, let the MozAudioAvailable event manager know about
+  // the metadata.
+  if (HasAudio()) {
+    mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
+    // Set the buffer length at the decoder level to be able, to be able
+    // to retrive the value via media element method. The RequestFrameBufferLength
+    // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength().
+    PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
+    mDecoder->RequestFrameBufferLength(frameBufferLength);
+  }
+  nsCOMPtr<nsIRunnable> metadataLoadedEvent =
+    new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate);
+  NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
+
+  if (mState == DECODER_STATE_DECODING_METADATA) {
+    LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder));
+    StartDecoding();
+  }
+
+  return NS_OK;
+}
+
 nsresult nsBuiltinDecoderStateMachine::Run()
 {
   NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
                "Should be on state machine thread.");
   nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
 
   while (PR_TRUE) {
@@ -1086,72 +1158,33 @@ nsresult nsBuiltinDecoderStateMachine::R
       StopAudioThread();
       StopDecodeThread();
       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
                    "How did we escape from the shutdown state???");
       return NS_OK;
 
     case DECODER_STATE_DECODING_METADATA:
       {
-        LoadMetadata();
-        if (mState == DECODER_STATE_SHUTDOWN) {
-          continue;
-        }
-
-        VideoData* videoData = FindStartTime();
-        if (videoData) {
-          ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-          RenderVideoFrame(videoData, TimeStamp::Now());
-        }
-
-        // Start the decode threads, so that we can pre buffer the streams.
-        // and calculate the start time in order to determine the duration.
+        // Start the decode threads, so that metadata decoding begins.
         if (NS_FAILED(StartDecodeThread())) {
           continue;
         }
 
-        NS_ASSERTION(mStartTime != -1, "Must have start time");
-        NS_ASSERTION((!HasVideo() && !HasAudio()) ||
-                     !mSeekable || mEndTime != -1,
-                     "Active seekable media should have end time");
-        NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration");
-        LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld) seekable=%d",
-                           mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable));
-
-        if (mState == DECODER_STATE_SHUTDOWN)
-          continue;
-
-        // Inform the element that we've loaded the metadata and the first frame,
-        // setting the default framebuffer size for audioavailable events.  Also,
-        // if there is audio, let the MozAudioAvailable event manager know about
-        // the metadata.
-        if (HasAudio()) {
-          mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
-          // Set the buffer length at the decoder level to be able, to be able
-          // to retrive the value via media element method. The RequestFrameBufferLength
-          // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength().
-          PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
-          mDecoder->RequestFrameBufferLength(frameBufferLength);
-        }
-        nsCOMPtr<nsIRunnable> metadataLoadedEvent =
-          new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate);
-        NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
-
-        if (mState == DECODER_STATE_DECODING_METADATA) {
-          LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder));
-          StartDecoding();
+        while (mState == DECODER_STATE_DECODING_METADATA) {
+          mon.Wait();
         }
 
-        // Start playback.
-        if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
-          if (!IsPlaying()) {
-            StartPlayback();
-            StartAudioThread();
-          }
+        if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
+            mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
+            !IsPlaying())
+        {
+          StartPlayback();
+          StartAudioThread();
         }
+
       }
       break;
 
     case DECODER_STATE_DECODING:
       {
         if (NS_FAILED(StartDecodeThread())) {
           continue;
         }
@@ -1383,17 +1416,18 @@ nsresult nsBuiltinDecoderStateMachine::R
   }
 
   return NS_OK;
 }
 
 void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData,
                                                     TimeStamp aTarget)
 {
-  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
+  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
+               "Should be on state machine or decode thread.");
   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
 
   if (aData->mDuplicate) {
     return;
   }
 
   nsRefPtr<Image> image = aData->mImage;
   if (image) {
@@ -1572,17 +1606,17 @@ void nsBuiltinDecoderStateMachine::Wait(
       break;
     }
     mDecoder->GetReentrantMonitor().Wait(PR_MillisecondsToInterval(static_cast<PRUint32>(ms)));
   }
 }
 
 VideoData* nsBuiltinDecoderStateMachine::FindStartTime()
 {
-  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   PRInt64 startTime = 0;
   mStartTime = 0;
   VideoData* v = nsnull;
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     v = mReader->FindStartTime(startTime);
   }
@@ -1621,42 +1655,16 @@ void nsBuiltinDecoderStateMachine::Updat
       break;
     default:
       PR_NOT_REACHED("unhandled frame state");
   }
 
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
-void nsBuiltinDecoderStateMachine::LoadMetadata()
-{
-  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
-               "Should be on state machine thread.");
-  mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
-
-  LOG(PR_LOG_DEBUG, ("%p Loading Media Headers", mDecoder));
-  nsresult res;
-  nsVideoInfo info;
-  {
-    ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-    res = mReader->ReadMetadata(&info);
-  }
-  mInfo = info;
-
-  if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
-    mState = DECODER_STATE_SHUTDOWN;      
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
-    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-    return;
-  }
-  mDecoder->StartProgressUpdates();
-  mGotDurationFromMetaData = (GetDuration() != -1);
-}
-
 PRBool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering()
 {
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromSeconds(QUICK_BUFFER_THRESHOLD_USECS);
 }
 
 void nsBuiltinDecoderStateMachine::StartBuffering()
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -167,21 +167,16 @@ public:
   virtual void Play();
   virtual void Seek(double aTime);
   virtual double GetCurrentTime() const;
   virtual void ClearPositionChangeFlag();
   virtual void SetSeekable(PRBool aSeekable);
   virtual void UpdatePlaybackPosition(PRInt64 aTime);
   virtual void StartBuffering();
 
-
-  // Load metadata Called on the state machine thread. The decoder monitor must be held with
-  // exactly one lock count.
-  virtual void LoadMetadata();
-
   // State machine thread run function. Polls the state, sends frames to be
   // displayed at appropriate times, and generally manages the decode.
   NS_IMETHOD Run();
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   PRBool HasAudio() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
@@ -209,21 +204,21 @@ public:
   PRBool IsSeeking() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
     return mState == nsBuiltinDecoderStateMachine::DECODER_STATE_SEEKING;
   }
 
   // Functions used by assertions to ensure we're calling things
   // on the appropriate threads.
-  PRBool OnAudioThread() {
+  PRBool OnAudioThread() const {
     return IsCurrentThread(mAudioThread);
   }
 
-  PRBool OnStateMachineThread() {
+  PRBool OnStateMachineThread() const {
     return mDecoder->OnStateMachineThread();
   }
 
   // Decode loop, called on the decode thread.
   void DecodeLoop();
 
   // The decoder object that created this state machine. The decoder
   // always outlives us since it controls our lifetime. This is accessed
@@ -406,16 +401,20 @@ protected:
   // decoded and playable. This is the sum of the number of usecs of audio which
   // is decoded and in the reader's audio queue, and the usecs of unplayed audio
   // which has been pushed to the audio hardware for playback. Note that after
   // calling this, the audio hardware may play some of the audio pushed to
   // hardware, so this can only be used as a upper bound. The decoder monitor
   // must be held when calling this. Called on the decoder thread.
   PRInt64 GetDecodedAudioDuration();
 
+  // Load metadata. Called on the decode thread. The decoder monitor
+  // must be held with exactly one lock count.
+  nsresult DecodeMetadata();
+
   // 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.
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -173,17 +173,17 @@ PRBool nsOggReader::ReadHeaders(nsOggCod
       aState->DecodeHeader(packet);
     }
   }
   return aState->Init();
 }
 
 nsresult nsOggReader::ReadMetadata(nsVideoInfo* aInfo)
 {
-  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on play state machine thread.");
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   // We read packets until all bitstreams have read all their header packets.
   // We record the offset of the first non-header page so that we know
   // what page to seek to when seeking to the media start.
 
   ogg_page page;
   nsAutoTArray<nsOggCodecState*,4> bitstreams;
@@ -637,18 +637,18 @@ GetChecksum(ogg_page* page)
                (p[1] << 8) + 
                (p[2] << 16) +
                (p[3] << 24);
   return c;
 }
 
 PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset)
 {
-  NS_ASSERTION(mDecoder->OnStateMachineThread(),
-               "Should be on state machine thread.");
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine or decode thread.");
   nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, nsnull);
   nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, nsnull);
   PRInt64 startTime = 0;
   nsBuiltinDecoderReader::FindStartTime(startTime);
   return startTime;
 }
@@ -661,18 +661,18 @@ struct nsAutoOggSyncState {
     ogg_sync_clear(&mState);
   }
   ogg_sync_state mState;
 };
 
 PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  NS_ASSERTION(mDecoder->OnStateMachineThread(),
-               "Should be on state machine thread.");
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine or decode thread.");
 
   nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, -1);
   PRInt64 position = stream->Tell();
   PRInt64 endTime = RangeEndTime(0, aEndOffset, PR_FALSE);
   nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, position);
   NS_ENSURE_SUCCESS(res, -1);
   return endTime;
--- a/content/media/wave/nsWaveReader.cpp
+++ b/content/media/wave/nsWaveReader.cpp
@@ -147,17 +147,17 @@ nsWaveReader::~nsWaveReader()
 
 nsresult nsWaveReader::Init(nsBuiltinDecoderReader* aCloneDonor)
 {
   return NS_OK;
 }
 
 nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo)
 {
-  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
   if (!loaded) {
     return NS_ERROR_FAILURE;
   }
 
   mInfo.mHasAudio = PR_TRUE;
--- a/content/media/webm/nsWebMReader.cpp
+++ b/content/media/webm/nsWebMReader.cpp
@@ -204,17 +204,17 @@ void nsWebMReader::Cleanup()
   if (mContext) {
     nestegg_destroy(mContext);
     mContext = nsnull;
   }
 }
 
 nsresult nsWebMReader::ReadMetadata(nsVideoInfo* aInfo)
 {
-  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on state machine thread.");
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   nestegg_io io;
   io.read = webm_read;
   io.seek = webm_seek;
   io.tell = webm_tell;
   io.userdata = static_cast<nsBuiltinDecoder*>(mDecoder);
   int r = nestegg_init(&mContext, io, NULL);