зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
8e35be685e
Коммит
bc8ada7974
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче