Files
video-shader-toys/apps/RenderCadenceCompositor/video/DeckLinkOutputThread.h
Aiden e0ca548ef5
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m54s
CI / Windows Release Package (push) Successful in 3m14s
V2 working
2026-05-12 01:59:02 +10:00

125 lines
2.8 KiB
C++

#pragma once
#include "../frames/SystemFrameTypes.h"
#include "DeckLinkOutput.h"
#include "VideoIOTypes.h"
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <thread>
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 <typename SystemFrameExchange>
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();
if (exchangeMetrics.scheduledCount >= 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<bool> mStopping{ false };
std::atomic<bool> mRunning{ false };
std::atomic<uint64_t> mScheduledFrames{ 0 };
std::atomic<uint64_t> mCompletedPollMisses{ 0 };
std::atomic<uint64_t> mScheduleFailures{ 0 };
};
}