Video format refactor
Some checks failed
CI / Native Windows Build And Tests (push) Failing after 4s
CI / React UI Build (push) Successful in 11s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
2026-05-06 11:51:08 +10:00
parent 02a8a64360
commit 515f58b848
5 changed files with 120 additions and 52 deletions

View File

@@ -14,6 +14,17 @@ std::string NormalizeModeToken(const std::string& value)
} }
bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName) bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName)
{
VideoFormat videoMode;
if (!ResolveConfiguredVideoFormat(videoFormat, frameRate, videoMode))
return false;
displayMode = videoMode.displayMode;
displayModeName = videoMode.displayName;
return true;
}
bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode)
{ {
const std::string formatToken = NormalizeModeToken(videoFormat); const std::string formatToken = NormalizeModeToken(videoFormat);
const std::string frameToken = NormalizeModeToken(frameRate); const std::string frameToken = NormalizeModeToken(frameRate);
@@ -78,8 +89,8 @@ bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::str
{ {
if (combinedToken == option.token || (frameToken.empty() && formatToken == option.token)) if (combinedToken == option.token || (frameToken.empty() && formatToken == option.token))
{ {
displayMode = option.mode; videoMode.displayMode = option.mode;
displayModeName = option.displayName; videoMode.displayName = option.displayName;
return true; return true;
} }
} }
@@ -87,6 +98,31 @@ bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::str
return false; return false;
} }
bool ResolveConfiguredVideoFormats(
const std::string& inputVideoFormat,
const std::string& inputFrameRate,
const std::string& outputVideoFormat,
const std::string& outputFrameRate,
VideoFormatSelection& videoModes,
std::string& error)
{
if (!ResolveConfiguredVideoFormat(inputVideoFormat, inputFrameRate, videoModes.input))
{
error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " +
inputVideoFormat + " / " + inputFrameRate;
return false;
}
if (!ResolveConfiguredVideoFormat(outputVideoFormat, outputFrameRate, videoModes.output))
{
error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
outputVideoFormat + " / " + outputFrameRate;
return false;
}
return true;
}
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode) bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode)
{ {
if (!iterator || !foundMode) if (!iterator || !foundMode)

View File

@@ -4,6 +4,44 @@
#include <string> #include <string>
struct FrameSize
{
unsigned width = 0;
unsigned height = 0;
bool IsEmpty() const { return width == 0 || height == 0; }
};
inline bool operator==(const FrameSize& left, const FrameSize& right)
{
return left.width == right.width && left.height == right.height;
}
inline bool operator!=(const FrameSize& left, const FrameSize& right)
{
return !(left == right);
}
struct VideoFormat
{
BMDDisplayMode displayMode = bmdModeHD1080p5994;
std::string displayName = "1080p59.94";
};
struct VideoFormatSelection
{
VideoFormat input;
VideoFormat output;
};
std::string NormalizeModeToken(const std::string& value); std::string NormalizeModeToken(const std::string& value);
bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName); bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName);
bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode);
bool ResolveConfiguredVideoFormats(
const std::string& inputVideoFormat,
const std::string& inputFrameRate,
const std::string& outputVideoFormat,
const std::string& outputFrameRate,
VideoFormatSelection& videoModes,
std::string& error);
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode); bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode);

View File

@@ -1,6 +1,5 @@
#include "DeckLinkSession.h" #include "DeckLinkSession.h"
#include "DeckLinkDisplayMode.h"
#include "GlRenderConstants.h" #include "GlRenderConstants.h"
#include <atlbase.h> #include <atlbase.h>
@@ -57,14 +56,14 @@ void DeckLinkSession::ReleaseResources()
playoutAllocator.Release(); playoutAllocator.Release();
} }
bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, BMDDisplayMode outputDisplayMode, const std::string& requestedInputDisplayModeName, const std::string& requestedOutputDisplayModeName, std::string& error) bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
{ {
CComPtr<IDeckLinkIterator> deckLinkIterator; CComPtr<IDeckLinkIterator> deckLinkIterator;
CComPtr<IDeckLinkDisplayMode> inputMode; CComPtr<IDeckLinkDisplayMode> inputMode;
CComPtr<IDeckLinkDisplayMode> outputMode; CComPtr<IDeckLinkDisplayMode> outputMode;
inputDisplayModeName = requestedInputDisplayModeName; inputDisplayModeName = videoModes.input.displayName;
outputDisplayModeName = requestedOutputDisplayModeName; outputDisplayModeName = videoModes.output.displayName;
HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast<void**>(&deckLinkIterator)); HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast<void**>(&deckLinkIterator));
if (FAILED(result)) if (FAILED(result))
@@ -143,9 +142,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B
return false; return false;
} }
if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, inputDisplayMode, &inputMode)) if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, videoModes.input.displayMode, &inputMode))
{ {
error = "Cannot get specified input BMDDisplayMode for configured mode: " + requestedInputDisplayModeName; error = "Cannot get specified input BMDDisplayMode for configured mode: " + videoModes.input.displayName;
ReleaseResources(); ReleaseResources();
return false; return false;
} }
@@ -159,17 +158,17 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B
return false; return false;
} }
if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, outputDisplayMode, &outputMode)) if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, videoModes.output.displayMode, &outputMode))
{ {
error = "Cannot get specified output BMDDisplayMode for configured mode: " + requestedOutputDisplayModeName; error = "Cannot get specified output BMDDisplayMode for configured mode: " + videoModes.output.displayName;
ReleaseResources(); ReleaseResources();
return false; return false;
} }
outputFrameWidth = outputMode->GetWidth(); outputFrameSize = { static_cast<unsigned>(outputMode->GetWidth()), static_cast<unsigned>(outputMode->GetHeight()) };
outputFrameHeight = outputMode->GetHeight(); inputFrameSize = inputMode
inputFrameWidth = inputMode ? inputMode->GetWidth() : outputFrameWidth; ? FrameSize{ static_cast<unsigned>(inputMode->GetWidth()), static_cast<unsigned>(inputMode->GetHeight()) }
inputFrameHeight = inputMode ? inputMode->GetHeight() : outputFrameHeight; : outputFrameSize;
if (!input) if (!input)
inputDisplayModeName = "No input - black frame"; inputDisplayModeName = "No input - black frame";
outputMode->GetFrameRate(&frameDuration, &frameTimescale); outputMode->GetFrameRate(&frameDuration, &frameTimescale);
@@ -177,7 +176,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B
return true; return true;
} }
bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode inputDisplayMode, std::string& error) bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error)
{ {
if (!input) if (!input)
{ {
@@ -188,7 +187,7 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr
CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hdc, hglrc)); CComPtr<IDeckLinkVideoBufferAllocatorProvider> captureAllocator(new (std::nothrow) InputAllocatorPool(hdc, hglrc));
if (input->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK) if (input->EnableVideoInputWithAllocatorProvider(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK)
{ {
OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n"); OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n");
input.Release(); input.Release();
@@ -212,23 +211,23 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr
return true; return true;
} }
bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode outputDisplayMode, bool externalKeyingEnabled, std::string& error) bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
{ {
int outputFrameRowBytes = 0; int outputFrameRowBytes = 0;
if (output->RowBytesForPixelFormat(bmdFormat8BitBGRA, outputFrameWidth, &outputFrameRowBytes) != S_OK) if (output->RowBytesForPixelFormat(bmdFormat8BitBGRA, outputFrameSize.width, &outputFrameRowBytes) != S_OK)
{ {
error = "DeckLink output setup failed while calculating BGRA row bytes."; error = "DeckLink output setup failed while calculating BGRA row bytes.";
return false; return false;
} }
playoutAllocator.Attach(new (std::nothrow) PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameHeight)); playoutAllocator.Attach(new (std::nothrow) PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameSize.height));
if (playoutAllocator == nullptr) if (playoutAllocator == nullptr)
{ {
error = "DeckLink output setup failed while creating the playout allocator."; error = "DeckLink output setup failed while creating the playout allocator.";
return false; return false;
} }
if (output->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK) if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK)
{ {
error = "DeckLink output setup failed while enabling video output."; error = "DeckLink output setup failed while enabling video output.";
return false; return false;
@@ -273,7 +272,7 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl
return false; return false;
} }
if (output->CreateVideoFrameWithBuffer(outputFrameWidth, outputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK) if (output->CreateVideoFrameWithBuffer(outputFrameSize.width, outputFrameSize.height, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK)
{ {
error = "DeckLink output setup failed while creating an output video frame."; error = "DeckLink output setup failed while creating an output video frame.";
return false; return false;
@@ -374,7 +373,7 @@ bool DeckLinkSession::Start()
void* pFrame; void* pFrame;
outputVideoFrameBuffer->GetBytes((void**)&pFrame); outputVideoFrameBuffer->GetBytes((void**)&pFrame);
memset(pFrame, 0, outputVideoFrame->GetRowBytes() * outputFrameHeight); memset(pFrame, 0, outputVideoFrame->GetRowBytes() * outputFrameSize.height);
outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite); outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "DeckLinkAPI_h.h" #include "DeckLinkAPI_h.h"
#include "DeckLinkDisplayMode.h"
#include "DeckLinkFrameTransfer.h" #include "DeckLinkFrameTransfer.h"
#include <atlbase.h> #include <atlbase.h>
@@ -16,20 +17,22 @@ public:
~DeckLinkSession(); ~DeckLinkSession();
void ReleaseResources(); void ReleaseResources();
bool DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, BMDDisplayMode outputDisplayMode, const std::string& requestedInputDisplayModeName, const std::string& requestedOutputDisplayModeName, std::string& error); bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error);
bool ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode inputDisplayMode, std::string& error); bool ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& inputVideoMode, std::string& error);
bool ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode outputDisplayMode, bool externalKeyingEnabled, std::string& error); bool ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error);
bool Start(); bool Start();
bool Stop(); bool Stop();
bool HasInputDevice() const { return input != nullptr; } bool HasInputDevice() const { return input != nullptr; }
bool HasInputSource() const { return !hasNoInputSource; } bool HasInputSource() const { return !hasNoInputSource; }
void SetInputSourceMissing(bool missing) { hasNoInputSource = missing; } void SetInputSourceMissing(bool missing) { hasNoInputSource = missing; }
bool InputOutputDimensionsDiffer() const { return inputFrameWidth != outputFrameWidth || inputFrameHeight != outputFrameHeight; } bool InputOutputDimensionsDiffer() const { return inputFrameSize != outputFrameSize; }
unsigned InputFrameWidth() const { return inputFrameWidth; } const FrameSize& InputFrameSize() const { return inputFrameSize; }
unsigned InputFrameHeight() const { return inputFrameHeight; } const FrameSize& OutputFrameSize() const { return outputFrameSize; }
unsigned OutputFrameWidth() const { return outputFrameWidth; } unsigned InputFrameWidth() const { return inputFrameSize.width; }
unsigned OutputFrameHeight() const { return outputFrameHeight; } unsigned InputFrameHeight() const { return inputFrameSize.height; }
unsigned OutputFrameWidth() const { return outputFrameSize.width; }
unsigned OutputFrameHeight() const { return outputFrameSize.height; }
const std::string& InputDisplayModeName() const { return inputDisplayModeName; } const std::string& InputDisplayModeName() const { return inputDisplayModeName; }
const std::string& OutputModelName() const { return outputModelName; } const std::string& OutputModelName() const { return outputModelName; }
bool SupportsInternalKeying() const { return supportsInternalKeying; } bool SupportsInternalKeying() const { return supportsInternalKeying; }
@@ -56,10 +59,8 @@ private:
BMDTimeValue frameDuration = 0; BMDTimeValue frameDuration = 0;
BMDTimeScale frameTimescale = 0; BMDTimeScale frameTimescale = 0;
unsigned totalPlayoutFrames = 0; unsigned totalPlayoutFrames = 0;
unsigned inputFrameWidth = 0; FrameSize inputFrameSize;
unsigned inputFrameHeight = 0; FrameSize outputFrameSize;
unsigned outputFrameWidth = 0;
unsigned outputFrameHeight = 0;
std::string inputDisplayModeName = "1080p59.94"; std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94"; std::string outputDisplayModeName = "1080p59.94";
bool hasNoInputSource = true; bool hasNoInputSource = true;

View File

@@ -97,10 +97,7 @@ OpenGLComposite::~OpenGLComposite()
bool OpenGLComposite::InitDeckLink() bool OpenGLComposite::InitDeckLink()
{ {
BMDDisplayMode inputDisplayMode = bmdModeHD1080p5994; VideoFormatSelection videoModes;
BMDDisplayMode outputDisplayMode = bmdModeHD1080p5994;
std::string inputDisplayModeName = "1080p59.94";
std::string outputDisplayModeName = "1080p59.94";
std::string initFailureReason; std::string initFailureReason;
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty()) if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
@@ -115,23 +112,20 @@ bool OpenGLComposite::InitDeckLink()
if (mRuntimeHost) if (mRuntimeHost)
{ {
if (!ResolveConfiguredDisplayMode(mRuntimeHost->GetInputVideoFormat(), mRuntimeHost->GetInputFrameRate(), inputDisplayMode, inputDisplayModeName)) if (!ResolveConfiguredVideoFormats(
mRuntimeHost->GetInputVideoFormat(),
mRuntimeHost->GetInputFrameRate(),
mRuntimeHost->GetOutputVideoFormat(),
mRuntimeHost->GetOutputFrameRate(),
videoModes,
initFailureReason))
{ {
const std::string error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " + MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink mode configuration error", MB_OK);
mRuntimeHost->GetInputVideoFormat() + " / " + mRuntimeHost->GetInputFrameRate();
MessageBoxA(NULL, error.c_str(), "DeckLink input mode configuration error", MB_OK);
return false;
}
if (!ResolveConfiguredDisplayMode(mRuntimeHost->GetOutputVideoFormat(), mRuntimeHost->GetOutputFrameRate(), outputDisplayMode, outputDisplayModeName))
{
const std::string error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
mRuntimeHost->GetOutputVideoFormat() + " / " + mRuntimeHost->GetOutputFrameRate();
MessageBoxA(NULL, error.c_str(), "DeckLink output mode configuration error", MB_OK);
return false; return false;
} }
} }
if (!mDeckLink->DiscoverDevicesAndModes(inputDisplayMode, outputDisplayMode, inputDisplayModeName, outputDisplayModeName, initFailureReason)) if (!mDeckLink->DiscoverDevicesAndModes(videoModes, initFailureReason))
{ {
const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application." const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application."
? "This application requires the DeckLink drivers installed." ? "This application requires the DeckLink drivers installed."
@@ -177,7 +171,7 @@ bool OpenGLComposite::InitDeckLink()
} }
} }
if (!mDeckLink->ConfigureInput(this, hGLDC, hGLRC, inputDisplayMode, initFailureReason)) if (!mDeckLink->ConfigureInput(this, hGLDC, hGLRC, videoModes.input, initFailureReason))
{ {
goto error; goto error;
} }
@@ -186,7 +180,7 @@ bool OpenGLComposite::InitDeckLink()
mRuntimeHost->SetSignalStatus(false, mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), mDeckLink->InputDisplayModeName()); mRuntimeHost->SetSignalStatus(false, mDeckLink->InputFrameWidth(), mDeckLink->InputFrameHeight(), mDeckLink->InputDisplayModeName());
} }
if (!mDeckLink->ConfigureOutput(this, hGLDC, hGLRC, outputDisplayMode, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason)) if (!mDeckLink->ConfigureOutput(this, hGLDC, hGLRC, videoModes.output, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason))
{ {
goto error; goto error;
} }