Bug 493958. When decoding ahead to the desired seek position, save up the audio data as we go, because some of it might be needed for the destination frame and later frames. Also, if we reach the end of the frames without finding our destination position, keep the last frame and display it. r=doublec,roc
authorChris Pearce <chris@pearce.org.nz>
Thu, 21 May 2009 17:55:12 +1200
changeset 25654 13b3b54d6d39064588c57b57e9e8598a30a52df6
parent 25653 ce375826aed1b4843ddead92a7d26ae778f9df7a
child 25655 5ac037ba09f90eabdd12370c00a538c3867f8b12
push id1518
push userrocallahan@mozilla.com
push dateThu, 21 May 2009 05:59:56 +0000
reviewersdoublec, roc
bugs493958
milestone1.9.1pre
Bug 493958. When decoding ahead to the desired seek position, save up the audio data as we go, because some of it might be needed for the destination frame and later frames. Also, if we reach the end of the frames without finding our destination position, keep the last frame and display it. r=doublec,roc
content/media/video/src/nsOggDecoder.cpp
--- a/content/media/video/src/nsOggDecoder.cpp
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -778,16 +778,18 @@ nsOggDecodeStateMachine::FrameData* nsOg
         needSilence = PR_FALSE;
       }
     }
   }
 
   if (needSilence) {
     // Write silence to keep audio clock moving for av sync
     size_t count = mAudioChannels * mAudioRate * mCallbackPeriod;
+    // count must be evenly divisble by number of channels.
+    count = mAudioChannels * PRInt32(NS_ceil(mAudioRate*mCallbackPeriod));
     float* data = frame->mAudioData.AppendElements(count);
     if (data) {
       memset(data, 0, sizeof(float)*count);
     }
   }
 
   // Pick one stream to act as the reference track to indicate if the
   // stream has ended, seeked, etc.
@@ -1037,20 +1039,22 @@ void nsOggDecodeStateMachine::StopPlayba
 }
 
 void nsOggDecodeStateMachine::PausePlayback()
 {
   if (!mAudioStream) {
     StopPlayback();
     return;
   }
-
   mAudioStream->Pause();
   mPlaying = PR_FALSE;
   mPauseStartTime = TimeStamp::Now();
+  if (mAudioStream->GetPosition() < 0) {
+    mLastFrameTime = mDecodedFrames.ResetTimes(mCallbackPeriod);
+  }
 }
 
 void nsOggDecodeStateMachine::ResumePlayback()
 {
  if (!mAudioStream) {
     StartPlayback();
     return;
  }
@@ -1492,40 +1496,70 @@ nsresult nsOggDecodeStateMachine::Run()
         }
 
         mon.Enter();
         mDecoder->StartProgressUpdates();
         mLastFramePosition = mDecoder->mPlaybackPosition;
         if (mState == DECODER_STATE_SHUTDOWN)
           continue;
 
+        // Drop frames before the target seek time.
+        float seekTarget = seekTime - mCallbackPeriod / 2.0;
         FrameData* frame = nsnull;
         OggPlayErrorCode r;
-        float seekTarget = seekTime - mCallbackPeriod / 2.0;
+        mLastFrameTime = 0;
+        // Some of the audio data from previous frames actually belongs
+        // to this frame and later frames. So rescue that data and stuff
+        // it into the first frame.
+        float audioTime = 0;
+        nsTArray<float> audioData;
         do {
           do {
             mon.Exit();
             r = DecodeFrame();
             mon.Enter();
           } while (mState != DECODER_STATE_SHUTDOWN && r == E_OGGPLAY_TIMEOUT);
+
           if (mState == DECODER_STATE_SHUTDOWN)
             break;
 
-          mLastFrameTime = 0;
+          FrameData* nextFrame = NextFrame();
+          if (!nextFrame)
+            break;
+
           delete frame;
-          frame = NextFrame();
-        } while (frame && frame->mDecodedFrameTime < seekTarget);
+          frame = nextFrame;
+
+          audioData.AppendElements(frame->mAudioData);
+          audioTime += frame->mAudioData.Length() /
+                       (float)mAudioRate / (float)mAudioChannels;
+        } while (frame->mDecodedFrameTime < seekTarget);
 
         if (mState == DECODER_STATE_SHUTDOWN) {
           delete frame;
           continue;
         }
 
         NS_ASSERTION(frame != nsnull, "No frame after seek!");
         if (frame) {
+          if (audioTime > frame->mTime) {
+            // liboggplay gave us more data than expected, we need to prepend
+            // the extra data to the current frame to keep audio in sync.
+            audioTime -= frame->mTime;
+            // numExtraSamples must be evenly divisble by number of channels.
+            size_t numExtraSamples = mAudioChannels *
+                                     PRInt32(NS_ceil(mAudioRate*audioTime));
+            float* data = audioData.Elements() + audioData.Length() - numExtraSamples;
+            float* dst = frame->mAudioData.InsertElementsAt(0, numExtraSamples);
+            memcpy(dst, data, numExtraSamples * sizeof(float));
+          }
+        
+          mLastFrameTime = 0;
+          frame->mTime = 0;
+          frame->mState = OGGPLAY_STREAM_JUST_SEEKED;
           mDecodedFrames.Push(frame);
           UpdatePlaybackPosition(frame->mDecodedFrameTime);
           PlayVideo(frame);
         }
 
         // Change state to DECODING now. SeekingStopped will call
         // nsOggDecodeStateMachine::Seek to reset our state to SEEKING
         // if we need to seek again.