177 lines
5.7 KiB
C++
177 lines
5.7 KiB
C++
#include "app/AppConfig.h"
|
|
#include "app/AppConfigProvider.h"
|
|
#include "app/RenderCadenceApp.h"
|
|
#include "app/VideoBackendFactory.h"
|
|
#include "frames/InputFrameMailbox.h"
|
|
#include "frames/SystemFrameExchange.h"
|
|
#include "logging/Logger.h"
|
|
#include "render/thread/RenderThread.h"
|
|
#include "video/core/VideoIOFormat.h"
|
|
#include "video/core/VideoMode.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
namespace
|
|
{
|
|
constexpr std::size_t kDeckLinkTargetBufferedFrames = 4;
|
|
constexpr std::size_t kReadbackDepth = 6;
|
|
constexpr std::size_t kWritableOutputReserveFrames = kReadbackDepth + 2;
|
|
|
|
class ComInitGuard
|
|
{
|
|
public:
|
|
~ComInitGuard()
|
|
{
|
|
if (mInitialized)
|
|
CoUninitialize();
|
|
}
|
|
|
|
bool Initialize()
|
|
{
|
|
const HRESULT result = CoInitialize(nullptr);
|
|
mInitialized = SUCCEEDED(result);
|
|
mResult = result;
|
|
return mInitialized;
|
|
}
|
|
|
|
HRESULT Result() const { return mResult; }
|
|
|
|
private:
|
|
bool mInitialized = false;
|
|
HRESULT mResult = S_OK;
|
|
};
|
|
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
RenderCadenceCompositor::AppConfigProvider configProvider;
|
|
std::string configError;
|
|
if (!configProvider.LoadDefault(configError))
|
|
{
|
|
RenderCadenceCompositor::Logger::Instance().Start(RenderCadenceCompositor::DefaultAppConfig().logging);
|
|
RenderCadenceCompositor::LogError("app", "Config load failed: " + configError);
|
|
RenderCadenceCompositor::Logger::Instance().Stop();
|
|
return 1;
|
|
}
|
|
configProvider.ApplyCommandLine(argc, argv);
|
|
|
|
RenderCadenceCompositor::AppConfig appConfig = configProvider.Config();
|
|
RenderCadenceCompositor::Logger::Instance().Start(appConfig.logging);
|
|
RenderCadenceCompositor::Log(
|
|
"app",
|
|
"RenderCadenceCompositor starting. Starts render cadence, configured video I/O backends, and telemetry. Press Enter to stop.");
|
|
RenderCadenceCompositor::Log("app", "Loaded config from " + configProvider.SourcePath().string());
|
|
|
|
ComInitGuard com;
|
|
if (RenderCadenceCompositor::VideoBackendsRequireCom(appConfig) && !com.Initialize())
|
|
{
|
|
std::ostringstream message;
|
|
message << "COM initialization failed: 0x" << std::hex << com.Result();
|
|
RenderCadenceCompositor::LogError("app", message.str());
|
|
RenderCadenceCompositor::Logger::Instance().Stop();
|
|
return 1;
|
|
}
|
|
|
|
SystemFrameExchangeConfig frameExchangeConfig;
|
|
VideoFormatDimensions(
|
|
appConfig.output.resolution,
|
|
frameExchangeConfig.width,
|
|
frameExchangeConfig.height);
|
|
frameExchangeConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
|
|
frameExchangeConfig.rowBytes = VideoIORowBytes(frameExchangeConfig.pixelFormat, frameExchangeConfig.width);
|
|
frameExchangeConfig.capacity =
|
|
appConfig.warmupCompletedFrames +
|
|
kDeckLinkTargetBufferedFrames +
|
|
kWritableOutputReserveFrames;
|
|
frameExchangeConfig.maxCompletedFrames = appConfig.warmupCompletedFrames;
|
|
|
|
SystemFrameExchange frameExchange(frameExchangeConfig);
|
|
|
|
InputFrameMailboxConfig inputMailboxConfig;
|
|
VideoFormatDimensions(
|
|
appConfig.input.resolution,
|
|
inputMailboxConfig.width,
|
|
inputMailboxConfig.height);
|
|
inputMailboxConfig.pixelFormat = VideoIOPixelFormat::Bgra8;
|
|
inputMailboxConfig.rowBytes = VideoIORowBytes(inputMailboxConfig.pixelFormat, inputMailboxConfig.width);
|
|
inputMailboxConfig.capacity = 4;
|
|
inputMailboxConfig.maxReadyFrames = 3;
|
|
InputFrameMailbox inputMailbox(inputMailboxConfig);
|
|
|
|
VideoFormat inputVideoMode;
|
|
VideoFormat outputVideoMode;
|
|
std::string inputVideoModeError;
|
|
const bool inputVideoModeResolved = ResolveConfiguredVideoFormat(appConfig.input.resolution, appConfig.input.frameRate, inputVideoMode);
|
|
const bool outputVideoModeResolved = ResolveConfiguredVideoFormat(appConfig.output.resolution, appConfig.output.frameRate, outputVideoMode);
|
|
if (!inputVideoModeResolved)
|
|
{
|
|
inputVideoModeError = "Unsupported input resolution/frameRate in config/runtime-host.json: " +
|
|
appConfig.input.resolution + " / " + appConfig.input.frameRate;
|
|
RenderCadenceCompositor::LogWarning("app", inputVideoModeError);
|
|
}
|
|
if (!outputVideoModeResolved)
|
|
{
|
|
RenderCadenceCompositor::LogWarning(
|
|
"app",
|
|
"Unsupported output resolution/frameRate in config/runtime-host.json; render cadence will use parsed frame-rate fallback: " +
|
|
appConfig.output.resolution + " / " + appConfig.output.frameRate);
|
|
}
|
|
else
|
|
{
|
|
appConfig.output.videoMode = outputVideoMode;
|
|
}
|
|
|
|
auto inputBackend = RenderCadenceCompositor::StartVideoInputBackend(
|
|
appConfig,
|
|
inputMailbox,
|
|
inputMailboxConfig,
|
|
inputVideoMode,
|
|
inputVideoModeResolved);
|
|
|
|
RenderThread::Config renderConfig;
|
|
renderConfig.width = frameExchangeConfig.width;
|
|
renderConfig.height = frameExchangeConfig.height;
|
|
const double fallbackFrameDurationMilliseconds = FrameDurationMillisecondsFromRateString(appConfig.output.frameRate);
|
|
renderConfig.frameDurationMilliseconds = outputVideoModeResolved
|
|
? outputVideoMode.frameDurationMilliseconds
|
|
: fallbackFrameDurationMilliseconds;
|
|
renderConfig.pboDepth = kReadbackDepth;
|
|
|
|
RenderThread renderThread(frameExchange, &inputMailbox, renderConfig);
|
|
|
|
auto outputBackend = RenderCadenceCompositor::CreateVideoOutputBackend(appConfig);
|
|
RenderCadenceCompositor::RenderCadenceApp<RenderThread, SystemFrameExchange> app(
|
|
renderThread,
|
|
frameExchange,
|
|
appConfig,
|
|
std::move(outputBackend));
|
|
app.SetVideoInputMetricsProvider([inputBackend = inputBackend.get()]() {
|
|
return inputBackend ? inputBackend->Metrics() : RenderCadenceCompositor::VideoInputEdgeMetrics();
|
|
});
|
|
|
|
std::string error;
|
|
if (!app.Start(error))
|
|
{
|
|
RenderCadenceCompositor::LogError("app", "RenderCadenceCompositor start failed: " + error);
|
|
if (inputBackend)
|
|
inputBackend->Stop();
|
|
RenderCadenceCompositor::Logger::Instance().Stop();
|
|
return 1;
|
|
}
|
|
|
|
std::string line;
|
|
std::getline(std::cin, line);
|
|
app.Stop();
|
|
if (inputBackend)
|
|
inputBackend->Stop();
|
|
RenderCadenceCompositor::Log("app", "RenderCadenceCompositor stopped.");
|
|
RenderCadenceCompositor::Logger::Instance().Stop();
|
|
return 0;
|
|
}
|