#include "OutputProductionController.h" #include namespace { std::size_t ClampReadyLimit(unsigned value, std::size_t capacity) { const std::size_t requested = static_cast(value); if (capacity == 0) return requested; return (std::min)(requested, capacity); } } OutputProductionController::OutputProductionController(const VideoPlayoutPolicy& policy) : mPolicy(NormalizeVideoPlayoutPolicy(policy)) { } void OutputProductionController::Configure(const VideoPlayoutPolicy& policy) { mPolicy = NormalizeVideoPlayoutPolicy(policy); } OutputProductionDecision OutputProductionController::Decide(const OutputProductionPressure& pressure) const { OutputProductionDecision decision; const std::size_t configuredMaxReadyFrames = static_cast(mPolicy.maxReadyFrames); const std::size_t effectiveMaxReadyFrames = pressure.readyQueueCapacity > 0 ? (std::min)(configuredMaxReadyFrames, pressure.readyQueueCapacity) : configuredMaxReadyFrames; const std::size_t effectiveTargetReadyFrames = (std::min)( ClampReadyLimit(mPolicy.targetReadyFrames, pressure.readyQueueCapacity), effectiveMaxReadyFrames); decision.targetReadyFrames = effectiveTargetReadyFrames; decision.maxReadyFrames = effectiveMaxReadyFrames; if (effectiveMaxReadyFrames == 0) { decision.action = OutputProductionAction::Throttle; decision.reason = "no-ready-frame-capacity"; return decision; } if (pressure.readyQueueDepth >= effectiveMaxReadyFrames) { decision.action = OutputProductionAction::Throttle; decision.reason = "ready-queue-full"; return decision; } if (pressure.readyQueueDepth < effectiveTargetReadyFrames) { decision.action = OutputProductionAction::Produce; decision.requestedFrames = effectiveTargetReadyFrames - pressure.readyQueueDepth; decision.reason = "ready-queue-below-target"; return decision; } if ((pressure.lateStreak > 0 || pressure.dropStreak > 0 || pressure.readyQueueUnderrunCount > 0) && pressure.readyQueueDepth < effectiveMaxReadyFrames) { decision.action = OutputProductionAction::Produce; decision.requestedFrames = 1; decision.reason = "playout-pressure"; return decision; } decision.action = OutputProductionAction::Wait; decision.reason = "ready-queue-at-target"; return decision; } const char* OutputProductionActionName(OutputProductionAction action) { switch (action) { case OutputProductionAction::Produce: return "Produce"; case OutputProductionAction::Throttle: return "Throttle"; case OutputProductionAction::Wait: default: return "Wait"; } }