diff --git a/content/media/video/public/nsWaveDecoder.h b/content/media/video/public/nsWaveDecoder.h index e65ca4e3278c..29ab1fc40309 100644 --- a/content/media/video/public/nsWaveDecoder.h +++ b/content/media/video/public/nsWaveDecoder.h @@ -252,6 +252,13 @@ private: // underlying channel type. nsAutoPtr mStream; + // The media time of the last requested seek. This has not been validated + // against the current media, so may be out of bounds. Set when + // Seek(float) is called, and passed to the state machine when the + // SeekStarted event fires to tell it to update its time offset. The + // state machine will validate the offset against the current media. + float mTimeOffset; + // Copy of the current time and duration when the state machine was // disposed. Used to respond to time and duration queries with sensible // values after playback has ended. diff --git a/content/media/video/src/nsWaveDecoder.cpp b/content/media/video/src/nsWaveDecoder.cpp index 3ae6783c2df0..76390739bc54 100644 --- a/content/media/video/src/nsWaveDecoder.cpp +++ b/content/media/video/src/nsWaveDecoder.cpp @@ -151,6 +151,11 @@ public: // Main state machine loop. Runs forever, until shutdown state is reached. NS_IMETHOD Run(); + // Called by the decoder when the SeekStarted event runs. This ensures + // the current time offset of the state machine is updated to the new seek + // position at the appropriate time. + void UpdateTimeOffset(float aTime); + private: // Change the current state and wake the playback thread if it is waiting // on mMonitor. Used by public member functions called from both threads, @@ -281,7 +286,7 @@ private: float mInitialVolume; // Time position (in seconds) to offset current time from audio stream. - // Set by calling Seek(float) when seeking, and when the stream is closed + // Set when the seek started event runs and when the stream is closed // during shutdown. float mTimeOffset; @@ -290,6 +295,9 @@ private: // Available() reports). PRPackedBool mExpectMoreData; + // Time position (in seconds) to seek to. Set by Seek(float). + float mSeekTime; + // True once metadata has been parsed and validated. Users of mSampleRate, // mChannels, mSampleSize, mSampleFormat, mWaveLength, mWavePCMOffset must // check this flag before assuming the values are valid. @@ -316,6 +324,7 @@ nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder* aDecoder, nsMediaStream* a mInitialVolume(aInitialVolume), mTimeOffset(0.0), mExpectMoreData(PR_TRUE), + mSeekTime(0.0), mMetadataValid(PR_FALSE) { mMonitor = nsAutoMonitor::NewMonitor("nsWaveStateMachine"); @@ -379,9 +388,9 @@ nsWaveStateMachine::Seek(float aTime) { nsAutoMonitor monitor(mMonitor); mNextState = mState; - mTimeOffset = NS_MIN(aTime, BytesToTime(mWaveLength)); - if (mTimeOffset < 0.0) { - mTimeOffset = 0.0; + mSeekTime = NS_MIN(aTime, BytesToTime(mWaveLength)); + if (mSeekTime < 0.0) { + mSeekTime = 0.0; } ChangeState(STATE_SEEKING); } @@ -391,7 +400,15 @@ nsWaveStateMachine::GetDuration() { nsAutoMonitor monitor(mMonitor); if (mMetadataValid) { - return BytesToTime(mWaveLength); + PRUint32 length = mWaveLength; + PRInt64 contentLength = mDecoder->GetTotalBytes(); + // If the decoder has a valid content length, and it's shorter than the + // expected length of the PCM data, calculate the playback duration from + // the content length rather than the expected PCM data length. + if (contentLength >= 0 && contentLength - mWavePCMOffset < length) { + length = contentLength - mWavePCMOffset; + } + return BytesToTime(length); } return std::numeric_limits::quiet_NaN(); } @@ -562,6 +579,9 @@ nsWaveStateMachine::Run() { CloseAudioStream(); + mSeekTime = NS_MIN(mSeekTime, GetDuration()); + float seekTime = mSeekTime; + monitor.Exit(); nsCOMPtr startEvent = NS_NEW_RUNNABLE_METHOD(nsWaveDecoder, mDecoder, SeekingStarted); @@ -572,8 +592,12 @@ nsWaveStateMachine::Run() break; } - PRInt64 position = RoundDownToSample(TimeToBytes(mTimeOffset)) + mWavePCMOffset; - NS_ABORT_IF_FALSE(position >= 0 && position <= mWaveLength + mWavePCMOffset, "Invalid seek position"); + // Calculate relative offset within PCM data. + PRInt64 position = RoundDownToSample(TimeToBytes(seekTime)); + NS_ABORT_IF_FALSE(position >= 0 && position <= mWaveLength, "Invalid seek position"); + + // Convert to absolute offset within stream. + position += mWavePCMOffset; monitor.Exit(); nsresult rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, position); @@ -592,7 +616,7 @@ nsWaveStateMachine::Run() NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC); monitor.Enter(); - if (mState != STATE_SHUTDOWN) { + if (mState == STATE_SEEKING && mSeekTime == seekTime) { ChangeState(mNextState); } } @@ -649,6 +673,16 @@ nsWaveStateMachine::Run() return NS_OK; } +void +nsWaveStateMachine::UpdateTimeOffset(float aTime) +{ + nsAutoMonitor monitor(mMonitor); + mTimeOffset = NS_MIN(aTime, GetDuration()); + if (mTimeOffset < 0.0) { + mTimeOffset = 0.0; + } +} + void nsWaveStateMachine::ChangeState(State aState) { @@ -915,6 +949,7 @@ nsWaveDecoder::nsWaveDecoder() : mBytesDownloaded(0), mInitialVolume(1.0), mStream(nsnull), + mTimeOffset(0.0), mEndedCurrentTime(0.0), mEndedDuration(std::numeric_limits::quiet_NaN()), mNotifyOnShutdown(PR_FALSE), @@ -955,8 +990,10 @@ nsWaveDecoder::GetCurrentTime() nsresult nsWaveDecoder::Seek(float aTime) { + mTimeOffset = aTime; + if (mPlaybackStateMachine) { - mPlaybackStateMachine->Seek(aTime); + mPlaybackStateMachine->Seek(mTimeOffset); return NS_OK; } return NS_ERROR_FAILURE; @@ -1255,6 +1292,10 @@ nsWaveDecoder::SeekingStarted() return; } + if (mPlaybackStateMachine) { + mPlaybackStateMachine->UpdateTimeOffset(mTimeOffset); + } + if (mElement) { mElement->SeekStarted(); } diff --git a/content/media/video/test/Makefile.in b/content/media/video/test/Makefile.in index e1ab6a9ce37b..5e3118c38475 100644 --- a/content/media/video/test/Makefile.in +++ b/content/media/video/test/Makefile.in @@ -71,12 +71,23 @@ _TEST_FILES = test_autoplay.html \ test_volume.html \ test_wav_8bit.html \ test_wav_ended1.html \ + test_wav_seek3.html \ + test_wav_seek4.html \ + test_wav_seek5.html \ + test_wav_seek6.html \ + test_wav_seek7.html \ + test_wav_seek8.html \ test_wav_seek_past_end.html \ test_wav_seek_then_play.html \ + test_wav_trailing.html \ + test_wav_trunc.html \ + test_wav_trunc_seek.html \ 320x240.ogg \ bug461281.ogg \ seek.ogg \ r11025_s16_c1.wav \ + r11025_s16_c1_trailing.wav \ + r11025_s16_c1_trunc.wav \ r11025_u8_c1.wav \ # test_bug448534.html \ $(NULL) diff --git a/content/media/video/test/r11025_s16_c1_trailing.wav b/content/media/video/test/r11025_s16_c1_trailing.wav new file mode 100644 index 000000000000..af53beaf255b Binary files /dev/null and b/content/media/video/test/r11025_s16_c1_trailing.wav differ diff --git a/content/media/video/test/r11025_s16_c1_trunc.wav b/content/media/video/test/r11025_s16_c1_trunc.wav new file mode 100644 index 000000000000..4a8f2c0e4f9d Binary files /dev/null and b/content/media/video/test/r11025_s16_c1_trunc.wav differ diff --git a/content/media/video/test/test_seek1.html b/content/media/video/test/test_seek1.html index 93d83a26ec2f..0040468f5f82 100644 --- a/content/media/video/test/test_seek1.html +++ b/content/media/video/test/test_seek1.html @@ -22,9 +22,10 @@ var seekFlagEnd = false; var readonly = true; var completed = false; -ok(!document.getElementById('v').seeking, "seeking should default to false"); +var v = document.getElementById('v'); +ok(!v.seeking, "seeking should default to false"); try { - v1.seeking = 1; + v.seeking = 1; readonly = false; } catch(e) { diff --git a/content/media/video/test/test_seek8.html b/content/media/video/test/test_seek8.html index 2801abe5bcd9..5655bb53b2a0 100644 --- a/content/media/video/test/test_seek8.html +++ b/content/media/video/test/test_seek8.html @@ -45,7 +45,7 @@ function startTest() { completed = true; ok(thrown1, "Setting currentTime to invalid value of NaN"); ok(thrown2, "Setting currentTime to invalid value of -1"); - ok(thrown2, "Setting currentTime to invalid value of a function"); + ok(thrown3, "Setting currentTime to invalid value of a function"); SimpleTest.finish(); return false; } diff --git a/content/media/video/test/test_wav_seek3.html b/content/media/video/test/test_wav_seek3.html new file mode 100644 index 000000000000..ba908dda19cc --- /dev/null +++ b/content/media/video/test/test_wav_seek3.html @@ -0,0 +1,68 @@ + + + + Wave Media test: seek test 3 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_seek4.html b/content/media/video/test/test_wav_seek4.html new file mode 100644 index 000000000000..fa9bfef0e4b3 --- /dev/null +++ b/content/media/video/test/test_wav_seek4.html @@ -0,0 +1,54 @@ + + + + Wave Media test: seek test 4 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_seek5.html b/content/media/video/test/test_wav_seek5.html new file mode 100644 index 000000000000..3d897cf9ed4f --- /dev/null +++ b/content/media/video/test/test_wav_seek5.html @@ -0,0 +1,71 @@ + + + + Wave Media test: seek test 5 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_seek6.html b/content/media/video/test/test_wav_seek6.html new file mode 100644 index 000000000000..c7224665c56d --- /dev/null +++ b/content/media/video/test/test_wav_seek6.html @@ -0,0 +1,63 @@ + + + + Wave Media test: seek test 6 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_seek7.html b/content/media/video/test/test_wav_seek7.html new file mode 100644 index 000000000000..91631ab4d4b1 --- /dev/null +++ b/content/media/video/test/test_wav_seek7.html @@ -0,0 +1,60 @@ + + + + Wave Media test: seek test 7 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_seek8.html b/content/media/video/test/test_wav_seek8.html new file mode 100644 index 000000000000..16d7cc995d9a --- /dev/null +++ b/content/media/video/test/test_wav_seek8.html @@ -0,0 +1,58 @@ + + + + Wave Media test: seek test 8 + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_trailing.html b/content/media/video/test/test_wav_trailing.html new file mode 100644 index 000000000000..f5a4e0201fd3 --- /dev/null +++ b/content/media/video/test/test_wav_trailing.html @@ -0,0 +1,46 @@ + + + + Wave Media test: playback ends when file has trailing non-PCM data + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_trunc.html b/content/media/video/test/test_wav_trunc.html new file mode 100644 index 000000000000..d07fb400070b --- /dev/null +++ b/content/media/video/test/test_wav_trunc.html @@ -0,0 +1,48 @@ + + + + Wave Media test: playback ends when file is truncated + + + + + + +
+
+
+ + diff --git a/content/media/video/test/test_wav_trunc_seek.html b/content/media/video/test/test_wav_trunc_seek.html new file mode 100644 index 000000000000..662b53c87a6f --- /dev/null +++ b/content/media/video/test/test_wav_trunc_seek.html @@ -0,0 +1,54 @@ + + + + Wave Media test: seeking in truncated file + + + + + + +
+
+
+ +