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
Родитель 8e35be685e
Коммит bc8ada7974
3 изменённых файлов: 114 добавлений и 79 удалений

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

@ -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();
}

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

@ -740,6 +740,58 @@ private:
// the video queue, we will not decode any more video frames until some have
// been consumed by the play state machine thread.
uint32_t mAmpleVideoFrames;
// Low audio threshold. If we've decoded less than this much audio we
// consider our audio decode "behind", and we may skip video decoding
// in order to allow our audio decoding to catch up. We favour audio
// decoding over video. We increase this threshold if we're slow to
// decode video frames, in order to reduce the chance of audio underruns.
// Note that we don't ever reset this threshold, it only ever grows as
// we detect that the decode can't keep up with rendering.
int64_t mLowAudioThresholdUsecs;
// Our "ample" audio threshold. Once we've this much audio decoded, we
// pause decoding. If we increase mLowAudioThresholdUsecs, we'll also
// increase this too appropriately (we don't want mLowAudioThresholdUsecs
// to be greater than ampleAudioThreshold, else we'd stop decoding!).
// Note that we don't ever reset this threshold, it only ever grows as
// we detect that the decode can't keep up with rendering.
int64_t mAmpleAudioThresholdUsecs;
// At the start of decoding we want to "preroll" the decode until we've
// got a few frames decoded before we consider whether decode is falling
// behind. Otherwise our "we're falling behind" logic will trigger
// unneccessarily if we start playing as soon as the first sample is
// decoded. These two fields store how many video frames and audio
// samples we must consume before are considered to be finished prerolling.
uint32_t mAudioPrerollUsecs;
uint32_t mVideoPrerollFrames;
// When we start decoding (either for the first time, or after a pause)
// we may be low on decoded data. We don't want our "low data" logic to
// kick in and decide that we're low on decoded data because the download
// can't keep up with the decode, and cause us to pause playback. So we
// have a "preroll" stage, where we ignore the results of our "low data"
// logic during the first few frames of our decode. This occurs during
// playback. The flags below are true when the corresponding stream is
// being "prerolled".
bool mIsAudioPrerolling;
bool mIsVideoPrerolling;
// True when we have an audio stream that we're decoding, and we have not
// yet decoded to end of stream.
bool mIsAudioDecoding;
// True when we have a video stream that we're decoding, and we have not
// yet decoded to end of stream.
bool mIsVideoDecoding;
// 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 mSkipToNextKeyFrame;
// True if we shouldn't play our audio (but still write it to any capturing
// streams). When this is true, mStopAudioThread is always true and
// the audio thread will never start again after it has stopped.
@ -817,8 +869,8 @@ private:
bool mRealTime;
// Record whether audio and video decoding were throttled during the
// previous iteration of DecodeLooop. When we transition from
// throttled to not-throttled we need to pump decoding.
// previous iteration of DecodeLoop. When we transition from
// throttled to not-throttled we need to preroll decoding.
bool mDidThrottleAudioDecoding;
bool mDidThrottleVideoDecoding;

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

@ -574,6 +574,14 @@ pref("dom.indexedDB.warningQuota", 5);
pref("media.preload.default", 1); // default to preload none
pref("media.preload.auto", 2); // preload metadata if preload=auto
// Number of video frames we buffer while decoding video.
// On 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.
pref("media.video-queue.default-size", 3);
// optimize images memory usage
pref("image.mem.decodeondraw", true);
pref("image.mem.min_discard_timeout_ms", 10000);