mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 620326 - Ensure media decoder loop always makes its decision to sleep based on up-to-date information. r=roc a=roc
This commit is contained in:
parent
85baaf14aa
commit
757d8f1ec3
@ -192,16 +192,19 @@ PRBool nsBuiltinDecoderStateMachine::HaveNextFrameData() const {
|
||||
(!HasVideo() || mReader->mVideoQueue.GetSize() > 0);
|
||||
}
|
||||
|
||||
PRInt64 nsBuiltinDecoderStateMachine::GetDecodedAudioDuration() {
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
mDecoder->GetMonitor().AssertCurrentThreadIn();
|
||||
PRInt64 audioDecoded = mReader->mAudioQueue.Duration();
|
||||
if (mAudioEndTime != -1) {
|
||||
audioDecoded += mAudioEndTime - GetMediaTime();
|
||||
}
|
||||
return audioDecoded;
|
||||
}
|
||||
|
||||
void nsBuiltinDecoderStateMachine::DecodeLoop()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
PRBool videoPlaying = PR_FALSE;
|
||||
PRBool audioPlaying = PR_FALSE;
|
||||
{
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
videoPlaying = HasVideo();
|
||||
audioPlaying = HasAudio();
|
||||
}
|
||||
|
||||
// We want to "pump" the decode until we've got a few frames/samples decoded
|
||||
// before we consider whether decode is falling behind.
|
||||
@ -228,58 +231,37 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
|
||||
PRInt64 lowAudioThreshold = LOW_AUDIO_MS;
|
||||
|
||||
// Our local ample audio threshold. If we increase lowAudioThreshold, we'll
|
||||
// also increase this to appropriately (we don't want lowAudioThreshold to
|
||||
// also increase this too appropriately (we don't want lowAudioThreshold to
|
||||
// be greater than ampleAudioThreshold, else we'd stop decoding!).
|
||||
PRInt64 ampleAudioThreshold = AMPLE_AUDIO_MS;
|
||||
|
||||
// Main decode loop.
|
||||
while (videoPlaying || audioPlaying) {
|
||||
PRBool audioWait = !audioPlaying;
|
||||
PRBool videoWait = !videoPlaying;
|
||||
{
|
||||
// Wait for more data to download if we've exhausted all our
|
||||
// buffered data.
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
if (mState == DECODER_STATE_SHUTDOWN || mStopDecodeThreads)
|
||||
break;
|
||||
}
|
||||
MediaQueue<VideoData>& videoQueue = mReader->mVideoQueue;
|
||||
MediaQueue<SoundData>& audioQueue = mReader->mAudioQueue;
|
||||
|
||||
PRUint32 videoQueueSize = mReader->mVideoQueue.GetSize();
|
||||
// Don't decode any more frames if we've filled our buffers.
|
||||
// Limits memory consumption.
|
||||
if (videoQueueSize > AMPLE_VIDEO_FRAMES) {
|
||||
videoWait = PR_TRUE;
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
|
||||
PRBool videoPlaying = HasVideo();
|
||||
PRBool audioPlaying = HasAudio();
|
||||
|
||||
// Main decode loop.
|
||||
while (mState != DECODER_STATE_SHUTDOWN &&
|
||||
!mStopDecodeThreads &&
|
||||
(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 && videoQueue.GetSize() >= videoPumpThreshold) {
|
||||
videoPump = PR_FALSE;
|
||||
}
|
||||
|
||||
// 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 allowing the keyframe skip.
|
||||
if (videoPump && videoQueueSize >= videoPumpThreshold) {
|
||||
videoPump = PR_FALSE;
|
||||
}
|
||||
|
||||
// Determine how much audio data is decoded ahead of the current playback
|
||||
// position.
|
||||
PRInt64 currentTime = 0;
|
||||
PRInt64 audioDecoded = 0;
|
||||
PRBool decodeCloseToDownload = PR_FALSE;
|
||||
{
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
currentTime = GetMediaTime();
|
||||
audioDecoded = mReader->mAudioQueue.Duration();
|
||||
if (mAudioEndTime != -1) {
|
||||
audioDecoded += mAudioEndTime - currentTime;
|
||||
}
|
||||
decodeCloseToDownload = IsDecodeCloseToDownload();
|
||||
}
|
||||
|
||||
// Don't decode any audio if the audio decode is way ahead.
|
||||
if (audioDecoded > ampleAudioThreshold) {
|
||||
audioWait = PR_TRUE;
|
||||
}
|
||||
if (audioPump && audioDecoded > audioPumpThresholdMs) {
|
||||
// some audio data before enabling the keyframe skip logic on audio.
|
||||
if (audioPump && GetDecodedAudioDuration() >= audioPumpThresholdMs) {
|
||||
audioPump = PR_FALSE;
|
||||
}
|
||||
|
||||
// We'll skip the video decode to the nearest keyframe if we're low on
|
||||
// audio, or if we're low on video, provided we're not running low on
|
||||
// data to decode. If we're running low on downloaded data to decode,
|
||||
@ -288,23 +270,28 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
|
||||
// after buffering finishes.
|
||||
if (!skipToNextKeyframe &&
|
||||
videoPlaying &&
|
||||
!decodeCloseToDownload &&
|
||||
((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) ||
|
||||
(!videoPump && videoQueueSize < LOW_VIDEO_FRAMES)))
|
||||
!IsDecodeCloseToDownload() &&
|
||||
((!audioPump && audioPlaying && GetDecodedAudioDuration() < lowAudioThreshold) ||
|
||||
(!videoPump && videoPlaying && videoQueue.GetSize() < LOW_VIDEO_FRAMES)))
|
||||
{
|
||||
skipToNextKeyframe = PR_TRUE;
|
||||
LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe"));
|
||||
}
|
||||
|
||||
// Video decode.
|
||||
if (videoPlaying && !videoWait) {
|
||||
if (videoPlaying && videoQueue.GetSize() < AMPLE_VIDEO_FRAMES) {
|
||||
// Time the video decode, so that if it's slow, we can increase our low
|
||||
// audio threshold to reduce the chance of an audio underrun while we're
|
||||
// waiting for a video decode to complete.
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
|
||||
TimeDuration decodeTime = TimeStamp::Now() - start;
|
||||
if (!decodeCloseToDownload &&
|
||||
TimeDuration decodeTime;
|
||||
{
|
||||
PRInt64 currentTime = GetMediaTime();
|
||||
MonitorAutoExit exitMon(mDecoder->GetMonitor());
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
|
||||
decodeTime = TimeStamp::Now() - start;
|
||||
}
|
||||
if (!IsDecodeCloseToDownload() &&
|
||||
THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold)
|
||||
{
|
||||
lowAudioThreshold =
|
||||
@ -317,50 +304,55 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
|
||||
lowAudioThreshold, ampleAudioThreshold));
|
||||
}
|
||||
}
|
||||
{
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
mDecoder->GetMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
// Audio decode.
|
||||
if (audioPlaying && !audioWait) {
|
||||
if (audioPlaying &&
|
||||
(GetDecodedAudioDuration() < ampleAudioThreshold || audioQueue.GetSize() == 0))
|
||||
{
|
||||
MonitorAutoExit exitMon(mDecoder->GetMonitor());
|
||||
audioPlaying = mReader->DecodeAudioData();
|
||||
}
|
||||
|
||||
// Notify to ensure that the AudioLoop() is not waiting, in case it was
|
||||
// waiting for more audio to be decoded.
|
||||
mDecoder->GetMonitor().NotifyAll();
|
||||
|
||||
{
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
|
||||
if (!IsPlaying()) {
|
||||
// Update the ready state, so that the play DOM events fire. We only
|
||||
// need to do this if we're not playing; if we're playing the playback
|
||||
// code will do an update whenever it advances a frame.
|
||||
UpdateReadyState();
|
||||
}
|
||||
|
||||
if (mState == DECODER_STATE_SHUTDOWN || mStopDecodeThreads) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((!HasAudio() || audioWait) &&
|
||||
(!HasVideo() || videoWait))
|
||||
{
|
||||
// All active bitstreams' decode is well ahead of the playback
|
||||
// position, we may as well wait for the playback to catch up.
|
||||
mon.Wait();
|
||||
}
|
||||
if (!IsPlaying()) {
|
||||
// Update the ready state, so that the play DOM events fire. We only
|
||||
// need to do this if we're not playing; if we're playing the playback
|
||||
// code will do an update whenever it advances a frame.
|
||||
UpdateReadyState();
|
||||
}
|
||||
}
|
||||
|
||||
if (mState != DECODER_STATE_SHUTDOWN &&
|
||||
!mStopDecodeThreads &&
|
||||
(!audioPlaying || (GetDecodedAudioDuration() >= ampleAudioThreshold &&
|
||||
audioQueue.GetSize() > 0))
|
||||
&&
|
||||
(!videoPlaying || videoQueue.GetSize() >= AMPLE_VIDEO_FRAMES))
|
||||
{
|
||||
// All active bitstreams' decode is well ahead of the playback
|
||||
// position, we may as well wait for the playback to catch up. Note the
|
||||
// audio push thread acquires and notifies the decoder monitor every time
|
||||
// it pops SoundData off the audio queue. So if the audio push thread pops
|
||||
// the last SoundData off the audio queue right after that queue reported
|
||||
// it was non-empty here, we'll receive a notification on the decoder
|
||||
// monitor which will wake us up shortly after we sleep, thus preventing
|
||||
// both the decode and audio push threads waiting at the same time.
|
||||
// See bug 620326.
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
} // End decode loop.
|
||||
|
||||
if (!mStopDecodeThreads &&
|
||||
mState != DECODER_STATE_SHUTDOWN &&
|
||||
mState != DECODER_STATE_SEEKING)
|
||||
{
|
||||
MonitorAutoEnter mon(mDecoder->GetMonitor());
|
||||
if (!mStopDecodeThreads &&
|
||||
mState != DECODER_STATE_SHUTDOWN &&
|
||||
mState != DECODER_STATE_SEEKING)
|
||||
{
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
mDecoder->GetMonitor().NotifyAll();
|
||||
}
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
mDecoder->GetMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
LOG(PR_LOG_DEBUG, ("Shutting down DecodeLoop this=%p", this));
|
||||
}
|
||||
|
||||
|
@ -370,6 +370,15 @@ protected:
|
||||
return mStartTime + mCurrentFrameTime;
|
||||
}
|
||||
|
||||
// Returns an upper bound on the number of milliseconds of audio that is
|
||||
// decoded and playable. This is the sum of the number of ms of audio which
|
||||
// is decoded and in the reader's audio queue, and the ms 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();
|
||||
|
||||
// Monitor 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
|
||||
|
Loading…
Reference in New Issue
Block a user