From 9b5b09a6c189cbe6347604ecc934d8d78d857fd4 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Mon, 9 Oct 2017 16:35:16 -0700 Subject: [PATCH] #434: adjust video queue settings; Mach factor decode delay logic (default off) --- dom/media/MediaDecoderStateMachine.cpp | 136 ++++++++++++++++++++----- dom/media/MediaDecoderStateMachine.h | 3 + modules/libpref/init/all.js | 8 -- 3 files changed, 115 insertions(+), 32 deletions(-) diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 708f948bd..d9fce0997 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -48,6 +48,10 @@ #include "VideoSegment.h" #include "VideoUtils.h" +#include +#include +#include + namespace mozilla { using namespace mozilla::dom; @@ -85,7 +89,7 @@ namespace detail { // trying to decode the video, we'll skip decoding video up to the next // keyframe. We may increase this value for an individual decoder if we // encounter video frames which take a long time to decode. -static const uint32_t LOW_AUDIO_USECS = 1000000; +static const uint32_t LOW_AUDIO_USECS = 300000; // If more than this many usecs of decoded audio is queued, we'll hold off // decoding more audio. If we increase the low audio threshold (see @@ -105,7 +109,7 @@ const int64_t NO_VIDEO_AMPLE_AUDIO_DIVISOR = 8; // If we have fewer than LOW_VIDEO_FRAMES decoded frames, and // 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 = 8; +static const uint32_t LOW_VIDEO_FRAMES = 8; // Threshold in usecs that used to check if we are low on decoded video. // If the last video frame's end time |mDecodedVideoEndTime| is more than @@ -128,7 +132,7 @@ namespace detail { // ourselves to be running low on undecoded data. We determine how much // undecoded data we have remaining using the reader's GetBuffered() // implementation. -static const int64_t LOW_DATA_THRESHOLD_USECS = 20000000; +static const int64_t LOW_DATA_THRESHOLD_USECS = 5000000; // LOW_DATA_THRESHOLD_USECS needs to be greater than AMPLE_AUDIO_USECS, otherwise // the skip-to-keyframe logic can activate when we're running low on data. @@ -174,9 +178,9 @@ static int64_t DurationToUsecs(TimeDuration aDuration) { return static_cast(aDuration.ToSeconds() * USECS_PER_S); } -static const uint32_t MIN_VIDEO_QUEUE_SIZE = 30; -static const uint32_t MAX_VIDEO_QUEUE_SIZE = 30; -static const uint32_t VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE = 9999; +static const uint32_t MIN_VIDEO_QUEUE_SIZE = 500; +static const uint32_t MAX_VIDEO_QUEUE_SIZE = 500; +static const uint32_t VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE = 45; static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE; static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE; @@ -185,9 +189,25 @@ static uint32_t sVideoQueueSendToCompositorSize = VIDEO_QUEUE_SEND_TO_COMPOSITOR // TenFourFox issue 434 // Seconds to stall the video decoder on initial startup to allow sufficient // buildup in memory and other items onscreen to render. -// Currently disabled by default. +// Currently disabled by default since it has odd behaviour with short +// videos and ads. static const uint32_t DEFAULT_VIDEO_DECODE_STARTUP_DELAY = 0; static uint32_t sVideoDecodeStartupDelay = DEFAULT_VIDEO_DECODE_STARTUP_DELAY; +// Settings for Mach factor cap controller. +// Originally this was load-average based, hence the variable names. +// Currently disabled by default pending additional testing. +static const uint32_t LOAD_AVERAGE_MAX = 450; +static const uint32_t LOAD_AVERAGE_DELAY = 5; +static const uint32_t MAX_LOAD_AVERAGE_DELAYS = 0; +static uint32_t sLoadAverageMax = LOAD_AVERAGE_MAX; +static uint32_t sLoadAverageDelay = LOAD_AVERAGE_DELAY; +static uint32_t sMaxLoadAverageDelays = MAX_LOAD_AVERAGE_DELAYS; +// Caches for Mach factor monitoring. +// These can be singletons since they're shared over the entire machine. +static processor_set_name_port_t sMachDefaultPset; +static struct processor_set_load_info sMachLoadInfo; +static host_name_port_t sMachHost; +static kern_return_t sMachLastKernelReturn; static void InitVideoQueuePrefs() { MOZ_ASSERT(NS_IsMainThread()); @@ -200,8 +220,29 @@ static void InitVideoQueuePrefs() { "media.video-queue.hw-accel-size", MIN_VIDEO_QUEUE_SIZE); sVideoQueueSendToCompositorSize = Preferences::GetUint( "media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE); + + // TenFourFox-specific state machine settings. + // If decode delay == 0, do not introduce a decoding delay before playback. sVideoDecodeStartupDelay = Preferences::GetUint( "tenfourfox.media.decode_delay", DEFAULT_VIDEO_DECODE_STARTUP_DELAY); + sLoadAverageMax = Preferences::GetUint( + "tenfourfox.media.mach_factor_min", LOAD_AVERAGE_MAX); + // If Mach factor delay == 0, then instead of a pause, check buffering. + sLoadAverageDelay = Preferences::GetUint( + "tenfourfox.media.mach_factor_delay", LOAD_AVERAGE_DELAY); + // If number of tries == 0, don't do Mach factor monitoring. + sMaxLoadAverageDelays = Preferences::GetUint( + "tenfourfox.media.mach_factor_max_tries", MAX_LOAD_AVERAGE_DELAYS); + + // Precompute variables for Mach factor/load monitoring. + // These can be singletons since they're shared over the entire machine. + // If this fails, warn, and then disable load average throttling. + sMachHost = mach_host_self(); + kern_return_t ret = processor_set_default(sMachHost, &sMachDefaultPset); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "Unable to initialize Mach video load monitoring: %i\n", (uint32_t)ret); + sMaxLoadAverageDelays = 0; + } } } @@ -297,6 +338,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, mTaskQueue->Dispatch(r.forget()); InitVideoQueuePrefs(); + mSystemLoadRetries = sMaxLoadAverageDelays; mBufferingWait = IsRealTime() ? 0 : 15; mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS; @@ -468,15 +510,24 @@ void MediaDecoderStateMachine::DiscardStreamData() // TenFourFox. Don't try to push video frames that are already past. // This just wastes time in the compositor. - while(true) { - const MediaData* v = VideoQueue().PeekFront(); + // Only bother doing this for the compositor queue size, because we'll only + // ever push that much to it anyway (otherwise this uses more CPU time than + // it saves). + size_t stopat = sVideoQueueSendToCompositorSize; + size_t framesDropped = 0; + while(stopat) { + stopat--; + const MediaData* v = VideoQueue().PeekFront(); + if (v && v->mTime < clockTime) { + RefPtr releaseMe = VideoQueue().PopFront(); + framesDropped++; + continue; + } + break; + } + if (framesDropped) { FrameStatistics& frameStats = *mFrameStats; - if (v && v->mTime < clockTime) { - RefPtr releaseMe = VideoQueue().PopFront(); - frameStats.NotifyDecodedFrames(0, 0, 1); // Frame dropped - continue; - } - break; + frameStats.NotifyDecodedFrames(0, 0, framesDropped); } } @@ -497,6 +548,9 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) bool MediaDecoderStateMachine::HaveEnoughDecodedVideo() { MOZ_ASSERT(OnTaskQueue()); + + if (mState == DECODER_STATE_COMPLETED) + return true; if (VideoQueue().GetSize() == 0) { return false; @@ -1176,6 +1230,37 @@ MediaDecoderStateMachine::MaybeStartBuffering() shouldBuffer = (OutOfDecodedAudio() && mAudioWaitRequest.Exists()) || (OutOfDecodedVideo() && mVideoWaitRequest.Exists()); } + + // TenFourFox issue 434 + if (MOZ_LIKELY(!shouldBuffer && sMachLastKernelReturn == KERN_SUCCESS) && + HasVideo() && + mSystemLoadRetries && + IsPlaying()) { + if (sMachLoadInfo.mach_factor < sLoadAverageMax) { +fprintf(stderr, "TenFourFox: Video throttled due to low Mach factor: %d (cap: %d / retries: %d)\n", sMachLoadInfo.mach_factor, sLoadAverageMax, mSystemLoadRetries); + + // Halt playback; too much is going on to play video well. + mDelayedScheduler.Reset(); // Must happen on state machine task queue. + mDispatchedStateMachine = false; + + if (IsPlaying()) StopPlayback(); // paranoia + + if (sLoadAverageDelay) { + // Switch to decode delay, even if we don't use it initially. + // When the state returns to decode delay, + // we will decrement the load retries count. This means + // any number of threads can get quieted. + SetState(DECODER_STATE_STARTUP_DELAY); + ScheduleStateMachineIn(sLoadAverageDelay * USECS_PER_S); + return; + } + + // else, try to buffer instead + shouldBuffer = true; + mSystemLoadRetries--; + } + } + if (shouldBuffer) { StartBuffering(); // Don't go straight back to the state machine loop since that might @@ -1807,6 +1892,7 @@ MediaDecoderStateMachine::RequestVideoData() bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent && NeedToSkipToNextKeyframe(); + int64_t currentTime = mState == DECODER_STATE_SEEKING || !mSentFirstFrameLoadedEvent ? 0 : GetMediaTime() + StartTime(); @@ -2052,9 +2138,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata) // TenFourFox issue 434 SetState(DECODER_STATE_STARTUP_DELAY); if (HasVideo() && MOZ_LIKELY(sVideoDecodeStartupDelay > 0)) { - FrameStatistics& frameStats = *mFrameStats; - // Fake out MSE by saying we've already dropped a crapload of frames. - frameStats.NotifyDecodedFrames(0, 0, 4000); // Stall a bit to let everything load. ScheduleStateMachineIn(USECS_PER_S * sVideoDecodeStartupDelay); return; @@ -2309,6 +2392,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine() MediaResource* resource = mResource; NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER); + + // TenFourFox issue 434 + if (mSystemLoadRetries) { + size_t count = PROCESSOR_SET_LOAD_INFO_COUNT; + sMachLastKernelReturn = processor_set_statistics(sMachDefaultPset, + PROCESSOR_SET_LOAD_INFO, + (processor_set_info_t)&sMachLoadInfo, &count); + } switch (mState) { case DECODER_STATE_ERROR: @@ -2340,16 +2431,13 @@ nsresult MediaDecoderStateMachine::RunStateMachine() // TenFourFox issue 434 case DECODER_STATE_STARTUP_DELAY: { + // Only update this here, as a hal-fassed mutex. + if (mSystemLoadRetries) mSystemLoadRetries--; + // We have to implement this as a separate phase, because we don't // know if we haz video until after metadata is read. StartDecoding(); - if (HasVideo() && MOZ_LIKELY(sVideoDecodeStartupDelay > 0)) { - FrameStatistics& frameStats = *mFrameStats; - // Fake out MSE by saying we've dropped a crapload more of frames. - frameStats.NotifyDecodedFrames(0, 0, 4000); - } - ScheduleStateMachine(); return NS_OK; } diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 5b8934486..54f4b8b9b 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -1214,6 +1214,9 @@ private: // True if audio is offloading. // Playback will not start when audio is offloading. bool mAudioOffloading; + + // Number of maximum tries to stall and buffer based on system load. + size_t mSystemLoadRetries; #ifdef MOZ_EME void OnCDMProxyReady(RefPtr aProxy); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index fcc2321c2..0acba78c1 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -516,14 +516,6 @@ pref("media.encoder.omx.enabled", true); // Whether to autostart a media element with an |autoplay| attribute pref("media.autoplay.enabled", true); -// The default number of decoded video frames that are enqueued in -// MediaDecoderReader's mVideoQueue. -pref("media.video-queue.default-size", 10); - -// The maximum number of queued frames to send to the compositor. -// By default, send all of them. -pref("media.video-queue.send-to-compositor-size", 9999); - // Whether to disable the video stats to prevent fingerprinting pref("media.video_stats.enabled", true);