#include "VideoPlayoutScheduler.h" #include #include namespace { int gFailures = 0; void Expect(bool condition, const char* message) { if (condition) return; std::cerr << "FAIL: " << message << "\n"; ++gFailures; } void ExpectNear(double actual, double expected, double tolerance, const char* message) { Expect(std::fabs(actual - expected) <= tolerance, message); } void TestScheduleAdvancesFromZero() { VideoPlayoutScheduler scheduler; scheduler.Configure(1001, 60000); const VideoIOScheduleTime first = scheduler.NextScheduleTime(); const VideoIOScheduleTime second = scheduler.NextScheduleTime(); const VideoIOScheduleTime third = scheduler.NextScheduleTime(); Expect(first.streamTime == 0, "first frame starts at stream time zero"); Expect(first.duration == 1001, "duration is preserved"); Expect(first.timeScale == 60000, "time scale is preserved"); Expect(second.streamTime == 1001, "second frame advances by one duration"); Expect(third.streamTime == 2002, "third frame advances by two durations"); } void TestLateAndDroppedRecoveryUsesMeasuredPressure() { VideoPlayoutScheduler scheduler; scheduler.Configure(1000, 50000); (void)scheduler.NextScheduleTime(); VideoPlayoutRecoveryDecision lateDecision = scheduler.AccountForCompletionResult(VideoIOCompletionResult::DisplayedLate, 2); Expect(lateDecision.catchUpFrames == 1, "single late completion catches up by measured one-frame lag"); Expect(lateDecision.lateStreak == 1, "late completion increments late streak"); Expect(scheduler.NextScheduleTime().streamTime == 2000, "single late recovery advances by measured lag"); VideoPlayoutRecoveryDecision dropDecision = scheduler.AccountForCompletionResult(VideoIOCompletionResult::Dropped, 2); Expect(dropDecision.catchUpFrames == 2, "dropped completion catches up by measured drop pressure"); Expect(dropDecision.lateStreak == 0, "dropped completion resets late streak"); Expect(dropDecision.dropStreak == 1, "dropped completion increments drop streak"); Expect(scheduler.NextScheduleTime().streamTime == 5000, "drop recovery advances by measured lag"); } void TestMeasuredRecoveryIsCappedByPolicy() { VideoPlayoutPolicy policy; policy.lateOrDropCatchUpFrames = 1; VideoPlayoutScheduler scheduler; scheduler.Configure(1000, 50000, policy); (void)scheduler.NextScheduleTime(); VideoPlayoutRecoveryDecision decision = scheduler.AccountForCompletionResult(VideoIOCompletionResult::Dropped, 0); Expect(decision.measuredLagFrames > decision.catchUpFrames, "policy caps measured recovery"); Expect(decision.catchUpFrames == 1, "drop recovery obeys policy cap"); Expect(scheduler.NextScheduleTime().streamTime == 2000, "capped recovery advances by one frame"); } void TestCleanCompletionTracksCompletedIndexAndClearsStreaks() { VideoPlayoutScheduler scheduler; scheduler.Configure(1000, 50000); (void)scheduler.NextScheduleTime(); (void)scheduler.AccountForCompletionResult(VideoIOCompletionResult::DisplayedLate, 2); VideoPlayoutRecoveryDecision decision = scheduler.AccountForCompletionResult(VideoIOCompletionResult::Completed, 2); Expect(decision.completedFrameIndex == 2, "completion accounting tracks completed index"); Expect(decision.catchUpFrames == 0, "clean completion does not catch up"); Expect(decision.lateStreak == 0, "clean completion clears late streak"); Expect(decision.dropStreak == 0, "clean completion keeps drop streak clear"); } void TestPolicyNormalization() { VideoPlayoutPolicy policy; policy.outputFramePoolSize = 0; policy.targetPrerollFrames = 0; policy.targetReadyFrames = 5; policy.maxReadyFrames = 2; VideoPlayoutPolicy normalized = NormalizeVideoPlayoutPolicy(policy); Expect(normalized.outputFramePoolSize == 1, "policy normalization keeps at least one output frame"); Expect(normalized.targetPrerollFrames == 1, "policy normalization keeps at least one preroll frame"); Expect(normalized.maxReadyFrames == normalized.targetReadyFrames, "policy normalization keeps max ready frames above target"); } void TestFrameBudgets() { VideoPlayoutScheduler scheduler; scheduler.Configure(1000, 50000); ExpectNear(scheduler.FrameBudgetMilliseconds(), 20.0, 0.0001, "50 fps budget"); scheduler.Configure(1001, 60000); ExpectNear(scheduler.FrameBudgetMilliseconds(), 16.6833, 0.0001, "59.94 fps budget"); scheduler.Configure(1, 60); ExpectNear(scheduler.FrameBudgetMilliseconds(), 16.6667, 0.0001, "60 fps budget"); } } int main() { TestScheduleAdvancesFromZero(); TestLateAndDroppedRecoveryUsesMeasuredPressure(); TestMeasuredRecoveryIsCappedByPolicy(); TestCleanCompletionTracksCompletedIndexAndClearsStreaks(); TestPolicyNormalization(); TestFrameBudgets(); if (gFailures != 0) { std::cerr << gFailures << " VideoPlayoutScheduler test failure(s).\n"; return 1; } std::cout << "VideoPlayoutScheduler tests passed.\n"; return 0; }