#pragma once #include "../frames/SystemFrameTypes.h" #include "DeckLinkOutput.h" #include "VideoIOTypes.h" #include #include #include #include #include namespace RenderCadenceCompositor { struct DeckLinkOutputThreadConfig { std::size_t targetBufferedFrames = 4; std::chrono::milliseconds idleSleep = std::chrono::milliseconds(1); }; struct DeckLinkOutputThreadMetrics { uint64_t scheduledFrames = 0; uint64_t completedPollMisses = 0; uint64_t scheduleFailures = 0; }; template class DeckLinkOutputThread { public: DeckLinkOutputThread(DeckLinkOutput& output, SystemFrameExchange& exchange, DeckLinkOutputThreadConfig config = DeckLinkOutputThreadConfig()) : mOutput(output), mExchange(exchange), mConfig(config) { } DeckLinkOutputThread(const DeckLinkOutputThread&) = delete; DeckLinkOutputThread& operator=(const DeckLinkOutputThread&) = delete; ~DeckLinkOutputThread() { Stop(); } bool Start() { if (mRunning) return true; mStopping = false; mThread = std::thread([this]() { ThreadMain(); }); mRunning = true; return true; } void Stop() { mStopping = true; if (mThread.joinable()) mThread.join(); mRunning = false; } DeckLinkOutputThreadMetrics Metrics() const { DeckLinkOutputThreadMetrics metrics; metrics.scheduledFrames = mScheduledFrames.load(); metrics.completedPollMisses = mCompletedPollMisses.load(); metrics.scheduleFailures = mScheduleFailures.load(); return metrics; } private: void ThreadMain() { while (!mStopping) { const auto exchangeMetrics = mExchange.Metrics(); const auto outputMetrics = mOutput.Metrics(); const std::size_t bufferedFrames = outputMetrics.actualBufferedFramesAvailable ? static_cast(outputMetrics.actualBufferedFrames) : exchangeMetrics.scheduledCount; if (bufferedFrames >= mConfig.targetBufferedFrames) { std::this_thread::sleep_for(mConfig.idleSleep); continue; } SystemFrame frame; if (!mExchange.ConsumeCompletedForSchedule(frame)) { ++mCompletedPollMisses; std::this_thread::sleep_for(mConfig.idleSleep); continue; } VideoIOOutputFrame outputFrame; outputFrame.bytes = frame.bytes; outputFrame.nativeBuffer = frame.bytes; outputFrame.rowBytes = frame.rowBytes; outputFrame.width = frame.width; outputFrame.height = frame.height; outputFrame.pixelFormat = frame.pixelFormat; if (!mOutput.ScheduleFrame(outputFrame)) { ++mScheduleFailures; mExchange.ReleaseScheduledByBytes(frame.bytes); std::this_thread::sleep_for(mConfig.idleSleep); continue; } ++mScheduledFrames; } } DeckLinkOutput& mOutput; SystemFrameExchange& mExchange; DeckLinkOutputThreadConfig mConfig; std::thread mThread; std::atomic mStopping{ false }; std::atomic mRunning{ false }; std::atomic mScheduledFrames{ 0 }; std::atomic mCompletedPollMisses{ 0 }; std::atomic mScheduleFailures{ 0 }; }; }