Video backend
This commit is contained in:
@@ -49,6 +49,10 @@ set(APP_SOURCES
|
|||||||
"${APP_DIR}/videoio/decklink/DeckLinkSession.h"
|
"${APP_DIR}/videoio/decklink/DeckLinkSession.h"
|
||||||
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp"
|
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp"
|
||||||
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h"
|
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h"
|
||||||
|
"${APP_DIR}/videoio/VideoIOBackendFactory.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoIOBackendFactory.h"
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.h"
|
||||||
"${APP_DIR}/gl/renderer/GLExtensions.cpp"
|
"${APP_DIR}/gl/renderer/GLExtensions.cpp"
|
||||||
"${APP_DIR}/gl/renderer/GLExtensions.h"
|
"${APP_DIR}/gl/renderer/GLExtensions.h"
|
||||||
"${APP_DIR}/gl/shader/GlobalParamsBuffer.cpp"
|
"${APP_DIR}/gl/shader/GlobalParamsBuffer.cpp"
|
||||||
@@ -204,6 +208,35 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests)
|
add_test(NAME RuntimeParameterUtilsTests COMMAND RuntimeParameterUtilsTests)
|
||||||
|
|
||||||
|
add_executable(RuntimeHostVideoIOStateTests
|
||||||
|
"${APP_DIR}/runtime/RuntimeHost.cpp"
|
||||||
|
"${APP_DIR}/runtime/RuntimeClock.cpp"
|
||||||
|
"${APP_DIR}/runtime/RuntimeJson.cpp"
|
||||||
|
"${APP_DIR}/runtime/RuntimeParameterUtils.cpp"
|
||||||
|
"${APP_DIR}/shader/ShaderCompiler.cpp"
|
||||||
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RuntimeHostVideoIOStateTests.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(RuntimeHostVideoIOStateTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/platform"
|
||||||
|
"${APP_DIR}/runtime"
|
||||||
|
"${APP_DIR}/shader"
|
||||||
|
"${APP_DIR}/videoio"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(RuntimeHostVideoIOStateTests PRIVATE
|
||||||
|
Advapi32
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(RuntimeHostVideoIOStateTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME RuntimeHostVideoIOStateTests COMMAND RuntimeHostVideoIOStateTests)
|
||||||
|
|
||||||
add_executable(Std140BufferTests
|
add_executable(Std140BufferTests
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp"
|
||||||
)
|
)
|
||||||
@@ -318,6 +351,7 @@ endif()
|
|||||||
add_test(NAME VideoPlayoutSchedulerTests COMMAND VideoPlayoutSchedulerTests)
|
add_test(NAME VideoPlayoutSchedulerTests COMMAND VideoPlayoutSchedulerTests)
|
||||||
|
|
||||||
add_executable(VideoIODeviceFakeTests
|
add_executable(VideoIODeviceFakeTests
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.cpp"
|
||||||
"${APP_DIR}/videoio/VideoIOFormat.cpp"
|
"${APP_DIR}/videoio/VideoIOFormat.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIODeviceFakeTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIODeviceFakeTests.cpp"
|
||||||
)
|
)
|
||||||
@@ -334,6 +368,43 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME VideoIODeviceFakeTests COMMAND VideoIODeviceFakeTests)
|
add_test(NAME VideoIODeviceFakeTests COMMAND VideoIODeviceFakeTests)
|
||||||
|
|
||||||
|
add_executable(VideoIOBackendFactoryTests
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkAPI_i.c"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkSession.cpp"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkSession.h"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkDisplayMode.cpp"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkDisplayMode.h"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.h"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkFrameTransfer.cpp"
|
||||||
|
"${APP_DIR}/videoio/decklink/DeckLinkFrameTransfer.h"
|
||||||
|
"${APP_DIR}/videoio/VideoIOBackendFactory.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoIOBackendFactory.h"
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoIOConfig.h"
|
||||||
|
"${APP_DIR}/videoio/VideoIOFormat.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoPlayoutScheduler.cpp"
|
||||||
|
"${APP_DIR}/videoio/VideoPlayoutScheduler.h"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIOBackendFactoryTests.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(VideoIOBackendFactoryTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/gl/renderer"
|
||||||
|
"${APP_DIR}/videoio"
|
||||||
|
"${APP_DIR}/videoio/decklink"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(VideoIOBackendFactoryTests PRIVATE
|
||||||
|
Ole32
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(VideoIOBackendFactoryTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME VideoIOBackendFactoryTests COMMAND VideoIOBackendFactoryTests)
|
||||||
|
|
||||||
install(TARGETS LoopThroughWithOpenGLCompositing
|
install(TARGETS LoopThroughWithOpenGLCompositing
|
||||||
RUNTIME DESTINATION "."
|
RUNTIME DESTINATION "."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -412,10 +412,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup OpenGL and DeckLink capture and playout object
|
// Setup OpenGL and video I/O capture/playout object
|
||||||
pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC);
|
pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC);
|
||||||
|
|
||||||
if (pOpenGLComposite->InitDeckLink())
|
if (pOpenGLComposite->InitializeVideoIO())
|
||||||
{
|
{
|
||||||
wglMakeCurrent( NULL, NULL );
|
wglMakeCurrent( NULL, NULL );
|
||||||
if (pOpenGLComposite->Start())
|
if (pOpenGLComposite->Start())
|
||||||
@@ -423,11 +423,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
PostMessage(hWnd, kCreateStatusStripMessage, 0, 0);
|
PostMessage(hWnd, kCreateStatusStripMessage, 0, 0);
|
||||||
break; // success
|
break; // success
|
||||||
}
|
}
|
||||||
MessageBoxA(NULL, "The OpenGL/DeckLink runtime initialized, but playout failed to start. See the previous DeckLink start message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
|
MessageBoxA(NULL, "The OpenGL/video I/O runtime initialized, but playout failed to start. See the previous start message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MessageBoxA(NULL, "The OpenGL/DeckLink runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
|
MessageBoxA(NULL, "The OpenGL/video I/O runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failed to initialize - cleanup
|
// Failed to initialize - cleanup
|
||||||
@@ -438,7 +438,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/DeckLink runtime.");
|
ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/video I/O runtime.");
|
||||||
PostMessage(hWnd, WM_CLOSE, 0, 0);
|
PostMessage(hWnd, WM_CLOSE, 0, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -474,7 +474,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/DeckLink runtime.");
|
ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/video I/O runtime.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deselect the current rendering context and delete it
|
// Deselect the current rendering context and delete it
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#include "DeckLinkDisplayMode.h"
|
|
||||||
#include "DeckLinkSession.h"
|
|
||||||
#include "OpenGLComposite.h"
|
#include "OpenGLComposite.h"
|
||||||
#include "GLExtensions.h"
|
#include "GLExtensions.h"
|
||||||
#include "GlRenderConstants.h"
|
#include "GlRenderConstants.h"
|
||||||
@@ -10,6 +8,7 @@
|
|||||||
#include "PngScreenshotWriter.h"
|
#include "PngScreenshotWriter.h"
|
||||||
#include "RuntimeServices.h"
|
#include "RuntimeServices.h"
|
||||||
#include "ShaderBuildQueue.h"
|
#include "ShaderBuildQueue.h"
|
||||||
|
#include "VideoIOBackendFactory.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -23,7 +22,6 @@
|
|||||||
|
|
||||||
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
||||||
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
|
hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC),
|
||||||
mVideoIO(std::make_unique<DeckLinkSession>()),
|
|
||||||
mRenderer(std::make_unique<OpenGLRenderer>()),
|
mRenderer(std::make_unique<OpenGLRenderer>()),
|
||||||
mUseCommittedLayerStates(false),
|
mUseCommittedLayerStates(false),
|
||||||
mScreenshotRequested(false)
|
mScreenshotRequested(false)
|
||||||
@@ -37,7 +35,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
|||||||
[this]() { ProcessScreenshotRequest(); },
|
[this]() { ProcessScreenshotRequest(); },
|
||||||
[this]() { paintGL(); });
|
[this]() { paintGL(); });
|
||||||
mVideoIOBridge = std::make_unique<OpenGLVideoIOBridge>(
|
mVideoIOBridge = std::make_unique<OpenGLVideoIOBridge>(
|
||||||
*mVideoIO,
|
nullptr,
|
||||||
*mRenderer,
|
*mRenderer,
|
||||||
*mRenderPipeline,
|
*mRenderPipeline,
|
||||||
*mRuntimeHost,
|
*mRuntimeHost,
|
||||||
@@ -56,20 +54,15 @@ OpenGLComposite::~OpenGLComposite()
|
|||||||
mRuntimeServices->Stop();
|
mRuntimeServices->Stop();
|
||||||
if (mShaderBuildQueue)
|
if (mShaderBuildQueue)
|
||||||
mShaderBuildQueue->Stop();
|
mShaderBuildQueue->Stop();
|
||||||
|
if (mVideoIO)
|
||||||
mVideoIO->ReleaseResources();
|
mVideoIO->ReleaseResources();
|
||||||
mRenderer->DestroyResources();
|
mRenderer->DestroyResources();
|
||||||
|
|
||||||
DeleteCriticalSection(&pMutex);
|
DeleteCriticalSection(&pMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLComposite::InitDeckLink()
|
bool OpenGLComposite::InitializeVideoIO()
|
||||||
{
|
{
|
||||||
return InitVideoIO();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenGLComposite::InitVideoIO()
|
|
||||||
{
|
|
||||||
VideoFormatSelection videoModes;
|
|
||||||
std::string initFailureReason;
|
std::string initFailureReason;
|
||||||
|
|
||||||
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
|
if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty())
|
||||||
@@ -82,31 +75,31 @@ bool OpenGLComposite::InitVideoIO()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mRuntimeHost)
|
if (!mRuntimeHost)
|
||||||
{
|
{
|
||||||
if (!ResolveConfiguredVideoFormats(
|
initFailureReason = "Runtime host is not available.";
|
||||||
mRuntimeHost->GetInputVideoFormat(),
|
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR);
|
||||||
mRuntimeHost->GetInputFrameRate(),
|
|
||||||
mRuntimeHost->GetOutputVideoFormat(),
|
|
||||||
mRuntimeHost->GetOutputFrameRate(),
|
|
||||||
videoModes,
|
|
||||||
initFailureReason))
|
|
||||||
{
|
|
||||||
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink mode configuration error", MB_OK);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!mVideoIO->DiscoverDevicesAndModes(videoModes, initFailureReason))
|
const VideoIOConfiguration videoIOConfig = mRuntimeHost->GetVideoIOConfiguration();
|
||||||
|
mVideoIO = CreateVideoIODevice(videoIOConfig.backendId, initFailureReason);
|
||||||
|
if (!mVideoIO)
|
||||||
|
{
|
||||||
|
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mVideoIOBridge->SetVideoIODevice(mVideoIO.get());
|
||||||
|
|
||||||
|
if (!mVideoIO->DiscoverDevicesAndModes(videoIOConfig, 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 selected video I/O drivers installed."
|
||||||
: "DeckLink initialization failed";
|
: "Video I/O initialization failed";
|
||||||
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
|
MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bool outputAlphaRequired = mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled();
|
if (!mVideoIO->SelectPreferredFormats(videoIOConfig, initFailureReason))
|
||||||
if (!mVideoIO->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason))
|
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (! CheckOpenGLExtensions())
|
if (! CheckOpenGLExtensions())
|
||||||
@@ -121,9 +114,9 @@ bool OpenGLComposite::InitVideoIO()
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
PublishVideoIOStatus(mVideoIO->OutputModelName().empty()
|
PublishVideoIOStatus(mVideoIO->DeviceName().empty()
|
||||||
? "DeckLink output device selected."
|
? "Video I/O output device selected."
|
||||||
: ("Selected output device: " + mVideoIO->OutputModelName()));
|
: ("Selected output device: " + mVideoIO->DeviceName()));
|
||||||
|
|
||||||
// Resize window to match output video frame, but scale large formats down by half for viewing.
|
// Resize window to match output video frame, but scale large formats down by half for viewing.
|
||||||
if (mVideoIO->OutputFrameWidth() < 1920)
|
if (mVideoIO->OutputFrameWidth() < 1920)
|
||||||
@@ -131,7 +124,7 @@ bool OpenGLComposite::InitVideoIO()
|
|||||||
else
|
else
|
||||||
resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2);
|
resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2);
|
||||||
|
|
||||||
if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, videoModes.input, initFailureReason))
|
if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, initFailureReason))
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -140,7 +133,7 @@ bool OpenGLComposite::InitVideoIO()
|
|||||||
mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName());
|
mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, videoModes.output, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason))
|
if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, initFailureReason))
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -151,13 +144,16 @@ bool OpenGLComposite::InitVideoIO()
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
if (!initFailureReason.empty())
|
if (!initFailureReason.empty())
|
||||||
MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR);
|
MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR);
|
||||||
mVideoIO->ReleaseResources();
|
mVideoIO->ReleaseResources();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLComposite::paintGL()
|
void OpenGLComposite::paintGL()
|
||||||
{
|
{
|
||||||
|
if (!mVideoIO)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!TryEnterCriticalSection(&pMutex))
|
if (!TryEnterCriticalSection(&pMutex))
|
||||||
{
|
{
|
||||||
ValidateRect(hGLWnd, NULL);
|
ValidateRect(hGLWnd, NULL);
|
||||||
@@ -187,21 +183,13 @@ void OpenGLComposite::resizeWindow(int width, int height)
|
|||||||
|
|
||||||
void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
|
void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage)
|
||||||
{
|
{
|
||||||
if (!mRuntimeHost)
|
if (!mRuntimeHost || !mVideoIO)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!statusMessage.empty())
|
if (!statusMessage.empty())
|
||||||
mVideoIO->SetStatusMessage(statusMessage);
|
mVideoIO->SetStatusMessage(statusMessage);
|
||||||
|
|
||||||
mRuntimeHost->SetVideoIOStatus(
|
mRuntimeHost->SetVideoIOStatus(mVideoIO->State());
|
||||||
"decklink",
|
|
||||||
mVideoIO->OutputModelName(),
|
|
||||||
mVideoIO->SupportsInternalKeying(),
|
|
||||||
mVideoIO->SupportsExternalKeying(),
|
|
||||||
mVideoIO->KeyerInterfaceAvailable(),
|
|
||||||
mRuntimeHost->ExternalKeyingEnabled(),
|
|
||||||
mVideoIO->ExternalKeyingActive(),
|
|
||||||
mVideoIO->StatusMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLComposite::InitOpenGLState()
|
bool OpenGLComposite::InitOpenGLState()
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ public:
|
|||||||
OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC);
|
OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC);
|
||||||
~OpenGLComposite();
|
~OpenGLComposite();
|
||||||
|
|
||||||
bool InitDeckLink();
|
bool InitializeVideoIO();
|
||||||
bool InitVideoIO();
|
|
||||||
bool Start();
|
bool Start();
|
||||||
bool Stop();
|
bool Stop();
|
||||||
bool ReloadShader();
|
bool ReloadShader();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
|
|
||||||
OpenGLVideoIOBridge::OpenGLVideoIOBridge(
|
OpenGLVideoIOBridge::OpenGLVideoIOBridge(
|
||||||
VideoIODevice& videoIO,
|
VideoIODevice* videoIO,
|
||||||
OpenGLRenderer& renderer,
|
OpenGLRenderer& renderer,
|
||||||
OpenGLRenderPipeline& renderPipeline,
|
OpenGLRenderPipeline& renderPipeline,
|
||||||
RuntimeHost& runtimeHost,
|
RuntimeHost& runtimeHost,
|
||||||
@@ -24,6 +24,11 @@ OpenGLVideoIOBridge::OpenGLVideoIOBridge(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLVideoIOBridge::SetVideoIODevice(VideoIODevice* videoIO)
|
||||||
|
{
|
||||||
|
mVideoIO = videoIO;
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult)
|
void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult)
|
||||||
{
|
{
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
@@ -57,7 +62,10 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe
|
|||||||
|
|
||||||
void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
|
void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
|
||||||
{
|
{
|
||||||
const VideoIOState& state = mVideoIO.State();
|
if (mVideoIO == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const VideoIOState& state = mVideoIO->State();
|
||||||
mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
||||||
|
|
||||||
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
|
if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr)
|
||||||
@@ -91,17 +99,20 @@ void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame)
|
|||||||
|
|
||||||
void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
|
void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion)
|
||||||
{
|
{
|
||||||
|
if (mVideoIO == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
RecordFramePacing(completion.result);
|
RecordFramePacing(completion.result);
|
||||||
|
|
||||||
EnterCriticalSection(&mMutex);
|
EnterCriticalSection(&mMutex);
|
||||||
|
|
||||||
VideoIOOutputFrame outputFrame;
|
VideoIOOutputFrame outputFrame;
|
||||||
if (!mVideoIO.BeginOutputFrame(outputFrame))
|
if (!mVideoIO->BeginOutputFrame(outputFrame))
|
||||||
{
|
{
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const VideoIOState& state = mVideoIO.State();
|
const VideoIOState& state = mVideoIO->State();
|
||||||
RenderPipelineFrameContext frameContext;
|
RenderPipelineFrameContext frameContext;
|
||||||
frameContext.videoState = state;
|
frameContext.videoState = state;
|
||||||
frameContext.completion = completion;
|
frameContext.completion = completion;
|
||||||
@@ -111,12 +122,12 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet
|
|||||||
|
|
||||||
mRenderPipeline.RenderFrame(frameContext, outputFrame);
|
mRenderPipeline.RenderFrame(frameContext, outputFrame);
|
||||||
|
|
||||||
mVideoIO.EndOutputFrame(outputFrame);
|
mVideoIO->EndOutputFrame(outputFrame);
|
||||||
|
|
||||||
mVideoIO.AccountForCompletionResult(completion.result);
|
mVideoIO->AccountForCompletionResult(completion.result);
|
||||||
|
|
||||||
// Schedule the next frame for playout
|
// Schedule the next frame for playout
|
||||||
mVideoIO.ScheduleOutputFrame(outputFrame);
|
mVideoIO->ScheduleOutputFrame(outputFrame);
|
||||||
|
|
||||||
wglMakeCurrent(NULL, NULL);
|
wglMakeCurrent(NULL, NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class OpenGLVideoIOBridge
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLVideoIOBridge(
|
OpenGLVideoIOBridge(
|
||||||
VideoIODevice& videoIO,
|
VideoIODevice* videoIO,
|
||||||
OpenGLRenderer& renderer,
|
OpenGLRenderer& renderer,
|
||||||
OpenGLRenderPipeline& renderPipeline,
|
OpenGLRenderPipeline& renderPipeline,
|
||||||
RuntimeHost& runtimeHost,
|
RuntimeHost& runtimeHost,
|
||||||
@@ -21,13 +21,15 @@ public:
|
|||||||
HDC hdc,
|
HDC hdc,
|
||||||
HGLRC hglrc);
|
HGLRC hglrc);
|
||||||
|
|
||||||
|
void SetVideoIODevice(VideoIODevice* videoIO);
|
||||||
|
|
||||||
void VideoFrameArrived(const VideoIOFrame& inputFrame);
|
void VideoFrameArrived(const VideoIOFrame& inputFrame);
|
||||||
void PlayoutFrameCompleted(const VideoIOCompletion& completion);
|
void PlayoutFrameCompleted(const VideoIOCompletion& completion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RecordFramePacing(VideoIOCompletionResult completionResult);
|
void RecordFramePacing(VideoIOCompletionResult completionResult);
|
||||||
|
|
||||||
VideoIODevice& mVideoIO;
|
VideoIODevice* mVideoIO;
|
||||||
OpenGLRenderer& mRenderer;
|
OpenGLRenderer& mRenderer;
|
||||||
OpenGLRenderPipeline& mRenderPipeline;
|
OpenGLRenderPipeline& mRenderPipeline;
|
||||||
RuntimeHost& mRuntimeHost;
|
RuntimeHost& mRuntimeHost;
|
||||||
|
|||||||
@@ -1228,25 +1228,20 @@ void RuntimeHost::MarkRenderStateDirtyLocked()
|
|||||||
mRenderStateVersion.fetch_add(1, std::memory_order_relaxed);
|
mRenderStateVersion.fetch_add(1, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
void RuntimeHost::SetVideoIOStatus(const VideoIOState& state)
|
||||||
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
|
|
||||||
{
|
|
||||||
SetVideoIOStatus("decklink", modelName, supportsInternalKeying, supportsExternalKeying, keyerInterfaceAvailable,
|
|
||||||
externalKeyingRequested, externalKeyingActive, statusMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuntimeHost::SetVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
|
||||||
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
mDeckLinkOutputStatus.backendName = backendName;
|
mVideoIOStatus.backendId = state.backendId;
|
||||||
mDeckLinkOutputStatus.modelName = modelName;
|
mVideoIOStatus.deviceName = state.deviceName;
|
||||||
mDeckLinkOutputStatus.supportsInternalKeying = supportsInternalKeying;
|
mVideoIOStatus.capabilities = state.capabilities;
|
||||||
mDeckLinkOutputStatus.supportsExternalKeying = supportsExternalKeying;
|
mVideoIOStatus.hasInputDevice = state.hasInputDevice;
|
||||||
mDeckLinkOutputStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
|
mVideoIOStatus.hasInputSource = state.hasInputSource;
|
||||||
mDeckLinkOutputStatus.externalKeyingRequested = externalKeyingRequested;
|
mVideoIOStatus.inputDisplayModeName = state.inputDisplayModeName;
|
||||||
mDeckLinkOutputStatus.externalKeyingActive = externalKeyingActive;
|
mVideoIOStatus.outputDisplayModeName = state.outputDisplayModeName;
|
||||||
mDeckLinkOutputStatus.statusMessage = statusMessage;
|
mVideoIOStatus.externalKeyingRequested = state.externalKeyingRequested;
|
||||||
|
mVideoIOStatus.externalKeyingActive = state.externalKeyingActive;
|
||||||
|
mVideoIOStatus.statusMessage = state.statusMessage;
|
||||||
|
mVideoIOStatus.formatStatusMessage = state.formatStatusMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
|
void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
|
||||||
@@ -1481,61 +1476,67 @@ bool RuntimeHost::LoadConfig(std::string& error)
|
|||||||
const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast<double>(mConfig.maxTemporalHistoryFrames));
|
const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast<double>(mConfig.maxTemporalHistoryFrames));
|
||||||
mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue);
|
mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue);
|
||||||
}
|
}
|
||||||
|
if (const JsonValue* videoBackendValue = configJson.find("videoBackend"))
|
||||||
|
{
|
||||||
|
VideoIOBackendId backendId = mConfig.videoIO.backendId;
|
||||||
|
if (videoBackendValue->isString() && ParseVideoIOBackendId(videoBackendValue->asString(), backendId))
|
||||||
|
mConfig.videoIO.backendId = backendId;
|
||||||
|
}
|
||||||
if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying"))
|
if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying"))
|
||||||
mConfig.enableExternalKeying = enableExternalKeyingValue->asBoolean(mConfig.enableExternalKeying);
|
mConfig.videoIO.externalKeyingEnabled = enableExternalKeyingValue->asBoolean(mConfig.videoIO.externalKeyingEnabled);
|
||||||
if (const JsonValue* videoFormatValue = configJson.find("videoFormat"))
|
if (const JsonValue* videoFormatValue = configJson.find("videoFormat"))
|
||||||
{
|
{
|
||||||
if (videoFormatValue->isString() && !videoFormatValue->asString().empty())
|
if (videoFormatValue->isString() && !videoFormatValue->asString().empty())
|
||||||
{
|
{
|
||||||
mConfig.inputVideoFormat = videoFormatValue->asString();
|
mConfig.videoIO.inputMode.videoFormat = videoFormatValue->asString();
|
||||||
mConfig.outputVideoFormat = videoFormatValue->asString();
|
mConfig.videoIO.outputMode.videoFormat = videoFormatValue->asString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const JsonValue* frameRateValue = configJson.find("frameRate"))
|
if (const JsonValue* frameRateValue = configJson.find("frameRate"))
|
||||||
{
|
{
|
||||||
if (frameRateValue->isString() && !frameRateValue->asString().empty())
|
if (frameRateValue->isString() && !frameRateValue->asString().empty())
|
||||||
{
|
{
|
||||||
mConfig.inputFrameRate = frameRateValue->asString();
|
mConfig.videoIO.inputMode.frameRate = frameRateValue->asString();
|
||||||
mConfig.outputFrameRate = frameRateValue->asString();
|
mConfig.videoIO.outputMode.frameRate = frameRateValue->asString();
|
||||||
}
|
}
|
||||||
else if (frameRateValue->isNumber())
|
else if (frameRateValue->isNumber())
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << frameRateValue->asNumber();
|
stream << frameRateValue->asNumber();
|
||||||
mConfig.inputFrameRate = stream.str();
|
mConfig.videoIO.inputMode.frameRate = stream.str();
|
||||||
mConfig.outputFrameRate = stream.str();
|
mConfig.videoIO.outputMode.frameRate = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat"))
|
if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat"))
|
||||||
{
|
{
|
||||||
if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty())
|
if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty())
|
||||||
mConfig.inputVideoFormat = inputVideoFormatValue->asString();
|
mConfig.videoIO.inputMode.videoFormat = inputVideoFormatValue->asString();
|
||||||
}
|
}
|
||||||
if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate"))
|
if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate"))
|
||||||
{
|
{
|
||||||
if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty())
|
if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty())
|
||||||
mConfig.inputFrameRate = inputFrameRateValue->asString();
|
mConfig.videoIO.inputMode.frameRate = inputFrameRateValue->asString();
|
||||||
else if (inputFrameRateValue->isNumber())
|
else if (inputFrameRateValue->isNumber())
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << inputFrameRateValue->asNumber();
|
stream << inputFrameRateValue->asNumber();
|
||||||
mConfig.inputFrameRate = stream.str();
|
mConfig.videoIO.inputMode.frameRate = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat"))
|
if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat"))
|
||||||
{
|
{
|
||||||
if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty())
|
if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty())
|
||||||
mConfig.outputVideoFormat = outputVideoFormatValue->asString();
|
mConfig.videoIO.outputMode.videoFormat = outputVideoFormatValue->asString();
|
||||||
}
|
}
|
||||||
if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate"))
|
if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate"))
|
||||||
{
|
{
|
||||||
if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty())
|
if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty())
|
||||||
mConfig.outputFrameRate = outputFrameRateValue->asString();
|
mConfig.videoIO.outputMode.frameRate = outputFrameRateValue->asString();
|
||||||
else if (outputFrameRateValue->isNumber())
|
else if (outputFrameRateValue->isNumber())
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << outputFrameRateValue->asNumber();
|
stream << outputFrameRateValue->asNumber();
|
||||||
mConfig.outputFrameRate = stream.str();
|
mConfig.videoIO.outputMode.frameRate = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1867,11 +1868,12 @@ JsonValue RuntimeHost::BuildStateValue() const
|
|||||||
app.set("oscPort", JsonValue(static_cast<double>(mConfig.oscPort)));
|
app.set("oscPort", JsonValue(static_cast<double>(mConfig.oscPort)));
|
||||||
app.set("autoReload", JsonValue(mAutoReloadEnabled));
|
app.set("autoReload", JsonValue(mAutoReloadEnabled));
|
||||||
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mConfig.maxTemporalHistoryFrames)));
|
app.set("maxTemporalHistoryFrames", JsonValue(static_cast<double>(mConfig.maxTemporalHistoryFrames)));
|
||||||
app.set("enableExternalKeying", JsonValue(mConfig.enableExternalKeying));
|
app.set("videoBackend", JsonValue(VideoIOBackendName(mConfig.videoIO.backendId)));
|
||||||
app.set("inputVideoFormat", JsonValue(mConfig.inputVideoFormat));
|
app.set("enableExternalKeying", JsonValue(mConfig.videoIO.externalKeyingEnabled));
|
||||||
app.set("inputFrameRate", JsonValue(mConfig.inputFrameRate));
|
app.set("inputVideoFormat", JsonValue(mConfig.videoIO.inputMode.videoFormat));
|
||||||
app.set("outputVideoFormat", JsonValue(mConfig.outputVideoFormat));
|
app.set("inputFrameRate", JsonValue(mConfig.videoIO.inputMode.frameRate));
|
||||||
app.set("outputFrameRate", JsonValue(mConfig.outputFrameRate));
|
app.set("outputVideoFormat", JsonValue(mConfig.videoIO.outputMode.videoFormat));
|
||||||
|
app.set("outputFrameRate", JsonValue(mConfig.videoIO.outputMode.frameRate));
|
||||||
root.set("app", app);
|
root.set("app", app);
|
||||||
|
|
||||||
JsonValue runtime = JsonValue::MakeObject();
|
JsonValue runtime = JsonValue::MakeObject();
|
||||||
@@ -1887,25 +1889,22 @@ JsonValue RuntimeHost::BuildStateValue() const
|
|||||||
video.set("modeName", JsonValue(mSignalModeName));
|
video.set("modeName", JsonValue(mSignalModeName));
|
||||||
root.set("video", video);
|
root.set("video", video);
|
||||||
|
|
||||||
JsonValue deckLink = JsonValue::MakeObject();
|
|
||||||
deckLink.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
|
|
||||||
deckLink.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
|
|
||||||
deckLink.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
|
|
||||||
deckLink.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
|
|
||||||
deckLink.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
|
|
||||||
deckLink.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
|
|
||||||
deckLink.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
|
|
||||||
root.set("decklink", deckLink);
|
|
||||||
|
|
||||||
JsonValue videoIO = JsonValue::MakeObject();
|
JsonValue videoIO = JsonValue::MakeObject();
|
||||||
videoIO.set("backend", JsonValue(mDeckLinkOutputStatus.backendName));
|
videoIO.set("backend", JsonValue(VideoIOBackendName(mVideoIOStatus.backendId)));
|
||||||
videoIO.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName));
|
videoIO.set("deviceName", JsonValue(mVideoIOStatus.deviceName));
|
||||||
videoIO.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying));
|
videoIO.set("hasInputDevice", JsonValue(mVideoIOStatus.hasInputDevice));
|
||||||
videoIO.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying));
|
videoIO.set("hasInputSource", JsonValue(mVideoIOStatus.hasInputSource));
|
||||||
videoIO.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable));
|
videoIO.set("inputModeName", JsonValue(mVideoIOStatus.inputDisplayModeName));
|
||||||
videoIO.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested));
|
videoIO.set("outputModeName", JsonValue(mVideoIOStatus.outputDisplayModeName));
|
||||||
videoIO.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive));
|
JsonValue capabilities = JsonValue::MakeObject();
|
||||||
videoIO.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage));
|
capabilities.set("supportsInternalKeying", JsonValue(mVideoIOStatus.capabilities.supportsInternalKeying));
|
||||||
|
capabilities.set("supportsExternalKeying", JsonValue(mVideoIOStatus.capabilities.supportsExternalKeying));
|
||||||
|
capabilities.set("keyerInterfaceAvailable", JsonValue(mVideoIOStatus.capabilities.keyerInterfaceAvailable));
|
||||||
|
videoIO.set("capabilities", capabilities);
|
||||||
|
videoIO.set("externalKeyingRequested", JsonValue(mVideoIOStatus.externalKeyingRequested));
|
||||||
|
videoIO.set("externalKeyingActive", JsonValue(mVideoIOStatus.externalKeyingActive));
|
||||||
|
videoIO.set("statusMessage", JsonValue(mVideoIOStatus.statusMessage));
|
||||||
|
videoIO.set("formatStatusMessage", JsonValue(mVideoIOStatus.formatStatusMessage));
|
||||||
root.set("videoIO", videoIO);
|
root.set("videoIO", videoIO);
|
||||||
|
|
||||||
JsonValue performance = JsonValue::MakeObject();
|
JsonValue performance = JsonValue::MakeObject();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "RuntimeJson.h"
|
#include "RuntimeJson.h"
|
||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
#include "VideoIOTypes.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -38,10 +39,7 @@ public:
|
|||||||
void SetCompileStatus(bool succeeded, const std::string& message);
|
void SetCompileStatus(bool succeeded, const std::string& message);
|
||||||
void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
||||||
bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
|
||||||
void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
void SetVideoIOStatus(const VideoIOState& state);
|
||||||
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
|
|
||||||
void SetVideoIOStatus(const std::string& backendName, const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
|
||||||
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
|
|
||||||
void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
||||||
bool TrySetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
bool TrySetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
||||||
void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
@@ -65,11 +63,8 @@ public:
|
|||||||
unsigned short GetServerPort() const { return mServerPort; }
|
unsigned short GetServerPort() const { return mServerPort; }
|
||||||
unsigned short GetOscPort() const { return mConfig.oscPort; }
|
unsigned short GetOscPort() const { return mConfig.oscPort; }
|
||||||
unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; }
|
unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; }
|
||||||
bool ExternalKeyingEnabled() const { return mConfig.enableExternalKeying; }
|
bool ExternalKeyingEnabled() const { return mConfig.videoIO.externalKeyingEnabled; }
|
||||||
const std::string& GetInputVideoFormat() const { return mConfig.inputVideoFormat; }
|
VideoIOConfiguration GetVideoIOConfiguration() const { return mConfig.videoIO; }
|
||||||
const std::string& GetInputFrameRate() const { return mConfig.inputFrameRate; }
|
|
||||||
const std::string& GetOutputVideoFormat() const { return mConfig.outputVideoFormat; }
|
|
||||||
const std::string& GetOutputFrameRate() const { return mConfig.outputFrameRate; }
|
|
||||||
void SetServerPort(unsigned short port);
|
void SetServerPort(unsigned short port);
|
||||||
bool AutoReloadEnabled() const { return mAutoReloadEnabled; }
|
bool AutoReloadEnabled() const { return mAutoReloadEnabled; }
|
||||||
|
|
||||||
@@ -81,23 +76,22 @@ private:
|
|||||||
unsigned short oscPort = 9000;
|
unsigned short oscPort = 9000;
|
||||||
bool autoReload = true;
|
bool autoReload = true;
|
||||||
unsigned maxTemporalHistoryFrames = 4;
|
unsigned maxTemporalHistoryFrames = 4;
|
||||||
bool enableExternalKeying = false;
|
VideoIOConfiguration videoIO;
|
||||||
std::string inputVideoFormat = "1080p";
|
|
||||||
std::string inputFrameRate = "59.94";
|
|
||||||
std::string outputVideoFormat = "1080p";
|
|
||||||
std::string outputFrameRate = "59.94";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeckLinkOutputStatus
|
struct VideoIOStatusSnapshot
|
||||||
{
|
{
|
||||||
std::string backendName = "decklink";
|
VideoIOBackendId backendId = VideoIOBackendId::DeckLink;
|
||||||
std::string modelName;
|
std::string deviceName;
|
||||||
bool supportsInternalKeying = false;
|
VideoIOCapabilities capabilities;
|
||||||
bool supportsExternalKeying = false;
|
bool hasInputDevice = false;
|
||||||
bool keyerInterfaceAvailable = false;
|
bool hasInputSource = false;
|
||||||
|
std::string inputDisplayModeName = "1080p59.94";
|
||||||
|
std::string outputDisplayModeName = "1080p59.94";
|
||||||
bool externalKeyingRequested = false;
|
bool externalKeyingRequested = false;
|
||||||
bool externalKeyingActive = false;
|
bool externalKeyingActive = false;
|
||||||
std::string statusMessage;
|
std::string statusMessage;
|
||||||
|
std::string formatStatusMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LayerPersistentState
|
struct LayerPersistentState
|
||||||
@@ -176,7 +170,7 @@ private:
|
|||||||
uint64_t mLateFrameCount;
|
uint64_t mLateFrameCount;
|
||||||
uint64_t mDroppedFrameCount;
|
uint64_t mDroppedFrameCount;
|
||||||
uint64_t mFlushedFrameCount;
|
uint64_t mFlushedFrameCount;
|
||||||
DeckLinkOutputStatus mDeckLinkOutputStatus;
|
VideoIOStatusSnapshot mVideoIOStatus;
|
||||||
unsigned short mServerPort;
|
unsigned short mServerPort;
|
||||||
bool mAutoReloadEnabled;
|
bool mAutoReloadEnabled;
|
||||||
std::chrono::steady_clock::time_point mStartTime;
|
std::chrono::steady_clock::time_point mStartTime;
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "VideoIOBackendFactory.h"
|
||||||
|
|
||||||
|
#include "DeckLinkSession.h"
|
||||||
|
#include "VideoIOTypes.h"
|
||||||
|
|
||||||
|
std::unique_ptr<VideoIODevice> CreateVideoIODevice(VideoIOBackendId backendId, std::string& error)
|
||||||
|
{
|
||||||
|
switch (backendId)
|
||||||
|
{
|
||||||
|
case VideoIOBackendId::DeckLink:
|
||||||
|
return std::make_unique<DeckLinkSession>();
|
||||||
|
}
|
||||||
|
|
||||||
|
error = "Unsupported video I/O backend.";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "VideoIOConfig.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class VideoIODevice;
|
||||||
|
|
||||||
|
std::unique_ptr<VideoIODevice> CreateVideoIODevice(VideoIOBackendId backendId, std::string& error);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include "VideoIOConfig.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string NormalizeToken(std::string value)
|
||||||
|
{
|
||||||
|
std::transform(value.begin(), value.end(), value.begin(),
|
||||||
|
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* VideoIOBackendName(VideoIOBackendId backendId)
|
||||||
|
{
|
||||||
|
switch (backendId)
|
||||||
|
{
|
||||||
|
case VideoIOBackendId::DeckLink:
|
||||||
|
return "decklink";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseVideoIOBackendId(const std::string& value, VideoIOBackendId& backendId)
|
||||||
|
{
|
||||||
|
const std::string normalized = NormalizeToken(value);
|
||||||
|
if (normalized.empty() || normalized == "decklink")
|
||||||
|
{
|
||||||
|
backendId = VideoIOBackendId::DeckLink;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum class VideoIOBackendId
|
||||||
|
{
|
||||||
|
DeckLink
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* VideoIOBackendName(VideoIOBackendId backendId);
|
||||||
|
bool ParseVideoIOBackendId(const std::string& value, VideoIOBackendId& backendId);
|
||||||
|
|
||||||
|
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 VideoIOModeConfiguration
|
||||||
|
{
|
||||||
|
std::string videoFormat = "1080p";
|
||||||
|
std::string frameRate = "59.94";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoIOConfiguration
|
||||||
|
{
|
||||||
|
VideoIOBackendId backendId = VideoIOBackendId::DeckLink;
|
||||||
|
VideoIOModeConfiguration inputMode;
|
||||||
|
VideoIOModeConfiguration outputMode;
|
||||||
|
bool externalKeyingEnabled = false;
|
||||||
|
bool preferTenBit = true;
|
||||||
|
};
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DeckLinkDisplayMode.h"
|
#include "VideoIOConfig.h"
|
||||||
#include "VideoIOFormat.h"
|
#include "VideoIOFormat.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum class VideoIOBackend
|
struct VideoIOCapabilities
|
||||||
{
|
{
|
||||||
DeckLink
|
bool supportsInternalKeying = false;
|
||||||
|
bool supportsExternalKeying = false;
|
||||||
|
bool keyerInterfaceAvailable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class VideoIOCompletionResult
|
enum class VideoIOCompletionResult
|
||||||
@@ -21,15 +23,9 @@ enum class VideoIOCompletionResult
|
|||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoIOConfig
|
|
||||||
{
|
|
||||||
VideoFormatSelection videoModes;
|
|
||||||
bool externalKeyingEnabled = false;
|
|
||||||
bool preferTenBit = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VideoIOState
|
struct VideoIOState
|
||||||
{
|
{
|
||||||
|
VideoIOBackendId backendId = VideoIOBackendId::DeckLink;
|
||||||
FrameSize inputFrameSize;
|
FrameSize inputFrameSize;
|
||||||
FrameSize outputFrameSize;
|
FrameSize outputFrameSize;
|
||||||
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
@@ -40,14 +36,13 @@ struct VideoIOState
|
|||||||
unsigned outputPackTextureWidth = 0;
|
unsigned outputPackTextureWidth = 0;
|
||||||
std::string inputDisplayModeName = "1080p59.94";
|
std::string inputDisplayModeName = "1080p59.94";
|
||||||
std::string outputDisplayModeName = "1080p59.94";
|
std::string outputDisplayModeName = "1080p59.94";
|
||||||
std::string outputModelName;
|
std::string deviceName;
|
||||||
std::string statusMessage;
|
std::string statusMessage;
|
||||||
std::string formatStatusMessage;
|
std::string formatStatusMessage;
|
||||||
bool hasInputDevice = false;
|
bool hasInputDevice = false;
|
||||||
bool hasInputSource = false;
|
bool hasInputSource = false;
|
||||||
bool supportsInternalKeying = false;
|
VideoIOCapabilities capabilities;
|
||||||
bool supportsExternalKeying = false;
|
bool externalKeyingRequested = false;
|
||||||
bool keyerInterfaceAvailable = false;
|
|
||||||
bool externalKeyingActive = false;
|
bool externalKeyingActive = false;
|
||||||
double frameBudgetMilliseconds = 0.0;
|
double frameBudgetMilliseconds = 0.0;
|
||||||
};
|
};
|
||||||
@@ -93,11 +88,12 @@ public:
|
|||||||
using OutputFrameCallback = std::function<void(const VideoIOCompletion&)>;
|
using OutputFrameCallback = std::function<void(const VideoIOCompletion&)>;
|
||||||
|
|
||||||
virtual ~VideoIODevice() = default;
|
virtual ~VideoIODevice() = default;
|
||||||
|
virtual VideoIOBackendId BackendId() const = 0;
|
||||||
virtual void ReleaseResources() = 0;
|
virtual void ReleaseResources() = 0;
|
||||||
virtual bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) = 0;
|
virtual bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) = 0;
|
||||||
virtual bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) = 0;
|
virtual bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) = 0;
|
||||||
virtual bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) = 0;
|
virtual bool ConfigureInput(InputFrameCallback callback, std::string& error) = 0;
|
||||||
virtual bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) = 0;
|
virtual bool ConfigureOutput(OutputFrameCallback callback, std::string& error) = 0;
|
||||||
virtual bool Start() = 0;
|
virtual bool Start() = 0;
|
||||||
virtual bool Stop() = 0;
|
virtual bool Stop() = 0;
|
||||||
virtual const VideoIOState& State() const = 0;
|
virtual const VideoIOState& State() const = 0;
|
||||||
@@ -126,10 +122,11 @@ public:
|
|||||||
unsigned OutputPackTextureWidth() const { return State().outputPackTextureWidth; }
|
unsigned OutputPackTextureWidth() const { return State().outputPackTextureWidth; }
|
||||||
const std::string& FormatStatusMessage() const { return State().formatStatusMessage; }
|
const std::string& FormatStatusMessage() const { return State().formatStatusMessage; }
|
||||||
const std::string& InputDisplayModeName() const { return State().inputDisplayModeName; }
|
const std::string& InputDisplayModeName() const { return State().inputDisplayModeName; }
|
||||||
const std::string& OutputModelName() const { return State().outputModelName; }
|
const std::string& DeviceName() const { return State().deviceName; }
|
||||||
bool SupportsInternalKeying() const { return State().supportsInternalKeying; }
|
bool SupportsInternalKeying() const { return State().capabilities.supportsInternalKeying; }
|
||||||
bool SupportsExternalKeying() const { return State().supportsExternalKeying; }
|
bool SupportsExternalKeying() const { return State().capabilities.supportsExternalKeying; }
|
||||||
bool KeyerInterfaceAvailable() const { return State().keyerInterfaceAvailable; }
|
bool KeyerInterfaceAvailable() const { return State().capabilities.keyerInterfaceAvailable; }
|
||||||
|
bool ExternalKeyingRequested() const { return State().externalKeyingRequested; }
|
||||||
bool ExternalKeyingActive() const { return State().externalKeyingActive; }
|
bool ExternalKeyingActive() const { return State().externalKeyingActive; }
|
||||||
const std::string& StatusMessage() const { return State().statusMessage; }
|
const std::string& StatusMessage() const { return State().statusMessage; }
|
||||||
double FrameBudgetMilliseconds() const { return State().frameBudgetMilliseconds; }
|
double FrameBudgetMilliseconds() const { return State().frameBudgetMilliseconds; }
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ std::string NormalizeModeToken(const std::string& value)
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName)
|
bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName)
|
||||||
{
|
{
|
||||||
VideoFormat videoMode;
|
DeckLinkVideoMode videoMode;
|
||||||
if (!ResolveConfiguredVideoFormat(videoFormat, frameRate, videoMode))
|
if (!ResolveConfiguredDeckLinkVideoMode(mode, videoMode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
displayMode = videoMode.displayMode;
|
displayMode = videoMode.displayMode;
|
||||||
@@ -24,10 +24,10 @@ bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::str
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode)
|
bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode)
|
||||||
{
|
{
|
||||||
const std::string formatToken = NormalizeModeToken(videoFormat);
|
const std::string formatToken = NormalizeModeToken(mode.videoFormat);
|
||||||
const std::string frameToken = NormalizeModeToken(frameRate);
|
const std::string frameToken = NormalizeModeToken(mode.frameRate);
|
||||||
const std::string combinedToken = formatToken + frameToken;
|
const std::string combinedToken = formatToken + frameToken;
|
||||||
|
|
||||||
struct ModeOption
|
struct ModeOption
|
||||||
@@ -98,25 +98,22 @@ bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::str
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResolveConfiguredVideoFormats(
|
bool ResolveConfiguredDeckLinkVideoModes(
|
||||||
const std::string& inputVideoFormat,
|
const VideoIOConfiguration& config,
|
||||||
const std::string& inputFrameRate,
|
DeckLinkVideoModeSelection& videoModes,
|
||||||
const std::string& outputVideoFormat,
|
|
||||||
const std::string& outputFrameRate,
|
|
||||||
VideoFormatSelection& videoModes,
|
|
||||||
std::string& error)
|
std::string& error)
|
||||||
{
|
{
|
||||||
if (!ResolveConfiguredVideoFormat(inputVideoFormat, inputFrameRate, videoModes.input))
|
if (!ResolveConfiguredDeckLinkVideoMode(config.inputMode, videoModes.input))
|
||||||
{
|
{
|
||||||
error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " +
|
error = "Unsupported DeckLink input mode in config/runtime-host.json: " +
|
||||||
inputVideoFormat + " / " + inputFrameRate;
|
config.inputMode.videoFormat + " / " + config.inputMode.frameRate;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ResolveConfiguredVideoFormat(outputVideoFormat, outputFrameRate, videoModes.output))
|
if (!ResolveConfiguredDeckLinkVideoMode(config.outputMode, videoModes.output))
|
||||||
{
|
{
|
||||||
error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
|
error = "Unsupported DeckLink output mode in config/runtime-host.json: " +
|
||||||
outputVideoFormat + " / " + outputFrameRate;
|
config.outputMode.videoFormat + " / " + config.outputMode.frameRate;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DeckLinkAPI_h.h"
|
#include "DeckLinkAPI_h.h"
|
||||||
|
#include "VideoIOConfig.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct FrameSize
|
struct DeckLinkVideoMode
|
||||||
{
|
|
||||||
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;
|
BMDDisplayMode displayMode = bmdModeHD1080p5994;
|
||||||
std::string displayName = "1080p59.94";
|
std::string displayName = "1080p59.94";
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoFormatSelection
|
struct DeckLinkVideoModeSelection
|
||||||
{
|
{
|
||||||
VideoFormat input;
|
DeckLinkVideoMode input;
|
||||||
VideoFormat output;
|
DeckLinkVideoMode 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 ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName);
|
||||||
bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode);
|
bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode);
|
||||||
bool ResolveConfiguredVideoFormats(
|
bool ResolveConfiguredDeckLinkVideoModes(
|
||||||
const std::string& inputVideoFormat,
|
const VideoIOConfiguration& config,
|
||||||
const std::string& inputFrameRate,
|
DeckLinkVideoModeSelection& videoModes,
|
||||||
const std::string& outputVideoFormat,
|
|
||||||
const std::string& outputFrameRate,
|
|
||||||
VideoFormatSelection& videoModes,
|
|
||||||
std::string& error);
|
std::string& error);
|
||||||
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode);
|
bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode);
|
||||||
|
|||||||
@@ -92,14 +92,19 @@ void DeckLinkSession::ReleaseResources()
|
|||||||
output.Release();
|
output.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
|
bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error)
|
||||||
{
|
{
|
||||||
CComPtr<IDeckLinkIterator> deckLinkIterator;
|
CComPtr<IDeckLinkIterator> deckLinkIterator;
|
||||||
CComPtr<IDeckLinkDisplayMode> inputMode;
|
CComPtr<IDeckLinkDisplayMode> inputMode;
|
||||||
CComPtr<IDeckLinkDisplayMode> outputMode;
|
CComPtr<IDeckLinkDisplayMode> outputMode;
|
||||||
|
|
||||||
mState.inputDisplayModeName = videoModes.input.displayName;
|
mState.backendId = BackendId();
|
||||||
mState.outputDisplayModeName = videoModes.output.displayName;
|
mState.externalKeyingRequested = config.externalKeyingEnabled;
|
||||||
|
if (!ResolveConfiguredDeckLinkVideoModes(config, mConfiguredModes, error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mState.inputDisplayModeName = mConfiguredModes.input.displayName;
|
||||||
|
mState.outputDisplayModeName = mConfiguredModes.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))
|
||||||
@@ -151,9 +156,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoM
|
|||||||
output.Release();
|
output.Release();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mState.outputModelName = modelName;
|
mState.deviceName = modelName;
|
||||||
mState.supportsInternalKeying = deviceSupportsInternalKeying;
|
mState.capabilities.supportsInternalKeying = deviceSupportsInternalKeying;
|
||||||
mState.supportsExternalKeying = deviceSupportsExternalKeying;
|
mState.capabilities.supportsExternalKeying = deviceSupportsExternalKeying;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,9 +183,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoM
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, videoModes.input.displayMode, &inputMode))
|
if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, mConfiguredModes.input.displayMode, &inputMode))
|
||||||
{
|
{
|
||||||
error = "Cannot get specified input BMDDisplayMode for configured mode: " + videoModes.input.displayName;
|
error = "Cannot get specified input BMDDisplayMode for configured mode: " + mConfiguredModes.input.displayName;
|
||||||
ReleaseResources();
|
ReleaseResources();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -194,9 +199,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoM
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, videoModes.output.displayMode, &outputMode))
|
if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, mConfiguredModes.output.displayMode, &outputMode))
|
||||||
{
|
{
|
||||||
error = "Cannot get specified output BMDDisplayMode for configured mode: " + videoModes.output.displayName;
|
error = "Cannot get specified output BMDDisplayMode for configured mode: " + mConfiguredModes.output.displayName;
|
||||||
ReleaseResources();
|
ReleaseResources();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -223,7 +228,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoM
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error)
|
bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error)
|
||||||
{
|
{
|
||||||
if (!output)
|
if (!output)
|
||||||
{
|
{
|
||||||
@@ -233,19 +238,19 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoMo
|
|||||||
|
|
||||||
mState.formatStatusMessage.clear();
|
mState.formatStatusMessage.clear();
|
||||||
|
|
||||||
const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, videoModes.input.displayMode, bmdFormat10BitYUV);
|
const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, mConfiguredModes.input.displayMode, bmdFormat10BitYUV);
|
||||||
mState.inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8;
|
mState.inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8;
|
||||||
if (input != nullptr && !inputTenBitSupported)
|
if (input != nullptr && !inputTenBitSupported)
|
||||||
mState.formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. ";
|
mState.formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. ";
|
||||||
|
|
||||||
const bool outputTenBitSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUV);
|
const bool outputTenBitSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUV);
|
||||||
const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUVA);
|
const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUVA);
|
||||||
mState.outputPixelFormat = outputAlphaRequired
|
mState.outputPixelFormat = config.externalKeyingEnabled
|
||||||
? (outputTenBitYuvaSupported ? VideoIOPixelFormat::Yuva10 : VideoIOPixelFormat::Bgra8)
|
? (outputTenBitYuvaSupported ? VideoIOPixelFormat::Yuva10 : VideoIOPixelFormat::Bgra8)
|
||||||
: (outputTenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Bgra8);
|
: (outputTenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Bgra8);
|
||||||
if (outputAlphaRequired && outputTenBitYuvaSupported)
|
if (config.externalKeyingEnabled && outputTenBitYuvaSupported)
|
||||||
mState.formatStatusMessage += "External keying requires alpha; using 10-bit YUVA output. ";
|
mState.formatStatusMessage += "External keying requires alpha; using 10-bit YUVA output. ";
|
||||||
else if (outputAlphaRequired)
|
else if (config.externalKeyingEnabled)
|
||||||
mState.formatStatusMessage += "External keying requires alpha, but DeckLink output does not report 10-bit YUVA support for the configured mode; using 8-bit BGRA output. ";
|
mState.formatStatusMessage += "External keying requires alpha, but DeckLink output does not report 10-bit YUVA support for the configured mode; using 8-bit BGRA output. ";
|
||||||
else if (!outputTenBitSupported)
|
else if (!outputTenBitSupported)
|
||||||
mState.formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. ";
|
mState.formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. ";
|
||||||
@@ -286,7 +291,7 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoMo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error)
|
bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& error)
|
||||||
{
|
{
|
||||||
mInputFrameCallback = std::move(callback);
|
mInputFrameCallback = std::move(callback);
|
||||||
|
|
||||||
@@ -298,7 +303,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, const VideoFor
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(mState.inputPixelFormat);
|
const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(mState.inputPixelFormat);
|
||||||
if (input->EnableVideoInput(inputVideoMode.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK)
|
if (input->EnableVideoInput(mConfiguredModes.input.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK)
|
||||||
{
|
{
|
||||||
if (mState.inputPixelFormat == VideoIOPixelFormat::V210)
|
if (mState.inputPixelFormat == VideoIOPixelFormat::V210)
|
||||||
{
|
{
|
||||||
@@ -306,7 +311,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, const VideoFor
|
|||||||
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
mState.inputFrameRowBytes = mState.inputFrameSize.width * 2u;
|
mState.inputFrameRowBytes = mState.inputFrameSize.width * 2u;
|
||||||
mState.captureTextureWidth = mState.inputFrameSize.width / 2u;
|
mState.captureTextureWidth = mState.inputFrameSize.width / 2u;
|
||||||
if (input->EnableVideoInput(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK)
|
if (input->EnableVideoInput(mConfiguredModes.input.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK)
|
||||||
{
|
{
|
||||||
std::ostringstream status;
|
std::ostringstream status;
|
||||||
status << "DeckLink formats: capture " << VideoIOPixelFormatName(mState.inputPixelFormat)
|
status << "DeckLink formats: capture " << VideoIOPixelFormatName(mState.inputPixelFormat)
|
||||||
@@ -341,26 +346,26 @@ input_enabled:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
|
bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, std::string& error)
|
||||||
{
|
{
|
||||||
mOutputFrameCallback = std::move(callback);
|
mOutputFrameCallback = std::move(callback);
|
||||||
|
|
||||||
if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK)
|
if (output->EnableVideoOutput(mConfiguredModes.output.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL)
|
if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL)
|
||||||
mState.keyerInterfaceAvailable = true;
|
mState.capabilities.keyerInterfaceAvailable = true;
|
||||||
|
|
||||||
if (externalKeyingEnabled)
|
if (mState.externalKeyingRequested)
|
||||||
{
|
{
|
||||||
if (!mState.supportsExternalKeying)
|
if (!mState.capabilities.supportsExternalKeying)
|
||||||
{
|
{
|
||||||
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
|
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support.";
|
||||||
}
|
}
|
||||||
else if (!mState.keyerInterfaceAvailable)
|
else if (!mState.capabilities.keyerInterfaceAvailable)
|
||||||
{
|
{
|
||||||
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
|
mState.statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface.";
|
||||||
}
|
}
|
||||||
@@ -374,7 +379,7 @@ bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, const VideoF
|
|||||||
mState.statusMessage = "External keying is active on the selected DeckLink output.";
|
mState.statusMessage = "External keying is active on the selected DeckLink output.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mState.supportsExternalKeying)
|
else if (mState.capabilities.supportsExternalKeying)
|
||||||
{
|
{
|
||||||
mState.statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
|
mState.statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,41 +20,14 @@ public:
|
|||||||
DeckLinkSession() = default;
|
DeckLinkSession() = default;
|
||||||
~DeckLinkSession();
|
~DeckLinkSession();
|
||||||
|
|
||||||
|
VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; }
|
||||||
void ReleaseResources() override;
|
void ReleaseResources() override;
|
||||||
bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) override;
|
bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) override;
|
||||||
bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) override;
|
bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) override;
|
||||||
bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) override;
|
bool ConfigureInput(InputFrameCallback callback, std::string& error) override;
|
||||||
bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) override;
|
bool ConfigureOutput(OutputFrameCallback callback, std::string& error) override;
|
||||||
bool Start() override;
|
bool Start() override;
|
||||||
bool Stop() override;
|
bool Stop() override;
|
||||||
|
|
||||||
bool HasInputDevice() const { return mState.hasInputDevice; }
|
|
||||||
bool HasInputSource() const { return mState.hasInputSource; }
|
|
||||||
void SetInputSourceMissing(bool missing) { mState.hasInputSource = !missing; }
|
|
||||||
bool InputOutputDimensionsDiffer() const { return mState.inputFrameSize != mState.outputFrameSize; }
|
|
||||||
const FrameSize& InputFrameSize() const { return mState.inputFrameSize; }
|
|
||||||
const FrameSize& OutputFrameSize() const { return mState.outputFrameSize; }
|
|
||||||
unsigned InputFrameWidth() const { return mState.inputFrameSize.width; }
|
|
||||||
unsigned InputFrameHeight() const { return mState.inputFrameSize.height; }
|
|
||||||
unsigned OutputFrameWidth() const { return mState.outputFrameSize.width; }
|
|
||||||
unsigned OutputFrameHeight() const { return mState.outputFrameSize.height; }
|
|
||||||
VideoIOPixelFormat InputPixelFormat() const { return mState.inputPixelFormat; }
|
|
||||||
VideoIOPixelFormat OutputPixelFormat() const { return mState.outputPixelFormat; }
|
|
||||||
bool InputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.inputPixelFormat); }
|
|
||||||
bool OutputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.outputPixelFormat); }
|
|
||||||
unsigned InputFrameRowBytes() const { return mState.inputFrameRowBytes; }
|
|
||||||
unsigned OutputFrameRowBytes() const { return mState.outputFrameRowBytes; }
|
|
||||||
unsigned CaptureTextureWidth() const { return mState.captureTextureWidth; }
|
|
||||||
unsigned OutputPackTextureWidth() const { return mState.outputPackTextureWidth; }
|
|
||||||
const std::string& FormatStatusMessage() const { return mState.formatStatusMessage; }
|
|
||||||
const std::string& InputDisplayModeName() const { return mState.inputDisplayModeName; }
|
|
||||||
const std::string& OutputModelName() const { return mState.outputModelName; }
|
|
||||||
bool SupportsInternalKeying() const { return mState.supportsInternalKeying; }
|
|
||||||
bool SupportsExternalKeying() const { return mState.supportsExternalKeying; }
|
|
||||||
bool KeyerInterfaceAvailable() const { return mState.keyerInterfaceAvailable; }
|
|
||||||
bool ExternalKeyingActive() const { return mState.externalKeyingActive; }
|
|
||||||
const std::string& StatusMessage() const { return mState.statusMessage; }
|
|
||||||
void SetStatusMessage(const std::string& message) { mState.statusMessage = message; }
|
|
||||||
const VideoIOState& State() const override { return mState; }
|
const VideoIOState& State() const override { return mState; }
|
||||||
VideoIOState& MutableState() override { return mState; }
|
VideoIOState& MutableState() override { return mState; }
|
||||||
double FrameBudgetMilliseconds() const;
|
double FrameBudgetMilliseconds() const;
|
||||||
@@ -76,4 +49,5 @@ private:
|
|||||||
VideoPlayoutScheduler mScheduler;
|
VideoPlayoutScheduler mScheduler;
|
||||||
InputFrameCallback mInputFrameCallback;
|
InputFrameCallback mInputFrameCallback;
|
||||||
OutputFrameCallback mOutputFrameCallback;
|
OutputFrameCallback mOutputFrameCallback;
|
||||||
|
DeckLinkVideoModeSelection mConfiguredModes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"shaderLibrary": "shaders",
|
"shaderLibrary": "shaders",
|
||||||
"serverPort": 8080,
|
"serverPort": 8080,
|
||||||
"oscPort": 9000,
|
"oscPort": 9000,
|
||||||
|
"videoBackend": "decklink",
|
||||||
"inputVideoFormat": "1080p",
|
"inputVideoFormat": "1080p",
|
||||||
"inputFrameRate": "59.94",
|
"inputFrameRate": "59.94",
|
||||||
"outputVideoFormat": "1080p",
|
"outputVideoFormat": "1080p",
|
||||||
|
|||||||
67
tests/RuntimeHostVideoIOStateTests.cpp
Normal file
67
tests/RuntimeHostVideoIOStateTests.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "RuntimeHost.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const char* message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cerr << "FAIL: " << message << "\n";
|
||||||
|
++gFailures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
RuntimeHost runtimeHost;
|
||||||
|
std::string error;
|
||||||
|
Expect(runtimeHost.Initialize(error), "runtime host initializes");
|
||||||
|
Expect(error.empty(), "runtime host initialization does not report an error");
|
||||||
|
|
||||||
|
VideoIOState state;
|
||||||
|
state.backendId = VideoIOBackendId::DeckLink;
|
||||||
|
state.deviceName = "Test Device";
|
||||||
|
state.hasInputDevice = true;
|
||||||
|
state.hasInputSource = true;
|
||||||
|
state.inputDisplayModeName = "fake input";
|
||||||
|
state.outputDisplayModeName = "fake output";
|
||||||
|
state.capabilities.supportsInternalKeying = true;
|
||||||
|
state.capabilities.supportsExternalKeying = true;
|
||||||
|
state.capabilities.keyerInterfaceAvailable = true;
|
||||||
|
state.externalKeyingRequested = true;
|
||||||
|
state.externalKeyingActive = true;
|
||||||
|
state.statusMessage = "ready";
|
||||||
|
state.formatStatusMessage = "using fake formats";
|
||||||
|
runtimeHost.SetVideoIOStatus(state);
|
||||||
|
|
||||||
|
JsonValue root;
|
||||||
|
Expect(ParseJson(runtimeHost.BuildStateJson(), root, error), "runtime state json parses");
|
||||||
|
Expect(root.find("videoIO") != nullptr, "runtime state exposes videoIO");
|
||||||
|
Expect(root.find("decklink") == nullptr, "runtime state no longer exposes a decklink top-level block");
|
||||||
|
|
||||||
|
const JsonValue* app = root.find("app");
|
||||||
|
Expect(app != nullptr, "runtime state exposes app settings");
|
||||||
|
Expect(app != nullptr && app->find("videoBackend") != nullptr, "app settings expose videoBackend");
|
||||||
|
Expect(app != nullptr && app->find("videoBackend")->asString() == "decklink", "videoBackend serializes as decklink");
|
||||||
|
|
||||||
|
const JsonValue* videoIO = root.find("videoIO");
|
||||||
|
Expect(videoIO != nullptr && videoIO->find("backend") != nullptr, "videoIO exposes backend");
|
||||||
|
Expect(videoIO != nullptr && videoIO->find("backend")->asString() == "decklink", "videoIO backend serializes as decklink");
|
||||||
|
Expect(videoIO != nullptr && videoIO->find("deviceName") != nullptr, "videoIO exposes device name");
|
||||||
|
Expect(videoIO != nullptr && videoIO->find("deviceName")->asString() == "Test Device", "videoIO device name matches");
|
||||||
|
Expect(videoIO != nullptr && videoIO->find("capabilities") != nullptr, "videoIO exposes capabilities");
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " RuntimeHost video I/O state test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "RuntimeHost video I/O state tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
41
tests/VideoIOBackendFactoryTests.cpp
Normal file
41
tests/VideoIOBackendFactoryTests.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "VideoIOBackendFactory.h"
|
||||||
|
#include "VideoIOTypes.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const char* message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cerr << "FAIL: " << message << "\n";
|
||||||
|
++gFailures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
std::unique_ptr<VideoIODevice> device = CreateVideoIODevice(VideoIOBackendId::DeckLink, error);
|
||||||
|
Expect(device != nullptr, "decklink backend factory returns a device");
|
||||||
|
Expect(!device || device->BackendId() == VideoIOBackendId::DeckLink, "decklink backend reports decklink id");
|
||||||
|
Expect(error.empty(), "supported backend does not produce an error");
|
||||||
|
|
||||||
|
error.clear();
|
||||||
|
device = CreateVideoIODevice(static_cast<VideoIOBackendId>(999), error);
|
||||||
|
Expect(device == nullptr, "unknown backend id is rejected");
|
||||||
|
Expect(!error.empty(), "unknown backend reports an error");
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " VideoIO backend factory test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "VideoIO backend factory tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -19,20 +19,26 @@ void Expect(bool condition, const char* message)
|
|||||||
class FakeVideoIODevice : public VideoIODevice
|
class FakeVideoIODevice : public VideoIODevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; }
|
||||||
void ReleaseResources() override {}
|
void ReleaseResources() override {}
|
||||||
|
|
||||||
bool DiscoverDevicesAndModes(const VideoFormatSelection&, std::string&) override
|
bool DiscoverDevicesAndModes(const VideoIOConfiguration&, std::string&) override
|
||||||
{
|
{
|
||||||
|
mState.backendId = BackendId();
|
||||||
mState.inputFrameSize = { 1920, 1080 };
|
mState.inputFrameSize = { 1920, 1080 };
|
||||||
mState.outputFrameSize = { 1920, 1080 };
|
mState.outputFrameSize = { 1920, 1080 };
|
||||||
mState.inputDisplayModeName = "fake 1080p";
|
mState.inputDisplayModeName = "fake 1080p";
|
||||||
mState.outputModelName = "Fake Video IO";
|
mState.outputDisplayModeName = "fake 1080p";
|
||||||
|
mState.deviceName = "Fake Video IO";
|
||||||
|
mState.capabilities.supportsInternalKeying = true;
|
||||||
|
mState.capabilities.supportsExternalKeying = true;
|
||||||
mState.hasInputDevice = true;
|
mState.hasInputDevice = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelectPreferredFormats(const VideoFormatSelection&, bool, std::string&) override
|
bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string&) override
|
||||||
{
|
{
|
||||||
|
mState.externalKeyingRequested = config.externalKeyingEnabled;
|
||||||
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8;
|
||||||
mState.outputPixelFormat = VideoIOPixelFormat::Bgra8;
|
mState.outputPixelFormat = VideoIOPixelFormat::Bgra8;
|
||||||
mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width);
|
mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width);
|
||||||
@@ -42,13 +48,13 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigureInput(InputFrameCallback callback, const VideoFormat&, std::string&) override
|
bool ConfigureInput(InputFrameCallback callback, std::string&) override
|
||||||
{
|
{
|
||||||
mInputCallback = callback;
|
mInputCallback = callback;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat&, bool, std::string&) override
|
bool ConfigureOutput(OutputFrameCallback callback, std::string&) override
|
||||||
{
|
{
|
||||||
mOutputCallback = callback;
|
mOutputCallback = callback;
|
||||||
return true;
|
return true;
|
||||||
@@ -114,19 +120,19 @@ private:
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
FakeVideoIODevice device;
|
FakeVideoIODevice device;
|
||||||
VideoFormatSelection selection;
|
VideoIOConfiguration config;
|
||||||
std::string error;
|
std::string error;
|
||||||
bool inputSeen = false;
|
bool inputSeen = false;
|
||||||
bool outputSeen = false;
|
bool outputSeen = false;
|
||||||
|
|
||||||
Expect(device.DiscoverDevicesAndModes(selection, error), "fake discovery succeeds");
|
Expect(device.DiscoverDevicesAndModes(config, error), "fake discovery succeeds");
|
||||||
Expect(device.SelectPreferredFormats(selection, false, error), "fake format selection succeeds");
|
Expect(device.SelectPreferredFormats(config, error), "fake format selection succeeds");
|
||||||
Expect(device.ConfigureInput([&](const VideoIOFrame& frame) {
|
Expect(device.ConfigureInput([&](const VideoIOFrame& frame) {
|
||||||
inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8;
|
inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8;
|
||||||
}, selection.input, error), "fake input config succeeds");
|
}, error), "fake input config succeeds");
|
||||||
Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) {
|
Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) {
|
||||||
outputSeen = completion.result == VideoIOCompletionResult::Completed;
|
outputSeen = completion.result == VideoIOCompletionResult::Completed;
|
||||||
}, selection.output, false, error), "fake output config succeeds");
|
}, error), "fake output config succeeds");
|
||||||
Expect(device.Start(), "fake device starts");
|
Expect(device.Start(), "fake device starts");
|
||||||
|
|
||||||
VideoIOOutputFrame outputFrame;
|
VideoIOOutputFrame outputFrame;
|
||||||
|
|||||||
Reference in New Issue
Block a user