diff --git a/src/app/VideoBackendFactory.cpp b/src/app/VideoBackendFactory.cpp index def8f4e..72b3055 100644 --- a/src/app/VideoBackendFactory.cpp +++ b/src/app/VideoBackendFactory.cpp @@ -7,9 +7,13 @@ #include "video/decklink/DeckLinkOutput.h" #include +#include #include +#include #include +#include #include +#include namespace RenderCadenceCompositor { @@ -26,15 +30,9 @@ std::string NormalizeBackendName(std::string name) return name; } -bool IsDeckLinkBackend(const std::string& name) +bool BackendNameMatches(std::string_view normalizedName, const std::vector& aliases) { - return NormalizeBackendName(name) == "decklink"; -} - -bool IsNoneBackend(const std::string& name) -{ - const std::string normalized = NormalizeBackendName(name); - return normalized == "none" || normalized == "disabled" || normalized == "off"; + return std::find(aliases.begin(), aliases.end(), normalizedName) != aliases.end(); } bool WaitForInputWarmup(InputFrameMailbox& mailbox, std::size_t targetReadyFrames, std::chrono::milliseconds timeout) @@ -50,6 +48,32 @@ bool WaitForInputWarmup(InputFrameMailbox& mailbox, std::size_t targetReadyFrame return false; } +struct VideoInputBackendStartContext +{ + const AppConfig& config; + InputFrameMailbox& mailbox; + InputFrameMailboxConfig& mailboxConfig; + const VideoFormat& inputVideoMode; + bool inputVideoModeResolved = false; +}; + +using VideoOutputBackendCreateFn = std::unique_ptr (*)(); +using VideoInputBackendStartFn = std::unique_ptr (*)(const VideoInputBackendStartContext&); + +struct VideoOutputBackendRegistration +{ + std::vector aliases; + bool requiresCom = false; + VideoOutputBackendCreateFn create = nullptr; +}; + +struct VideoInputBackendRegistration +{ + std::vector aliases; + bool requiresCom = false; + VideoInputBackendStartFn start = nullptr; +}; + class DisabledVideoOutputEdge final : public IVideoOutputEdge { public: @@ -174,20 +198,95 @@ private: DeckLinkInputThread mThread; bool mStarted = false; }; + +std::unique_ptr CreateDeckLinkOutputBackend() +{ + return std::make_unique(); +} + +std::unique_ptr CreateDisabledOutputBackend() +{ + return std::make_unique("Video output backend is disabled by config."); +} + +std::unique_ptr StartNoInputBackend(const VideoInputBackendStartContext&) +{ + Log("app", "Video input backend disabled by config."); + return std::make_unique("none"); +} + +std::unique_ptr StartDeckLinkInputBackend(const VideoInputBackendStartContext& context) +{ + if (!context.inputVideoModeResolved) + { + LogWarning("app", "DeckLink input mode was not resolved; runtime shaders will use fallback input until a real input edge is available."); + return std::make_unique("decklink"); + } + + auto session = std::make_unique(context.mailbox); + std::string error; + if (!session->Start(context.mailbox, context.mailboxConfig, context.inputVideoMode, error)) + { + LogWarning("app", "DeckLink input edge unavailable; runtime shaders will use fallback input until a real input edge is available. " + error); + return std::make_unique("decklink"); + } + return session; +} + +const std::vector& VideoOutputBackendRegistry() +{ + static const std::vector registry = { + VideoOutputBackendRegistration{ { "decklink" }, true, &CreateDeckLinkOutputBackend }, + VideoOutputBackendRegistration{ { "none", "disabled", "off" }, false, &CreateDisabledOutputBackend }, + }; + return registry; +} + +const std::vector& VideoInputBackendRegistry() +{ + static const std::vector registry = { + VideoInputBackendRegistration{ { "decklink" }, true, &StartDeckLinkInputBackend }, + VideoInputBackendRegistration{ { "none", "disabled", "off" }, false, &StartNoInputBackend }, + }; + return registry; +} + +const VideoOutputBackendRegistration* FindVideoOutputBackend(std::string_view backendName) +{ + for (const VideoOutputBackendRegistration& backend : VideoOutputBackendRegistry()) + { + if (BackendNameMatches(backendName, backend.aliases)) + return &backend; + } + return nullptr; +} + +const VideoInputBackendRegistration* FindVideoInputBackend(std::string_view backendName) +{ + for (const VideoInputBackendRegistration& backend : VideoInputBackendRegistry()) + { + if (BackendNameMatches(backendName, backend.aliases)) + return &backend; + } + return nullptr; +} } bool VideoBackendsRequireCom(const AppConfig& config) { - return IsDeckLinkBackend(config.videoInputBackend) || IsDeckLinkBackend(config.videoOutputBackend); + const std::string inputBackend = NormalizeBackendName(config.videoInputBackend); + const std::string outputBackend = NormalizeBackendName(config.videoOutputBackend); + const VideoInputBackendRegistration* input = FindVideoInputBackend(inputBackend); + const VideoOutputBackendRegistration* output = FindVideoOutputBackend(outputBackend); + return (input != nullptr && input->requiresCom) || (output != nullptr && output->requiresCom); } std::unique_ptr CreateVideoOutputBackend(const AppConfig& config) { - if (IsDeckLinkBackend(config.videoOutputBackend)) - return std::make_unique(); - - if (IsNoneBackend(config.videoOutputBackend)) - return std::make_unique("Video output backend is disabled by config."); + const std::string backendName = NormalizeBackendName(config.videoOutputBackend); + const VideoOutputBackendRegistration* backend = FindVideoOutputBackend(backendName); + if (backend != nullptr && backend->create != nullptr) + return backend->create(); return std::make_unique("Unsupported videoOutputBackend: " + config.videoOutputBackend); } @@ -199,31 +298,15 @@ std::unique_ptr StartVideoInputBackend( const VideoFormat& inputVideoMode, bool inputVideoModeResolved) { - if (IsNoneBackend(config.videoInputBackend)) + const std::string backendName = NormalizeBackendName(config.videoInputBackend); + const VideoInputBackendRegistration* backend = FindVideoInputBackend(backendName); + if (backend != nullptr && backend->start != nullptr) { - Log("app", "Video input backend disabled by config."); - return std::make_unique("none"); + VideoInputBackendStartContext context{ config, mailbox, mailboxConfig, inputVideoMode, inputVideoModeResolved }; + return backend->start(context); } - if (!IsDeckLinkBackend(config.videoInputBackend)) - { - LogWarning("app", "Unsupported videoInputBackend '" + config.videoInputBackend + "'; runtime shaders will use fallback input."); - return std::make_unique(config.videoInputBackend); - } - - if (!inputVideoModeResolved) - { - LogWarning("app", "DeckLink input mode was not resolved; runtime shaders will use fallback input until a real input edge is available."); - return std::make_unique("decklink"); - } - - auto session = std::make_unique(mailbox); - std::string error; - if (!session->Start(mailbox, mailboxConfig, inputVideoMode, error)) - { - LogWarning("app", "DeckLink input edge unavailable; runtime shaders will use fallback input until a real input edge is available. " + error); - return std::make_unique("decklink"); - } - return session; + LogWarning("app", "Unsupported videoInputBackend '" + config.videoInputBackend + "'; runtime shaders will use fallback input."); + return std::make_unique(config.videoInputBackend); } }