Bug 973408 - Move MediaDecoderStateMachine::DecodeLoop()'s local variables to class members so the function can be made reentrant in future. r=kinetik

This commit is contained in:
Chris Pearce
2014-03-11 11:44:08 +08:00
parent e485f83be5
commit 32baf29589
3 changed files with 114 additions and 79 deletions

View File

@@ -82,7 +82,7 @@ const int64_t AMPLE_AUDIO_USECS = 1000000;
const uint32_t SILENCE_BYTES_CHUNK = 32 * 1024;
// If we have fewer than LOW_VIDEO_FRAMES decoded frames, and
// we're not "pumping video", we'll skip the video up to the next keyframe
// we're not "prerolling video", we'll skip the video up to the next keyframe
// which is at or after the current playback position.
static const uint32_t LOW_VIDEO_FRAMES = 1;
@@ -170,6 +170,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mPlaybackRate(1.0),
mPreservesPitch(true),
mBasePosition(0),
mAmpleVideoFrames(2),
mLowAudioThresholdUsecs(LOW_AUDIO_USECS),
mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
mAudioCaptured(false),
mTransportSeekable(true),
mMediaSeekable(true),
@@ -193,29 +196,19 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
// only enable realtime mode when "media.realtime_decoder.enabled" is true.
// Only enable realtime mode when "media.realtime_decoder.enabled" is true.
if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
mRealTime = false;
mAmpleVideoFrames =
std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S;
mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS;
// If we've got more than mAmpleVideoFrames decoded video frames waiting in
// the video queue, we will not decode any more video frames until some have
// been consumed by the play state machine thread.
#if defined(MOZ_OMX_DECODER) || defined(MOZ_MEDIA_PLUGINS)
// On B2G and Android this is decided by a similar value which varies for
// each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
// number must be less than the OMX equivalent or gecko will think it is
// chronically starved of video frames. All decoders seen so far have a value
// of at least 4.
mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 3);
#else
mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 10);
#endif
if (mAmpleVideoFrames < 2) {
mAmpleVideoFrames = 2;
}
mVideoPrerollFrames = mRealTime ? 0 : GetAmpleVideoFrames() / 2;
mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2;
#ifdef XP_WIN
// Ensure high precision timers are enabled on Windows, otherwise the state
// machine thread isn't woken up at reliable intervals to set the next frame,
@@ -608,57 +601,30 @@ void MediaDecoderStateMachine::DecodeLoop()
AssertCurrentThreadInMonitor();
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
// We want to "pump" the decode until we've got a few frames decoded
// before we consider whether decode is falling behind.
bool audioPump = true;
bool videoPump = true;
// If the video decode is falling behind the audio, we'll start dropping the
// inter-frames up until the next keyframe which is at or before the current
// playback position. skipToNextKeyframe is true if we're currently
// skipping up to the next keyframe.
bool skipToNextKeyframe = false;
// Once we've decoded more than videoPumpThreshold video frames, we'll
// no longer be considered to be "pumping video".
const unsigned videoPumpThreshold = mRealTime ? 0 : GetAmpleVideoFrames() / 2;
// After the audio decode fills with more than audioPumpThreshold usecs
// of decoded audio, we'll start to check whether the audio or video decode
// is falling behind.
const unsigned audioPumpThreshold = mRealTime ? 0 : LOW_AUDIO_USECS * 2;
// Our local low audio threshold. We may increase this if we're slow to
// decode video frames, in order to reduce the chance of audio underruns.
int64_t lowAudioThreshold = LOW_AUDIO_USECS;
// Our local ample audio threshold. If we increase lowAudioThreshold, we'll
// also increase this too appropriately (we don't want lowAudioThreshold to
// be greater than ampleAudioThreshold, else we'd stop decoding!).
int64_t ampleAudioThreshold = AMPLE_AUDIO_USECS;
mIsAudioPrerolling = true;
mIsVideoPrerolling = true;
// Main decode loop.
bool videoPlaying = HasVideo();
bool audioPlaying = HasAudio();
while ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
!mStopDecodeThread &&
(videoPlaying || audioPlaying))
(mIsVideoDecoding || mIsAudioDecoding))
{
// 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 &&
if (mIsVideoPrerolling &&
(static_cast<uint32_t>(mReader->VideoQueue().GetSize())
>= videoPumpThreshold * mPlaybackRate))
>= mVideoPrerollFrames * mPlaybackRate))
{
videoPump = false;
mIsVideoPrerolling = 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 audio data before enabling the keyframe skip logic on audio.
if (audioPump && GetDecodedAudioDuration() >= audioPumpThreshold * mPlaybackRate) {
audioPump = false;
if (mIsAudioPrerolling &&
GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) {
mIsAudioPrerolling = false;
}
// We'll skip the video decode to the nearest keyframe if we're low on
@@ -668,27 +634,26 @@ void MediaDecoderStateMachine::DecodeLoop()
// soon anyway and we'll want to be able to display frames immediately
// after buffering finishes.
if (mState == DECODER_STATE_DECODING &&
!skipToNextKeyframe &&
videoPlaying &&
((!audioPump && audioPlaying && !mDidThrottleAudioDecoding &&
GetDecodedAudioDuration() < lowAudioThreshold * mPlaybackRate) ||
(!videoPump && videoPlaying && !mDidThrottleVideoDecoding &&
!mSkipToNextKeyFrame &&
mIsVideoDecoding &&
((!mIsAudioPrerolling && mIsAudioDecoding && !mDidThrottleAudioDecoding &&
GetDecodedAudioDuration() < mLowAudioThresholdUsecs * mPlaybackRate) ||
(!mIsVideoPrerolling && mIsVideoDecoding && !mDidThrottleVideoDecoding &&
(static_cast<uint32_t>(mReader->VideoQueue().GetSize())
< LOW_VIDEO_FRAMES * mPlaybackRate))) &&
!HasLowUndecodedData())
{
skipToNextKeyframe = true;
mSkipToNextKeyFrame = true;
DECODER_LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder.get()));
}
// Video decode.
bool throttleVideoDecoding = !videoPlaying || HaveEnoughDecodedVideo();
bool throttleVideoDecoding = !mIsVideoDecoding || HaveEnoughDecodedVideo();
if (mDidThrottleVideoDecoding && !throttleVideoDecoding) {
videoPump = true;
mIsVideoPrerolling = true;
}
mDidThrottleVideoDecoding = throttleVideoDecoding;
if (!throttleVideoDecoding)
{
if (!throttleVideoDecoding) {
// 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.
@@ -697,36 +662,38 @@ void MediaDecoderStateMachine::DecodeLoop()
int64_t currentTime = GetMediaTime();
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
TimeStamp start = TimeStamp::Now();
videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
mIsVideoDecoding = mReader->DecodeVideoFrame(mSkipToNextKeyFrame, currentTime);
decodeTime = TimeStamp::Now() - start;
if (!videoPlaying) {
if (!mIsVideoDecoding) {
// Playback ended for this stream, close the sample queue.
mReader->VideoQueue().Finish();
}
}
if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > lowAudioThreshold &&
if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
!HasLowUndecodedData())
{
lowAudioThreshold =
mLowAudioThresholdUsecs =
std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
ampleAudioThreshold = std::max(THRESHOLD_FACTOR * lowAudioThreshold,
ampleAudioThreshold);
mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
mAmpleAudioThresholdUsecs);
DECODER_LOG(PR_LOG_DEBUG,
("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld",
lowAudioThreshold, ampleAudioThreshold));
("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs));
}
}
// Audio decode.
bool throttleAudioDecoding = !audioPlaying || HaveEnoughDecodedAudio(ampleAudioThreshold * mPlaybackRate);
bool throttleAudioDecoding =
!mIsAudioDecoding ||
HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate);
if (mDidThrottleAudioDecoding && !throttleAudioDecoding) {
audioPump = true;
mIsAudioPrerolling = true;
}
mDidThrottleAudioDecoding = throttleAudioDecoding;
if (!mDidThrottleAudioDecoding) {
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
audioPlaying = mReader->DecodeAudioData();
if (!audioPlaying) {
mIsAudioDecoding = mReader->DecodeAudioData();
if (!mIsAudioDecoding) {
// Playback ended for this stream, close the sample queue.
mReader->AudioQueue().Finish();
}
@@ -744,7 +711,7 @@ void MediaDecoderStateMachine::DecodeLoop()
if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
!mStopDecodeThread &&
(videoPlaying || audioPlaying) &&
(mIsVideoDecoding || mIsAudioDecoding) &&
throttleAudioDecoding && throttleVideoDecoding)
{
// All active bitstreams' decode is well ahead of the playback
@@ -1382,6 +1349,14 @@ void MediaDecoderStateMachine::StartDecoding()
mDecodeStartTime = TimeStamp::Now();
}
mState = DECODER_STATE_DECODING;
// Reset our "stream finished decoding" flags, so we try to decode all
// streams that we have when we start decoding.
mIsVideoDecoding = HasVideo();
mIsAudioDecoding = HasAudio();
mSkipToNextKeyFrame = false;
ScheduleStateMachine();
}