diff --git a/CMakeLists.txt b/CMakeLists.txt index 47b15a9..e110aea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,10 +49,6 @@ set(APP_SOURCES "${APP_DIR}/videoio/decklink/DeckLinkSession.h" "${APP_DIR}/videoio/decklink/DeckLinkVideoIOFormat.cpp" "${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.h" "${APP_DIR}/gl/shader/GlobalParamsBuffer.cpp" @@ -208,35 +204,6 @@ endif() 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 "${CMAKE_CURRENT_SOURCE_DIR}/tests/Std140BufferTests.cpp" ) @@ -351,7 +318,6 @@ endif() add_test(NAME VideoPlayoutSchedulerTests COMMAND VideoPlayoutSchedulerTests) add_executable(VideoIODeviceFakeTests - "${APP_DIR}/videoio/VideoIOConfig.cpp" "${APP_DIR}/videoio/VideoIOFormat.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/tests/VideoIODeviceFakeTests.cpp" ) @@ -368,43 +334,6 @@ endif() 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 RUNTIME DESTINATION "." ) diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp index 16ec07c..5ce3e30 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp @@ -412,10 +412,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; } - // Setup OpenGL and video I/O capture/playout object + // Setup OpenGL and DeckLink capture and playout object pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC); - if (pOpenGLComposite->InitializeVideoIO()) + if (pOpenGLComposite->InitDeckLink()) { wglMakeCurrent( NULL, NULL ); if (pOpenGLComposite->Start()) @@ -423,11 +423,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostMessage(hWnd, kCreateStatusStripMessage, 0, 0); break; // success } - 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); + 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); } else { - 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); + MessageBoxA(NULL, "The OpenGL/DeckLink runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR); } // Failed to initialize - cleanup @@ -438,7 +438,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } catch (...) { - ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/video I/O runtime."); + ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/DeckLink runtime."); PostMessage(hWnd, WM_CLOSE, 0, 0); break; } @@ -474,7 +474,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } catch (...) { - ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/video I/O runtime."); + ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/DeckLink runtime."); } // Deselect the current rendering context and delete it diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp index 1efc5db..d46322b 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp @@ -1,3 +1,5 @@ +#include "DeckLinkDisplayMode.h" +#include "DeckLinkSession.h" #include "OpenGLComposite.h" #include "GLExtensions.h" #include "GlRenderConstants.h" @@ -8,7 +10,6 @@ #include "PngScreenshotWriter.h" #include "RuntimeServices.h" #include "ShaderBuildQueue.h" -#include "VideoIOBackendFactory.h" #include #include @@ -22,6 +23,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : hGLWnd(hWnd), hGLDC(hDC), hGLRC(hRC), + mVideoIO(std::make_unique()), mRenderer(std::make_unique()), mUseCommittedLayerStates(false), mScreenshotRequested(false) @@ -35,7 +37,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : [this]() { ProcessScreenshotRequest(); }, [this]() { paintGL(); }); mVideoIOBridge = std::make_unique( - nullptr, + *mVideoIO, *mRenderer, *mRenderPipeline, *mRuntimeHost, @@ -54,15 +56,20 @@ OpenGLComposite::~OpenGLComposite() mRuntimeServices->Stop(); if (mShaderBuildQueue) mShaderBuildQueue->Stop(); - if (mVideoIO) - mVideoIO->ReleaseResources(); + mVideoIO->ReleaseResources(); mRenderer->DestroyResources(); DeleteCriticalSection(&pMutex); } -bool OpenGLComposite::InitializeVideoIO() +bool OpenGLComposite::InitDeckLink() { + return InitVideoIO(); +} + +bool OpenGLComposite::InitVideoIO() +{ + VideoFormatSelection videoModes; std::string initFailureReason; if (mRuntimeHost && mRuntimeHost->GetRepoRoot().empty()) @@ -75,31 +82,31 @@ bool OpenGLComposite::InitializeVideoIO() } } - if (!mRuntimeHost) + if (mRuntimeHost) { - initFailureReason = "Runtime host is not available."; - MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR); - return false; + if (!ResolveConfiguredVideoFormats( + mRuntimeHost->GetInputVideoFormat(), + mRuntimeHost->GetInputFrameRate(), + mRuntimeHost->GetOutputVideoFormat(), + mRuntimeHost->GetOutputFrameRate(), + videoModes, + initFailureReason)) + { + MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink mode configuration error", MB_OK); + return false; + } } - 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)) + if (!mVideoIO->DiscoverDevicesAndModes(videoModes, initFailureReason)) { const char* title = initFailureReason == "Please install the Blackmagic DeckLink drivers to use the features of this application." - ? "This application requires the selected video I/O drivers installed." - : "Video I/O initialization failed"; + ? "This application requires the DeckLink drivers installed." + : "DeckLink initialization failed"; MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR); return false; } - if (!mVideoIO->SelectPreferredFormats(videoIOConfig, initFailureReason)) + const bool outputAlphaRequired = mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(); + if (!mVideoIO->SelectPreferredFormats(videoModes, outputAlphaRequired, initFailureReason)) goto error; if (! CheckOpenGLExtensions()) @@ -114,9 +121,9 @@ bool OpenGLComposite::InitializeVideoIO() goto error; } - PublishVideoIOStatus(mVideoIO->DeviceName().empty() - ? "Video I/O output device selected." - : ("Selected output device: " + mVideoIO->DeviceName())); + PublishVideoIOStatus(mVideoIO->OutputModelName().empty() + ? "DeckLink output device selected." + : ("Selected output device: " + mVideoIO->OutputModelName())); // Resize window to match output video frame, but scale large formats down by half for viewing. if (mVideoIO->OutputFrameWidth() < 1920) @@ -124,7 +131,7 @@ bool OpenGLComposite::InitializeVideoIO() else resizeWindow(mVideoIO->OutputFrameWidth() / 2, mVideoIO->OutputFrameHeight() / 2); - if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, initFailureReason)) + if (!mVideoIO->ConfigureInput([this](const VideoIOFrame& frame) { mVideoIOBridge->VideoFrameArrived(frame); }, videoModes.input, initFailureReason)) { goto error; } @@ -133,7 +140,7 @@ bool OpenGLComposite::InitializeVideoIO() mRuntimeHost->SetSignalStatus(false, mVideoIO->InputFrameWidth(), mVideoIO->InputFrameHeight(), mVideoIO->InputDisplayModeName()); } - if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, initFailureReason)) + if (!mVideoIO->ConfigureOutput([this](const VideoIOCompletion& completion) { mVideoIOBridge->PlayoutFrameCompleted(completion); }, videoModes.output, mRuntimeHost && mRuntimeHost->ExternalKeyingEnabled(), initFailureReason)) { goto error; } @@ -144,16 +151,13 @@ bool OpenGLComposite::InitializeVideoIO() error: if (!initFailureReason.empty()) - MessageBoxA(NULL, initFailureReason.c_str(), "Video I/O initialization failed", MB_OK | MB_ICONERROR); + MessageBoxA(NULL, initFailureReason.c_str(), "DeckLink initialization failed", MB_OK | MB_ICONERROR); mVideoIO->ReleaseResources(); return false; } void OpenGLComposite::paintGL() { - if (!mVideoIO) - return; - if (!TryEnterCriticalSection(&pMutex)) { ValidateRect(hGLWnd, NULL); @@ -183,13 +187,21 @@ void OpenGLComposite::resizeWindow(int width, int height) void OpenGLComposite::PublishVideoIOStatus(const std::string& statusMessage) { - if (!mRuntimeHost || !mVideoIO) + if (!mRuntimeHost) return; if (!statusMessage.empty()) mVideoIO->SetStatusMessage(statusMessage); - mRuntimeHost->SetVideoIOStatus(mVideoIO->State()); + mRuntimeHost->SetVideoIOStatus( + "decklink", + mVideoIO->OutputModelName(), + mVideoIO->SupportsInternalKeying(), + mVideoIO->SupportsExternalKeying(), + mVideoIO->KeyerInterfaceAvailable(), + mRuntimeHost->ExternalKeyingEnabled(), + mVideoIO->ExternalKeyingActive(), + mVideoIO->StatusMessage()); } bool OpenGLComposite::InitOpenGLState() diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h index a889591..7aa49e4 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h @@ -39,7 +39,8 @@ public: OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC); ~OpenGLComposite(); - bool InitializeVideoIO(); + bool InitDeckLink(); + bool InitVideoIO(); bool Start(); bool Stop(); bool ReloadShader(); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp index 904f399..658ecd8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.cpp @@ -7,7 +7,7 @@ #include OpenGLVideoIOBridge::OpenGLVideoIOBridge( - VideoIODevice* videoIO, + VideoIODevice& videoIO, OpenGLRenderer& renderer, OpenGLRenderPipeline& renderPipeline, RuntimeHost& runtimeHost, @@ -24,11 +24,6 @@ OpenGLVideoIOBridge::OpenGLVideoIOBridge( { } -void OpenGLVideoIOBridge::SetVideoIODevice(VideoIODevice* videoIO) -{ - mVideoIO = videoIO; -} - void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionResult) { const auto now = std::chrono::steady_clock::now(); @@ -62,10 +57,7 @@ void OpenGLVideoIOBridge::RecordFramePacing(VideoIOCompletionResult completionRe void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame) { - if (mVideoIO == nullptr) - return; - - const VideoIOState& state = mVideoIO->State(); + const VideoIOState& state = mVideoIO.State(); mRuntimeHost.TrySetSignalStatus(!inputFrame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName); if (inputFrame.hasNoInputSource || inputFrame.bytes == nullptr) @@ -99,20 +91,17 @@ void OpenGLVideoIOBridge::VideoFrameArrived(const VideoIOFrame& inputFrame) void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& completion) { - if (mVideoIO == nullptr) - return; - RecordFramePacing(completion.result); EnterCriticalSection(&mMutex); VideoIOOutputFrame outputFrame; - if (!mVideoIO->BeginOutputFrame(outputFrame)) + if (!mVideoIO.BeginOutputFrame(outputFrame)) { LeaveCriticalSection(&mMutex); return; } - const VideoIOState& state = mVideoIO->State(); + const VideoIOState& state = mVideoIO.State(); RenderPipelineFrameContext frameContext; frameContext.videoState = state; frameContext.completion = completion; @@ -122,12 +111,12 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet mRenderPipeline.RenderFrame(frameContext, outputFrame); - mVideoIO->EndOutputFrame(outputFrame); + mVideoIO.EndOutputFrame(outputFrame); - mVideoIO->AccountForCompletionResult(completion.result); + mVideoIO.AccountForCompletionResult(completion.result); // Schedule the next frame for playout - mVideoIO->ScheduleOutputFrame(outputFrame); + mVideoIO.ScheduleOutputFrame(outputFrame); wglMakeCurrent(NULL, NULL); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h index 47cef08..7cca4f8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/pipeline/OpenGLVideoIOBridge.h @@ -13,7 +13,7 @@ class OpenGLVideoIOBridge { public: OpenGLVideoIOBridge( - VideoIODevice* videoIO, + VideoIODevice& videoIO, OpenGLRenderer& renderer, OpenGLRenderPipeline& renderPipeline, RuntimeHost& runtimeHost, @@ -21,15 +21,13 @@ public: HDC hdc, HGLRC hglrc); - void SetVideoIODevice(VideoIODevice* videoIO); - void VideoFrameArrived(const VideoIOFrame& inputFrame); void PlayoutFrameCompleted(const VideoIOCompletion& completion); private: void RecordFramePacing(VideoIOCompletionResult completionResult); - VideoIODevice* mVideoIO; + VideoIODevice& mVideoIO; OpenGLRenderer& mRenderer; OpenGLRenderPipeline& mRenderPipeline; RuntimeHost& mRuntimeHost; diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp index 1ce10ba..56b850f 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp @@ -1228,20 +1228,25 @@ void RuntimeHost::MarkRenderStateDirtyLocked() mRenderStateVersion.fetch_add(1, std::memory_order_relaxed); } -void RuntimeHost::SetVideoIOStatus(const VideoIOState& state) +void RuntimeHost::SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying, + 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 lock(mMutex); - mVideoIOStatus.backendId = state.backendId; - mVideoIOStatus.deviceName = state.deviceName; - mVideoIOStatus.capabilities = state.capabilities; - mVideoIOStatus.hasInputDevice = state.hasInputDevice; - mVideoIOStatus.hasInputSource = state.hasInputSource; - mVideoIOStatus.inputDisplayModeName = state.inputDisplayModeName; - mVideoIOStatus.outputDisplayModeName = state.outputDisplayModeName; - mVideoIOStatus.externalKeyingRequested = state.externalKeyingRequested; - mVideoIOStatus.externalKeyingActive = state.externalKeyingActive; - mVideoIOStatus.statusMessage = state.statusMessage; - mVideoIOStatus.formatStatusMessage = state.formatStatusMessage; + mDeckLinkOutputStatus.backendName = backendName; + mDeckLinkOutputStatus.modelName = modelName; + mDeckLinkOutputStatus.supportsInternalKeying = supportsInternalKeying; + mDeckLinkOutputStatus.supportsExternalKeying = supportsExternalKeying; + mDeckLinkOutputStatus.keyerInterfaceAvailable = keyerInterfaceAvailable; + mDeckLinkOutputStatus.externalKeyingRequested = externalKeyingRequested; + mDeckLinkOutputStatus.externalKeyingActive = externalKeyingActive; + mDeckLinkOutputStatus.statusMessage = statusMessage; } void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds) @@ -1476,67 +1481,61 @@ bool RuntimeHost::LoadConfig(std::string& error) const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast(mConfig.maxTemporalHistoryFrames)); mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast(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")) - mConfig.videoIO.externalKeyingEnabled = enableExternalKeyingValue->asBoolean(mConfig.videoIO.externalKeyingEnabled); + mConfig.enableExternalKeying = enableExternalKeyingValue->asBoolean(mConfig.enableExternalKeying); if (const JsonValue* videoFormatValue = configJson.find("videoFormat")) { if (videoFormatValue->isString() && !videoFormatValue->asString().empty()) { - mConfig.videoIO.inputMode.videoFormat = videoFormatValue->asString(); - mConfig.videoIO.outputMode.videoFormat = videoFormatValue->asString(); + mConfig.inputVideoFormat = videoFormatValue->asString(); + mConfig.outputVideoFormat = videoFormatValue->asString(); } } if (const JsonValue* frameRateValue = configJson.find("frameRate")) { if (frameRateValue->isString() && !frameRateValue->asString().empty()) { - mConfig.videoIO.inputMode.frameRate = frameRateValue->asString(); - mConfig.videoIO.outputMode.frameRate = frameRateValue->asString(); + mConfig.inputFrameRate = frameRateValue->asString(); + mConfig.outputFrameRate = frameRateValue->asString(); } else if (frameRateValue->isNumber()) { std::ostringstream stream; stream << frameRateValue->asNumber(); - mConfig.videoIO.inputMode.frameRate = stream.str(); - mConfig.videoIO.outputMode.frameRate = stream.str(); + mConfig.inputFrameRate = stream.str(); + mConfig.outputFrameRate = stream.str(); } } if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat")) { if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty()) - mConfig.videoIO.inputMode.videoFormat = inputVideoFormatValue->asString(); + mConfig.inputVideoFormat = inputVideoFormatValue->asString(); } if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate")) { if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty()) - mConfig.videoIO.inputMode.frameRate = inputFrameRateValue->asString(); + mConfig.inputFrameRate = inputFrameRateValue->asString(); else if (inputFrameRateValue->isNumber()) { std::ostringstream stream; stream << inputFrameRateValue->asNumber(); - mConfig.videoIO.inputMode.frameRate = stream.str(); + mConfig.inputFrameRate = stream.str(); } } if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat")) { if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty()) - mConfig.videoIO.outputMode.videoFormat = outputVideoFormatValue->asString(); + mConfig.outputVideoFormat = outputVideoFormatValue->asString(); } if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate")) { if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty()) - mConfig.videoIO.outputMode.frameRate = outputFrameRateValue->asString(); + mConfig.outputFrameRate = outputFrameRateValue->asString(); else if (outputFrameRateValue->isNumber()) { std::ostringstream stream; stream << outputFrameRateValue->asNumber(); - mConfig.videoIO.outputMode.frameRate = stream.str(); + mConfig.outputFrameRate = stream.str(); } } @@ -1868,12 +1867,11 @@ JsonValue RuntimeHost::BuildStateValue() const app.set("oscPort", JsonValue(static_cast(mConfig.oscPort))); app.set("autoReload", JsonValue(mAutoReloadEnabled)); app.set("maxTemporalHistoryFrames", JsonValue(static_cast(mConfig.maxTemporalHistoryFrames))); - app.set("videoBackend", JsonValue(VideoIOBackendName(mConfig.videoIO.backendId))); - app.set("enableExternalKeying", JsonValue(mConfig.videoIO.externalKeyingEnabled)); - app.set("inputVideoFormat", JsonValue(mConfig.videoIO.inputMode.videoFormat)); - app.set("inputFrameRate", JsonValue(mConfig.videoIO.inputMode.frameRate)); - app.set("outputVideoFormat", JsonValue(mConfig.videoIO.outputMode.videoFormat)); - app.set("outputFrameRate", JsonValue(mConfig.videoIO.outputMode.frameRate)); + app.set("enableExternalKeying", JsonValue(mConfig.enableExternalKeying)); + app.set("inputVideoFormat", JsonValue(mConfig.inputVideoFormat)); + app.set("inputFrameRate", JsonValue(mConfig.inputFrameRate)); + app.set("outputVideoFormat", JsonValue(mConfig.outputVideoFormat)); + app.set("outputFrameRate", JsonValue(mConfig.outputFrameRate)); root.set("app", app); JsonValue runtime = JsonValue::MakeObject(); @@ -1889,22 +1887,25 @@ JsonValue RuntimeHost::BuildStateValue() const video.set("modeName", JsonValue(mSignalModeName)); 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(); - videoIO.set("backend", JsonValue(VideoIOBackendName(mVideoIOStatus.backendId))); - videoIO.set("deviceName", JsonValue(mVideoIOStatus.deviceName)); - videoIO.set("hasInputDevice", JsonValue(mVideoIOStatus.hasInputDevice)); - videoIO.set("hasInputSource", JsonValue(mVideoIOStatus.hasInputSource)); - videoIO.set("inputModeName", JsonValue(mVideoIOStatus.inputDisplayModeName)); - videoIO.set("outputModeName", JsonValue(mVideoIOStatus.outputDisplayModeName)); - JsonValue capabilities = JsonValue::MakeObject(); - 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)); + videoIO.set("backend", JsonValue(mDeckLinkOutputStatus.backendName)); + videoIO.set("modelName", JsonValue(mDeckLinkOutputStatus.modelName)); + videoIO.set("supportsInternalKeying", JsonValue(mDeckLinkOutputStatus.supportsInternalKeying)); + videoIO.set("supportsExternalKeying", JsonValue(mDeckLinkOutputStatus.supportsExternalKeying)); + videoIO.set("keyerInterfaceAvailable", JsonValue(mDeckLinkOutputStatus.keyerInterfaceAvailable)); + videoIO.set("externalKeyingRequested", JsonValue(mDeckLinkOutputStatus.externalKeyingRequested)); + videoIO.set("externalKeyingActive", JsonValue(mDeckLinkOutputStatus.externalKeyingActive)); + videoIO.set("statusMessage", JsonValue(mDeckLinkOutputStatus.statusMessage)); root.set("videoIO", videoIO); JsonValue performance = JsonValue::MakeObject(); diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h index 66b3b39..8c075d8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h @@ -2,7 +2,6 @@ #include "RuntimeJson.h" #include "ShaderTypes.h" -#include "VideoIOTypes.h" #include #include @@ -39,7 +38,10 @@ public: void SetCompileStatus(bool succeeded, const std::string& message); void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName); - void SetVideoIOStatus(const VideoIOState& state); + void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying, + 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); bool TrySetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds); void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds, @@ -63,8 +65,11 @@ public: unsigned short GetServerPort() const { return mServerPort; } unsigned short GetOscPort() const { return mConfig.oscPort; } unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; } - bool ExternalKeyingEnabled() const { return mConfig.videoIO.externalKeyingEnabled; } - VideoIOConfiguration GetVideoIOConfiguration() const { return mConfig.videoIO; } + bool ExternalKeyingEnabled() const { return mConfig.enableExternalKeying; } + const std::string& GetInputVideoFormat() const { return mConfig.inputVideoFormat; } + 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); bool AutoReloadEnabled() const { return mAutoReloadEnabled; } @@ -76,22 +81,23 @@ private: unsigned short oscPort = 9000; bool autoReload = true; unsigned maxTemporalHistoryFrames = 4; - VideoIOConfiguration videoIO; + bool enableExternalKeying = false; + std::string inputVideoFormat = "1080p"; + std::string inputFrameRate = "59.94"; + std::string outputVideoFormat = "1080p"; + std::string outputFrameRate = "59.94"; }; - struct VideoIOStatusSnapshot + struct DeckLinkOutputStatus { - VideoIOBackendId backendId = VideoIOBackendId::DeckLink; - std::string deviceName; - VideoIOCapabilities capabilities; - bool hasInputDevice = false; - bool hasInputSource = false; - std::string inputDisplayModeName = "1080p59.94"; - std::string outputDisplayModeName = "1080p59.94"; + std::string backendName = "decklink"; + std::string modelName; + bool supportsInternalKeying = false; + bool supportsExternalKeying = false; + bool keyerInterfaceAvailable = false; bool externalKeyingRequested = false; bool externalKeyingActive = false; std::string statusMessage; - std::string formatStatusMessage; }; struct LayerPersistentState @@ -170,7 +176,7 @@ private: uint64_t mLateFrameCount; uint64_t mDroppedFrameCount; uint64_t mFlushedFrameCount; - VideoIOStatusSnapshot mVideoIOStatus; + DeckLinkOutputStatus mDeckLinkOutputStatus; unsigned short mServerPort; bool mAutoReloadEnabled; std::chrono::steady_clock::time_point mStartTime; diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp deleted file mode 100644 index 7ec5b60..0000000 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "VideoIOBackendFactory.h" - -#include "DeckLinkSession.h" -#include "VideoIOTypes.h" - -std::unique_ptr CreateVideoIODevice(VideoIOBackendId backendId, std::string& error) -{ - switch (backendId) - { - case VideoIOBackendId::DeckLink: - return std::make_unique(); - } - - error = "Unsupported video I/O backend."; - return nullptr; -} diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h deleted file mode 100644 index be41432..0000000 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "VideoIOConfig.h" - -#include -#include - -class VideoIODevice; - -std::unique_ptr CreateVideoIODevice(VideoIOBackendId backendId, std::string& error); diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.cpp deleted file mode 100644 index 0e03338..0000000 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "VideoIOConfig.h" - -#include -#include - -namespace -{ -std::string NormalizeToken(std::string value) -{ - std::transform(value.begin(), value.end(), value.begin(), - [](unsigned char ch) { return static_cast(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; -} diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.h deleted file mode 100644 index aa45597..0000000 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOConfig.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - -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; -}; diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOTypes.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOTypes.h index b5cb65b..aa75fcf 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOTypes.h +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOTypes.h @@ -1,17 +1,15 @@ #pragma once -#include "VideoIOConfig.h" +#include "DeckLinkDisplayMode.h" #include "VideoIOFormat.h" #include #include #include -struct VideoIOCapabilities +enum class VideoIOBackend { - bool supportsInternalKeying = false; - bool supportsExternalKeying = false; - bool keyerInterfaceAvailable = false; + DeckLink }; enum class VideoIOCompletionResult @@ -23,9 +21,15 @@ enum class VideoIOCompletionResult Unknown }; +struct VideoIOConfig +{ + VideoFormatSelection videoModes; + bool externalKeyingEnabled = false; + bool preferTenBit = true; +}; + struct VideoIOState { - VideoIOBackendId backendId = VideoIOBackendId::DeckLink; FrameSize inputFrameSize; FrameSize outputFrameSize; VideoIOPixelFormat inputPixelFormat = VideoIOPixelFormat::Uyvy8; @@ -36,13 +40,14 @@ struct VideoIOState unsigned outputPackTextureWidth = 0; std::string inputDisplayModeName = "1080p59.94"; std::string outputDisplayModeName = "1080p59.94"; - std::string deviceName; + std::string outputModelName; std::string statusMessage; std::string formatStatusMessage; bool hasInputDevice = false; bool hasInputSource = false; - VideoIOCapabilities capabilities; - bool externalKeyingRequested = false; + bool supportsInternalKeying = false; + bool supportsExternalKeying = false; + bool keyerInterfaceAvailable = false; bool externalKeyingActive = false; double frameBudgetMilliseconds = 0.0; }; @@ -88,12 +93,11 @@ public: using OutputFrameCallback = std::function; virtual ~VideoIODevice() = default; - virtual VideoIOBackendId BackendId() const = 0; virtual void ReleaseResources() = 0; - virtual bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) = 0; - virtual bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) = 0; - virtual bool ConfigureInput(InputFrameCallback callback, std::string& error) = 0; - virtual bool ConfigureOutput(OutputFrameCallback callback, std::string& error) = 0; + virtual bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) = 0; + virtual bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) = 0; + virtual bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) = 0; + virtual bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) = 0; virtual bool Start() = 0; virtual bool Stop() = 0; virtual const VideoIOState& State() const = 0; @@ -122,11 +126,10 @@ public: unsigned OutputPackTextureWidth() const { return State().outputPackTextureWidth; } const std::string& FormatStatusMessage() const { return State().formatStatusMessage; } const std::string& InputDisplayModeName() const { return State().inputDisplayModeName; } - const std::string& DeviceName() const { return State().deviceName; } - bool SupportsInternalKeying() const { return State().capabilities.supportsInternalKeying; } - bool SupportsExternalKeying() const { return State().capabilities.supportsExternalKeying; } - bool KeyerInterfaceAvailable() const { return State().capabilities.keyerInterfaceAvailable; } - bool ExternalKeyingRequested() const { return State().externalKeyingRequested; } + const std::string& OutputModelName() const { return State().outputModelName; } + bool SupportsInternalKeying() const { return State().supportsInternalKeying; } + bool SupportsExternalKeying() const { return State().supportsExternalKeying; } + bool KeyerInterfaceAvailable() const { return State().keyerInterfaceAvailable; } bool ExternalKeyingActive() const { return State().externalKeyingActive; } const std::string& StatusMessage() const { return State().statusMessage; } double FrameBudgetMilliseconds() const { return State().frameBudgetMilliseconds; } diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.cpp index c2efeeb..85bf5ea 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.cpp @@ -13,10 +13,10 @@ std::string NormalizeModeToken(const std::string& value) return normalized; } -bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName) +bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName) { - DeckLinkVideoMode videoMode; - if (!ResolveConfiguredDeckLinkVideoMode(mode, videoMode)) + VideoFormat videoMode; + if (!ResolveConfiguredVideoFormat(videoFormat, frameRate, videoMode)) return false; displayMode = videoMode.displayMode; @@ -24,10 +24,10 @@ bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, return true; } -bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode) +bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode) { - const std::string formatToken = NormalizeModeToken(mode.videoFormat); - const std::string frameToken = NormalizeModeToken(mode.frameRate); + const std::string formatToken = NormalizeModeToken(videoFormat); + const std::string frameToken = NormalizeModeToken(frameRate); const std::string combinedToken = formatToken + frameToken; struct ModeOption @@ -98,22 +98,25 @@ bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, De return false; } -bool ResolveConfiguredDeckLinkVideoModes( - const VideoIOConfiguration& config, - DeckLinkVideoModeSelection& videoModes, +bool ResolveConfiguredVideoFormats( + const std::string& inputVideoFormat, + const std::string& inputFrameRate, + const std::string& outputVideoFormat, + const std::string& outputFrameRate, + VideoFormatSelection& videoModes, std::string& error) { - if (!ResolveConfiguredDeckLinkVideoMode(config.inputMode, videoModes.input)) + if (!ResolveConfiguredVideoFormat(inputVideoFormat, inputFrameRate, videoModes.input)) { - error = "Unsupported DeckLink input mode in config/runtime-host.json: " + - config.inputMode.videoFormat + " / " + config.inputMode.frameRate; + error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " + + inputVideoFormat + " / " + inputFrameRate; return false; } - if (!ResolveConfiguredDeckLinkVideoMode(config.outputMode, videoModes.output)) + if (!ResolveConfiguredVideoFormat(outputVideoFormat, outputFrameRate, videoModes.output)) { - error = "Unsupported DeckLink output mode in config/runtime-host.json: " + - config.outputMode.videoFormat + " / " + config.outputMode.frameRate; + error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " + + outputVideoFormat + " / " + outputFrameRate; return false; } diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.h b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.h index f65c13d..3be24fd 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.h +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkDisplayMode.h @@ -1,27 +1,47 @@ #pragma once #include "DeckLinkAPI_h.h" -#include "VideoIOConfig.h" #include -struct DeckLinkVideoMode +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 DeckLinkVideoModeSelection +struct VideoFormatSelection { - DeckLinkVideoMode input; - DeckLinkVideoMode output; + VideoFormat input; + VideoFormat output; }; std::string NormalizeModeToken(const std::string& value); -bool ResolveConfiguredDeckLinkDisplayMode(const VideoIOModeConfiguration& mode, BMDDisplayMode& displayMode, std::string& displayModeName); -bool ResolveConfiguredDeckLinkVideoMode(const VideoIOModeConfiguration& mode, DeckLinkVideoMode& videoMode); -bool ResolveConfiguredDeckLinkVideoModes( - const VideoIOConfiguration& config, - DeckLinkVideoModeSelection& videoModes, +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); diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp index 19d2d0b..4c8e4fc 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.cpp @@ -92,19 +92,14 @@ void DeckLinkSession::ReleaseResources() output.Release(); } -bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) +bool DeckLinkSession::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) { CComPtr deckLinkIterator; CComPtr inputMode; CComPtr outputMode; - mState.backendId = BackendId(); - mState.externalKeyingRequested = config.externalKeyingEnabled; - if (!ResolveConfiguredDeckLinkVideoModes(config, mConfiguredModes, error)) - return false; - - mState.inputDisplayModeName = mConfiguredModes.input.displayName; - mState.outputDisplayModeName = mConfiguredModes.output.displayName; + mState.inputDisplayModeName = videoModes.input.displayName; + mState.outputDisplayModeName = videoModes.output.displayName; HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast(&deckLinkIterator)); if (FAILED(result)) @@ -156,9 +151,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config output.Release(); else { - mState.deviceName = modelName; - mState.capabilities.supportsInternalKeying = deviceSupportsInternalKeying; - mState.capabilities.supportsExternalKeying = deviceSupportsExternalKeying; + mState.outputModelName = modelName; + mState.supportsInternalKeying = deviceSupportsInternalKeying; + mState.supportsExternalKeying = deviceSupportsExternalKeying; } } @@ -183,9 +178,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config return false; } - if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, mConfiguredModes.input.displayMode, &inputMode)) + if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, videoModes.input.displayMode, &inputMode)) { - error = "Cannot get specified input BMDDisplayMode for configured mode: " + mConfiguredModes.input.displayName; + error = "Cannot get specified input BMDDisplayMode for configured mode: " + videoModes.input.displayName; ReleaseResources(); return false; } @@ -199,9 +194,9 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config return false; } - if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, mConfiguredModes.output.displayMode, &outputMode)) + if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, videoModes.output.displayMode, &outputMode)) { - error = "Cannot get specified output BMDDisplayMode for configured mode: " + mConfiguredModes.output.displayName; + error = "Cannot get specified output BMDDisplayMode for configured mode: " + videoModes.output.displayName; ReleaseResources(); return false; } @@ -228,7 +223,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(const VideoIOConfiguration& config return true; } -bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) +bool DeckLinkSession::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) { if (!output) { @@ -238,19 +233,19 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config, mState.formatStatusMessage.clear(); - const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, mConfiguredModes.input.displayMode, bmdFormat10BitYUV); + const bool inputTenBitSupported = input != nullptr && InputSupportsFormat(input, videoModes.input.displayMode, bmdFormat10BitYUV); mState.inputPixelFormat = input != nullptr ? ChoosePreferredVideoIOFormat(inputTenBitSupported) : VideoIOPixelFormat::Uyvy8; if (input != nullptr && !inputTenBitSupported) mState.formatStatusMessage += "DeckLink input does not report 10-bit YUV support for the configured mode; using 8-bit capture. "; - const bool outputTenBitSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUV); - const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, mConfiguredModes.output.displayMode, bmdFormat10BitYUVA); - mState.outputPixelFormat = config.externalKeyingEnabled + const bool outputTenBitSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUV); + const bool outputTenBitYuvaSupported = OutputSupportsFormat(output, videoModes.output.displayMode, bmdFormat10BitYUVA); + mState.outputPixelFormat = outputAlphaRequired ? (outputTenBitYuvaSupported ? VideoIOPixelFormat::Yuva10 : VideoIOPixelFormat::Bgra8) : (outputTenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Bgra8); - if (config.externalKeyingEnabled && outputTenBitYuvaSupported) + if (outputAlphaRequired && outputTenBitYuvaSupported) mState.formatStatusMessage += "External keying requires alpha; using 10-bit YUVA output. "; - else if (config.externalKeyingEnabled) + else if (outputAlphaRequired) 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) mState.formatStatusMessage += "DeckLink output does not report 10-bit YUV support for the configured mode; using 8-bit BGRA output. "; @@ -291,7 +286,7 @@ bool DeckLinkSession::SelectPreferredFormats(const VideoIOConfiguration& config, return true; } -bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& error) +bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) { mInputFrameCallback = std::move(callback); @@ -303,7 +298,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& e } const BMDPixelFormat deckLinkInputPixelFormat = DeckLinkPixelFormatForVideoIO(mState.inputPixelFormat); - if (input->EnableVideoInput(mConfiguredModes.input.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK) + if (input->EnableVideoInput(inputVideoMode.displayMode, deckLinkInputPixelFormat, bmdVideoInputFlagDefault) != S_OK) { if (mState.inputPixelFormat == VideoIOPixelFormat::V210) { @@ -311,7 +306,7 @@ bool DeckLinkSession::ConfigureInput(InputFrameCallback callback, std::string& e mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8; mState.inputFrameRowBytes = mState.inputFrameSize.width * 2u; mState.captureTextureWidth = mState.inputFrameSize.width / 2u; - if (input->EnableVideoInput(mConfiguredModes.input.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK) + if (input->EnableVideoInput(inputVideoMode.displayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault) == S_OK) { std::ostringstream status; status << "DeckLink formats: capture " << VideoIOPixelFormatName(mState.inputPixelFormat) @@ -346,26 +341,26 @@ input_enabled: return true; } -bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, std::string& error) +bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) { mOutputFrameCallback = std::move(callback); - if (output->EnableVideoOutput(mConfiguredModes.output.displayMode, bmdVideoOutputFlagDefault) != S_OK) + if (output->EnableVideoOutput(outputVideoMode.displayMode, bmdVideoOutputFlagDefault) != S_OK) { error = "DeckLink output setup failed while enabling video output."; return false; } if (output->QueryInterface(IID_IDeckLinkKeyer, (void**)&keyer) == S_OK && keyer != NULL) - mState.capabilities.keyerInterfaceAvailable = true; + mState.keyerInterfaceAvailable = true; - if (mState.externalKeyingRequested) + if (externalKeyingEnabled) { - if (!mState.capabilities.supportsExternalKeying) + if (!mState.supportsExternalKeying) { mState.statusMessage = "External keying was requested, but the selected DeckLink output does not report external keying support."; } - else if (!mState.capabilities.keyerInterfaceAvailable) + else if (!mState.keyerInterfaceAvailable) { mState.statusMessage = "External keying was requested, but the selected DeckLink output does not expose the IDeckLinkKeyer interface."; } @@ -379,7 +374,7 @@ bool DeckLinkSession::ConfigureOutput(OutputFrameCallback callback, std::string& mState.statusMessage = "External keying is active on the selected DeckLink output."; } } - else if (mState.capabilities.supportsExternalKeying) + else if (mState.supportsExternalKeying) { mState.statusMessage = "Selected DeckLink output supports external keying. Set enableExternalKeying to true in runtime-host.json to request it."; } diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h index 02cb6a3..211914d 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/decklink/DeckLinkSession.h @@ -20,14 +20,41 @@ public: DeckLinkSession() = default; ~DeckLinkSession(); - VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; } void ReleaseResources() override; - bool DiscoverDevicesAndModes(const VideoIOConfiguration& config, std::string& error) override; - bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string& error) override; - bool ConfigureInput(InputFrameCallback callback, std::string& error) override; - bool ConfigureOutput(OutputFrameCallback callback, std::string& error) override; + bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error) override; + bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error) override; + bool ConfigureInput(InputFrameCallback callback, const VideoFormat& inputVideoMode, std::string& error) override; + bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error) override; bool Start() 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; } VideoIOState& MutableState() override { return mState; } double FrameBudgetMilliseconds() const; @@ -49,5 +76,4 @@ private: VideoPlayoutScheduler mScheduler; InputFrameCallback mInputFrameCallback; OutputFrameCallback mOutputFrameCallback; - DeckLinkVideoModeSelection mConfiguredModes; }; diff --git a/config/runtime-host.json b/config/runtime-host.json index f0587f1..5e3a55e 100644 --- a/config/runtime-host.json +++ b/config/runtime-host.json @@ -2,7 +2,6 @@ "shaderLibrary": "shaders", "serverPort": 8080, "oscPort": 9000, - "videoBackend": "decklink", "inputVideoFormat": "1080p", "inputFrameRate": "59.94", "outputVideoFormat": "1080p", diff --git a/tests/RuntimeHostVideoIOStateTests.cpp b/tests/RuntimeHostVideoIOStateTests.cpp deleted file mode 100644 index d078ec1..0000000 --- a/tests/RuntimeHostVideoIOStateTests.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "RuntimeHost.h" - -#include - -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; -} diff --git a/tests/VideoIOBackendFactoryTests.cpp b/tests/VideoIOBackendFactoryTests.cpp deleted file mode 100644 index 3f4b6cd..0000000 --- a/tests/VideoIOBackendFactoryTests.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "VideoIOBackendFactory.h" -#include "VideoIOTypes.h" - -#include - -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 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(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; -} diff --git a/tests/VideoIODeviceFakeTests.cpp b/tests/VideoIODeviceFakeTests.cpp index ee80a54..6dd3788 100644 --- a/tests/VideoIODeviceFakeTests.cpp +++ b/tests/VideoIODeviceFakeTests.cpp @@ -19,26 +19,20 @@ void Expect(bool condition, const char* message) class FakeVideoIODevice : public VideoIODevice { public: - VideoIOBackendId BackendId() const override { return VideoIOBackendId::DeckLink; } void ReleaseResources() override {} - bool DiscoverDevicesAndModes(const VideoIOConfiguration&, std::string&) override + bool DiscoverDevicesAndModes(const VideoFormatSelection&, std::string&) override { - mState.backendId = BackendId(); mState.inputFrameSize = { 1920, 1080 }; mState.outputFrameSize = { 1920, 1080 }; mState.inputDisplayModeName = "fake 1080p"; - mState.outputDisplayModeName = "fake 1080p"; - mState.deviceName = "Fake Video IO"; - mState.capabilities.supportsInternalKeying = true; - mState.capabilities.supportsExternalKeying = true; + mState.outputModelName = "Fake Video IO"; mState.hasInputDevice = true; return true; } - bool SelectPreferredFormats(const VideoIOConfiguration& config, std::string&) override + bool SelectPreferredFormats(const VideoFormatSelection&, bool, std::string&) override { - mState.externalKeyingRequested = config.externalKeyingEnabled; mState.inputPixelFormat = VideoIOPixelFormat::Uyvy8; mState.outputPixelFormat = VideoIOPixelFormat::Bgra8; mState.inputFrameRowBytes = VideoIORowBytes(mState.inputPixelFormat, mState.inputFrameSize.width); @@ -48,13 +42,13 @@ public: return true; } - bool ConfigureInput(InputFrameCallback callback, std::string&) override + bool ConfigureInput(InputFrameCallback callback, const VideoFormat&, std::string&) override { mInputCallback = callback; return true; } - bool ConfigureOutput(OutputFrameCallback callback, std::string&) override + bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat&, bool, std::string&) override { mOutputCallback = callback; return true; @@ -120,19 +114,19 @@ private: int main() { FakeVideoIODevice device; - VideoIOConfiguration config; + VideoFormatSelection selection; std::string error; bool inputSeen = false; bool outputSeen = false; - Expect(device.DiscoverDevicesAndModes(config, error), "fake discovery succeeds"); - Expect(device.SelectPreferredFormats(config, error), "fake format selection succeeds"); + Expect(device.DiscoverDevicesAndModes(selection, error), "fake discovery succeeds"); + Expect(device.SelectPreferredFormats(selection, false, error), "fake format selection succeeds"); Expect(device.ConfigureInput([&](const VideoIOFrame& frame) { inputSeen = frame.bytes != nullptr && frame.width == 1920 && frame.pixelFormat == VideoIOPixelFormat::Uyvy8; - }, error), "fake input config succeeds"); + }, selection.input, error), "fake input config succeeds"); Expect(device.ConfigureOutput([&](const VideoIOCompletion& completion) { outputSeen = completion.result == VideoIOCompletionResult::Completed; - }, error), "fake output config succeeds"); + }, selection.output, false, error), "fake output config succeeds"); Expect(device.Start(), "fake device starts"); VideoIOOutputFrame outputFrame;