#include "VideoPlayoutScheduler.h" void VideoPlayoutScheduler::Configure(int64_t frameDuration, int64_t timeScale) { Configure(frameDuration, timeScale, VideoPlayoutPolicy()); } void VideoPlayoutScheduler::Configure(int64_t frameDuration, int64_t timeScale, const VideoPlayoutPolicy& policy) { mFrameDuration = frameDuration; mTimeScale = timeScale; mPolicy = NormalizeVideoPlayoutPolicy(policy); Reset(); } void VideoPlayoutScheduler::Reset() { mScheduledFrameIndex = 0; mCompletedFrameIndex = 0; mLateStreak = 0; mDropStreak = 0; } VideoIOScheduleTime VideoPlayoutScheduler::NextScheduleTime() { VideoIOScheduleTime time; time.streamTime = static_cast(mScheduledFrameIndex) * mFrameDuration; time.duration = mFrameDuration; time.timeScale = mTimeScale; time.frameIndex = mScheduledFrameIndex; ++mScheduledFrameIndex; return time; } void VideoPlayoutScheduler::AlignNextScheduleTimeToPlayback(int64_t streamTime, uint64_t leadFrames) { if (mFrameDuration <= 0 || streamTime < 0) return; const uint64_t playbackFrameIndex = static_cast(streamTime / mFrameDuration); const uint64_t minimumScheduleIndex = playbackFrameIndex + leadFrames; if (minimumScheduleIndex > mScheduledFrameIndex) mScheduledFrameIndex = minimumScheduleIndex; } VideoPlayoutRecoveryDecision VideoPlayoutScheduler::AccountForCompletionResult(VideoIOCompletionResult result, uint64_t readyQueueDepth) { ++mCompletedFrameIndex; if (result == VideoIOCompletionResult::DisplayedLate) ++mLateStreak; else mLateStreak = 0; if (result == VideoIOCompletionResult::Dropped) ++mDropStreak; else mDropStreak = 0; const uint64_t measuredLagFrames = MeasureLag(result, readyQueueDepth); const uint64_t catchUpFrames = measuredLagFrames < mPolicy.lateOrDropCatchUpFrames ? measuredLagFrames : mPolicy.lateOrDropCatchUpFrames; if (catchUpFrames > 0) mScheduledFrameIndex += catchUpFrames; VideoPlayoutRecoveryDecision decision; decision.result = result; decision.completedFrameIndex = mCompletedFrameIndex; decision.scheduledFrameIndex = mScheduledFrameIndex; decision.readyQueueDepth = readyQueueDepth; decision.scheduledLeadFrames = mScheduledFrameIndex > mCompletedFrameIndex ? mScheduledFrameIndex - mCompletedFrameIndex : 0; decision.measuredLagFrames = measuredLagFrames; decision.catchUpFrames = catchUpFrames; decision.lateStreak = mLateStreak; decision.dropStreak = mDropStreak; return decision; } double VideoPlayoutScheduler::FrameBudgetMilliseconds() const { return mTimeScale != 0 ? (static_cast(mFrameDuration) * 1000.0) / static_cast(mTimeScale) : 0.0; } uint64_t VideoPlayoutScheduler::MeasureLag(VideoIOCompletionResult result, uint64_t readyQueueDepth) const { if (result != VideoIOCompletionResult::DisplayedLate && result != VideoIOCompletionResult::Dropped) return 0; uint64_t lagFrames = 1; if (result == VideoIOCompletionResult::DisplayedLate && mLateStreak > lagFrames) lagFrames = mLateStreak; if (result == VideoIOCompletionResult::Dropped && mDropStreak * 2 > lagFrames) lagFrames = mDropStreak * 2; if (mCompletedFrameIndex >= mScheduledFrameIndex) { const uint64_t scheduleLagFrames = mCompletedFrameIndex - mScheduledFrameIndex + 1; if (scheduleLagFrames > lagFrames) lagFrames = scheduleLagFrames; } if (readyQueueDepth < mPolicy.targetReadyFrames && mPolicy.targetReadyFrames - readyQueueDepth > lagFrames) lagFrames = mPolicy.targetReadyFrames - readyQueueDepth; return lagFrames; }