Bug 495156 - Video playback gets stuck forever when pressing End key. r=doublec sr=roc a=blocking1.9.1+

This commit is contained in:
Chris Pearce 2009-05-29 00:15:16 -04:00
Родитель 73e0202e9d
Коммит 0c2e85ab11
1 изменённых файлов: 83 добавлений и 63 удалений

Просмотреть файл

@ -377,6 +377,12 @@ public:
} }
protected: protected:
// Decodes from the current position until encountering a frame with time
// greater or equal to aSeekTime.
void DecodeToFrame(nsAutoMonitor& aMonitor,
float aSeekTime);
// Convert the OggPlay frame information into a format used by Gecko // Convert the OggPlay frame information into a format used by Gecko
// (RGB for video, float for sound, etc).The decoder monitor must be // (RGB for video, float for sound, etc).The decoder monitor must be
// acquired in the scope of calls to these functions. They must be // acquired in the scope of calls to these functions. They must be
@ -1212,6 +1218,9 @@ void nsOggDecodeStateMachine::Seek(float aTime)
NS_ASSERTION(mState != DECODER_STATE_SEEKING, NS_ASSERTION(mState != DECODER_STATE_SEEKING,
"We shouldn't already be seeking"); "We shouldn't already be seeking");
mSeekTime = aTime + mPlaybackStartTime; mSeekTime = aTime + mPlaybackStartTime;
float duration = static_cast<float>(mDuration) / 1000.0;
NS_ASSERTION(mSeekTime >= 0 && mSeekTime <= duration,
"Can only seek in range [0,duration]");
LOG(PR_LOG_DEBUG, ("Changed state to SEEKING (to %f)", aTime)); LOG(PR_LOG_DEBUG, ("Changed state to SEEKING (to %f)", aTime));
mState = DECODER_STATE_SEEKING; mState = DECODER_STATE_SEEKING;
} }
@ -1297,6 +1306,71 @@ nsresult nsOggDecodeStateMachine::Seek(float aTime, nsChannelReader* aReader)
return (rv < 0) ? NS_ERROR_FAILURE : NS_OK; return (rv < 0) ? NS_ERROR_FAILURE : NS_OK;
} }
void nsOggDecodeStateMachine::DecodeToFrame(nsAutoMonitor& aMonitor,
float aTime)
{
// Drop frames before the target time.
float target = aTime - mCallbackPeriod / 2.0;
FrameData* frame = nsnull;
OggPlayErrorCode r;
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 {
aMonitor.Exit();
r = DecodeFrame();
aMonitor.Enter();
} while (mState != DECODER_STATE_SHUTDOWN && r == E_OGGPLAY_TIMEOUT);
HandleDecodeErrors(r);
if (mState == DECODER_STATE_SHUTDOWN)
break;
FrameData* nextFrame = NextFrame();
if (!nextFrame)
break;
delete frame;
frame = nextFrame;
audioData.AppendElements(frame->mAudioData);
audioTime += frame->mAudioData.Length() /
(float)mAudioRate / (float)mAudioChannels;
} while (frame->mDecodedFrameTime < target);
if (mState == DECODER_STATE_SHUTDOWN) {
delete frame;
return;
}
NS_ASSERTION(frame != nsnull, "No frame after decode!");
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);
}
}
void nsOggDecodeStateMachine::StopStepDecodeThread(nsAutoMonitor* aMonitor) void nsOggDecodeStateMachine::StopStepDecodeThread(nsAutoMonitor* aMonitor)
{ {
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mDecoder->GetMonitor()); PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mDecoder->GetMonitor());
@ -1518,7 +1592,7 @@ nsresult nsOggDecodeStateMachine::Run()
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, SeekingStarted); NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, SeekingStarted);
NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC); NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
Seek(seekTime, reader); nsresult res = Seek(seekTime, reader);
// Reactivate all tracks. Liboggplay deactivates tracks when it // Reactivate all tracks. Liboggplay deactivates tracks when it
// reads to the end of stream, but they must be reactivated in order // reads to the end of stream, but they must be reactivated in order
@ -1531,75 +1605,21 @@ nsresult nsOggDecodeStateMachine::Run()
if (mState == DECODER_STATE_SHUTDOWN) if (mState == DECODER_STATE_SHUTDOWN)
continue; continue;
// Drop frames before the target seek time. if (NS_SUCCEEDED(res)) {
float seekTarget = seekTime - mCallbackPeriod / 2.0; DecodeToFrame(mon, seekTime);
FrameData* frame = nsnull; // mSeekTime should not have changed. While we seek, mPlayState
OggPlayErrorCode r; // should always be PLAY_STATE_SEEKING and no-one will call
mLastFrameTime = 0; // nsOggDecoderStateMachine::Seek.
// Some of the audio data from previous frames actually belongs NS_ASSERTION(seekTime == mSeekTime, "No-one should have changed mSeekTime");
// to this frame and later frames. So rescue that data and stuff if (mState == DECODER_STATE_SHUTDOWN) {
// it into the first frame. continue;
float audioTime = 0;
nsTArray<float> audioData;
do {
do {
mon.Exit();
r = DecodeFrame();
mon.Enter();
} while (mState != DECODER_STATE_SHUTDOWN && r == E_OGGPLAY_TIMEOUT);
HandleDecodeErrors(r);
if (mState == DECODER_STATE_SHUTDOWN)
break;
FrameData* nextFrame = NextFrame();
if (!nextFrame)
break;
delete frame;
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 // Change state to DECODING now. SeekingStopped will call
// nsOggDecodeStateMachine::Seek to reset our state to SEEKING // nsOggDecodeStateMachine::Seek to reset our state to SEEKING
// if we need to seek again. // if we need to seek again.
LOG(PR_LOG_DEBUG, ("Changed state from SEEKING (to %f) to DECODING", seekTime)); LOG(PR_LOG_DEBUG, ("Changed state from SEEKING (to %f) to DECODING", seekTime));
// mSeekTime should not have changed. While we seek, mPlayState
// should always be PLAY_STATE_SEEKING and no-one will call
// nsOggDecoderStateMachine::Seek.
NS_ASSERTION(seekTime == mSeekTime, "No-one should have changed mSeekTime");
mState = DECODER_STATE_DECODING; mState = DECODER_STATE_DECODING;
mon.NotifyAll(); mon.NotifyAll();