#pragma once #include #include #include #include // HealthTelemetry owns the current operational status snapshot directly, so // callers can report health without sharing runtime-store state. class HealthTelemetry { public: struct SignalStatusSnapshot { bool hasSignal = false; unsigned width = 0; unsigned height = 0; std::string modeName; }; struct VideoIOStatusSnapshot { std::string backendName = "decklink"; std::string modelName; bool supportsInternalKeying = false; bool supportsExternalKeying = false; bool keyerInterfaceAvailable = false; bool externalKeyingRequested = false; bool externalKeyingActive = false; std::string statusMessage; }; struct PerformanceSnapshot { double frameBudgetMilliseconds = 0.0; double renderMilliseconds = 0.0; double smoothedRenderMilliseconds = 0.0; double completionIntervalMilliseconds = 0.0; double smoothedCompletionIntervalMilliseconds = 0.0; double maxCompletionIntervalMilliseconds = 0.0; uint64_t lateFrameCount = 0; uint64_t droppedFrameCount = 0; uint64_t flushedFrameCount = 0; }; struct RuntimeEventQueueSnapshot { std::string queueName = "runtime-events"; std::size_t depth = 0; std::size_t capacity = 0; uint64_t droppedCount = 0; double oldestEventAgeMilliseconds = 0.0; }; struct RuntimeEventDispatchSnapshot { uint64_t dispatchCallCount = 0; uint64_t dispatchedEventCount = 0; uint64_t handlerInvocationCount = 0; uint64_t handlerFailureCount = 0; double lastDispatchDurationMilliseconds = 0.0; double maxDispatchDurationMilliseconds = 0.0; }; struct RuntimeEventMetricsSnapshot { RuntimeEventQueueSnapshot queue; RuntimeEventDispatchSnapshot dispatch; }; struct PersistenceSnapshot { uint64_t writeSuccessCount = 0; uint64_t writeFailureCount = 0; bool lastWriteSucceeded = true; bool unsavedChanges = false; bool newerRequestPending = false; std::string lastTargetKind; std::string lastTargetPath; std::string lastReason; std::string lastErrorMessage; }; struct BackendPlayoutSnapshot { std::string lifecycleState = "NotStarted"; std::string completionResult = "Unknown"; std::size_t readyQueueDepth = 0; std::size_t readyQueueCapacity = 0; std::size_t minReadyQueueDepth = 0; std::size_t maxReadyQueueDepth = 0; uint64_t readyQueueZeroDepthCount = 0; uint64_t readyQueuePushedCount = 0; uint64_t readyQueuePoppedCount = 0; uint64_t readyQueueDroppedCount = 0; uint64_t readyQueueUnderrunCount = 0; std::size_t systemFramePoolFree = 0; std::size_t systemFramePoolReady = 0; std::size_t systemFramePoolScheduled = 0; uint64_t systemFrameUnderrunCount = 0; uint64_t systemFrameRepeatCount = 0; uint64_t systemFrameDropCount = 0; double systemFrameAgeAtScheduleMilliseconds = 0.0; double systemFrameAgeAtCompletionMilliseconds = 0.0; double outputRenderMilliseconds = 0.0; double smoothedOutputRenderMilliseconds = 0.0; double maxOutputRenderMilliseconds = 0.0; double outputFrameAcquireMilliseconds = 0.0; double outputFrameRenderRequestMilliseconds = 0.0; double outputFrameEndAccessMilliseconds = 0.0; double outputRenderQueueWaitMilliseconds = 0.0; double outputRenderDrawMilliseconds = 0.0; double outputReadbackFenceWaitMilliseconds = 0.0; double outputReadbackMapMilliseconds = 0.0; double outputReadbackCopyMilliseconds = 0.0; double outputCachedCopyMilliseconds = 0.0; double outputAsyncQueueMilliseconds = 0.0; double outputAsyncQueueBufferMilliseconds = 0.0; double outputAsyncQueueSetupMilliseconds = 0.0; double outputAsyncQueueReadPixelsMilliseconds = 0.0; double outputAsyncQueueFenceMilliseconds = 0.0; double outputSyncReadMilliseconds = 0.0; uint64_t outputAsyncReadbackMissCount = 0; uint64_t outputCachedFallbackCount = 0; uint64_t outputSyncFallbackCount = 0; uint64_t completedFrameIndex = 0; uint64_t scheduledFrameIndex = 0; uint64_t scheduledLeadFrames = 0; bool actualDeckLinkBufferedFramesAvailable = false; uint64_t actualDeckLinkBufferedFrames = 0; std::size_t targetDeckLinkBufferedFrames = 0; double deckLinkScheduleCallMilliseconds = 0.0; uint64_t deckLinkScheduleFailureCount = 0; uint64_t measuredLagFrames = 0; uint64_t catchUpFrames = 0; uint64_t lateStreak = 0; uint64_t dropStreak = 0; uint64_t lateFrameCount = 0; uint64_t droppedFrameCount = 0; uint64_t flushedFrameCount = 0; bool degraded = false; std::string statusMessage; }; struct Snapshot { SignalStatusSnapshot signal; VideoIOStatusSnapshot videoIO; PerformanceSnapshot performance; RuntimeEventMetricsSnapshot runtimeEvents; PersistenceSnapshot persistence; BackendPlayoutSnapshot backendPlayout; }; HealthTelemetry() = default; void ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); bool TryReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); void ReportVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage); bool TryReportVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage); void RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); bool TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); void RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds, double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount); bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds, double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount); void RecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity, uint64_t droppedCount, double oldestEventAgeMilliseconds); bool TryRecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity, uint64_t droppedCount, double oldestEventAgeMilliseconds); void RecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations, std::size_t handlerFailures, double dispatchDurationMilliseconds); bool TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations, std::size_t handlerFailures, double dispatchDurationMilliseconds); void RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath, const std::string& reason, const std::string& errorMessage, bool newerRequestPending); bool TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath, const std::string& reason, const std::string& errorMessage, bool newerRequestPending); void RecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult, std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount, std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount, uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount, double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds, double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds, uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames, uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount, bool degraded, const std::string& statusMessage); bool TryRecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult, std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount, std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount, uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount, double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds, double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds, uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames, uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount, bool degraded, const std::string& statusMessage); void RecordOutputRenderQueueWait(double queueWaitMilliseconds); bool TryRecordOutputRenderQueueWait(double queueWaitMilliseconds); void RecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount, std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount, double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds); bool TryRecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount, std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount, double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds); void RecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames, std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount); bool TryRecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames, std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount); void RecordOutputRenderPipelineTiming( double drawMilliseconds, double fenceWaitMilliseconds, double mapMilliseconds, double readbackCopyMilliseconds, double cachedCopyMilliseconds, double asyncQueueMilliseconds, double asyncQueueBufferMilliseconds, double asyncQueueSetupMilliseconds, double asyncQueueReadPixelsMilliseconds, double asyncQueueFenceMilliseconds, double syncReadMilliseconds, bool asyncReadbackMissed, bool cachedFallbackUsed, bool syncFallbackUsed); bool TryRecordOutputRenderPipelineTiming( double drawMilliseconds, double fenceWaitMilliseconds, double mapMilliseconds, double readbackCopyMilliseconds, double cachedCopyMilliseconds, double asyncQueueMilliseconds, double asyncQueueBufferMilliseconds, double asyncQueueSetupMilliseconds, double asyncQueueReadPixelsMilliseconds, double asyncQueueFenceMilliseconds, double syncReadMilliseconds, bool asyncReadbackMissed, bool cachedFallbackUsed, bool syncFallbackUsed); SignalStatusSnapshot GetSignalStatusSnapshot() const; VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const; PerformanceSnapshot GetPerformanceSnapshot() const; RuntimeEventMetricsSnapshot GetRuntimeEventMetricsSnapshot() const; PersistenceSnapshot GetPersistenceSnapshot() const; BackendPlayoutSnapshot GetBackendPlayoutSnapshot() const; Snapshot GetSnapshot() const; private: mutable std::mutex mMutex; SignalStatusSnapshot mSignalStatus; VideoIOStatusSnapshot mVideoIOStatus; PerformanceSnapshot mPerformance; RuntimeEventMetricsSnapshot mRuntimeEvents; PersistenceSnapshot mPersistence; BackendPlayoutSnapshot mBackendPlayout; };