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 28700 a983f0d35311fd910ae29880d1970f0e0625ba1c
parent 28699 de69f4c9857d8ffc237d5ec84c05af30db6bf280
child 28701 d6a2b89e5a0c22d43328174324e5ae550183f2b1
push id7192
push userrocallahan@mozilla.com
push dateThu, 21 May 2009 05:55:36 +0000
treeherdermozilla-central@a983f0d35311 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdoublec, roc
bugs493958
milestone1.9.2a1pre
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.