diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp index 1efc5db..d0f2514 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp @@ -83,6 +83,7 @@ bool OpenGLComposite::InitializeVideoIO() } const VideoIOConfiguration videoIOConfig = mRuntimeHost->GetVideoIOConfiguration(); + const VideoIOBackendDescriptor* backendDescriptor = GetVideoIOBackendDescriptor(videoIOConfig.backendId); mVideoIO = CreateVideoIODevice(videoIOConfig.backendId, initFailureReason); if (!mVideoIO) { @@ -93,8 +94,8 @@ bool OpenGLComposite::InitializeVideoIO() if (!mVideoIO->DiscoverDevicesAndModes(videoIOConfig, 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." + const char* title = IsVideoIOBackendUnavailableError(videoIOConfig.backendId, initFailureReason) && backendDescriptor != nullptr + ? backendDescriptor->unavailableTitle : "Video I/O initialization failed"; MessageBoxA(NULL, initFailureReason.c_str(), title, MB_OK | MB_ICONERROR); return false; diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp index 7ec5b60..a12a0a1 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.cpp @@ -3,14 +3,73 @@ #include "DeckLinkSession.h" #include "VideoIOTypes.h" +#include + +namespace +{ +struct VideoIOBackendRegistration +{ + VideoIOBackendDescriptor descriptor; + VideoIODeviceFactory factory; +}; + +const std::array& RegisteredBackends() +{ + static const std::array backends = { { + { + { + VideoIOBackendId::DeckLink, + "decklink", + "Blackmagic DeckLink", + "Please install the Blackmagic DeckLink drivers to use the features of this application.", + "This application requires the selected video I/O drivers installed." + }, + []() { return std::make_unique(); } + } + } }; + return backends; +} +} + +const VideoIOBackendDescriptor* GetVideoIOBackendDescriptor(VideoIOBackendId backendId) +{ + for (const VideoIOBackendRegistration& registration : RegisteredBackends()) + { + if (registration.descriptor.backendId == backendId) + return ®istration.descriptor; + } + return nullptr; +} + +std::vector ListVideoIOBackendDescriptors() +{ + std::vector descriptors; + descriptors.reserve(RegisteredBackends().size()); + for (const VideoIOBackendRegistration& registration : RegisteredBackends()) + descriptors.push_back(registration.descriptor); + return descriptors; +} + std::unique_ptr CreateVideoIODevice(VideoIOBackendId backendId, std::string& error) { - switch (backendId) + for (const VideoIOBackendRegistration& registration : RegisteredBackends()) { - case VideoIOBackendId::DeckLink: - return std::make_unique(); + if (registration.descriptor.backendId != backendId) + continue; + + error.clear(); + return registration.factory ? registration.factory() : nullptr; } error = "Unsupported video I/O backend."; return nullptr; } + +bool IsVideoIOBackendUnavailableError(VideoIOBackendId backendId, const std::string& error) +{ + const VideoIOBackendDescriptor* descriptor = GetVideoIOBackendDescriptor(backendId); + return descriptor != nullptr && + descriptor->unavailableMessage != nullptr && + *descriptor->unavailableMessage != '\0' && + error == descriptor->unavailableMessage; +} diff --git a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h index be41432..d85f8f3 100644 --- a/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h +++ b/apps/LoopThroughWithOpenGLCompositing/videoio/VideoIOBackendFactory.h @@ -2,9 +2,25 @@ #include "VideoIOConfig.h" +#include #include #include +#include class VideoIODevice; +struct VideoIOBackendDescriptor +{ + VideoIOBackendId backendId = VideoIOBackendId::DeckLink; + const char* backendName = "decklink"; + const char* displayName = "Blackmagic DeckLink"; + const char* unavailableMessage = ""; + const char* unavailableTitle = "Video I/O initialization failed"; +}; + +using VideoIODeviceFactory = std::function()>; + +const VideoIOBackendDescriptor* GetVideoIOBackendDescriptor(VideoIOBackendId backendId); +std::vector ListVideoIOBackendDescriptors(); std::unique_ptr CreateVideoIODevice(VideoIOBackendId backendId, std::string& error); +bool IsVideoIOBackendUnavailableError(VideoIOBackendId backendId, const std::string& error); diff --git a/tests/VideoIOBackendFactoryTests.cpp b/tests/VideoIOBackendFactoryTests.cpp index 3f4b6cd..f31800d 100644 --- a/tests/VideoIOBackendFactoryTests.cpp +++ b/tests/VideoIOBackendFactoryTests.cpp @@ -19,6 +19,18 @@ void Expect(bool condition, const char* message) int main() { + const std::vector backends = ListVideoIOBackendDescriptors(); + Expect(!backends.empty(), "backend registry is not empty"); + Expect(backends.front().backendId == VideoIOBackendId::DeckLink, "decklink is registered"); + Expect(std::string(backends.front().backendName) == "decklink", "decklink registry name is stable"); + + const VideoIOBackendDescriptor* descriptor = GetVideoIOBackendDescriptor(VideoIOBackendId::DeckLink); + Expect(descriptor != nullptr, "decklink descriptor can be looked up"); + Expect(descriptor != nullptr && std::string(descriptor->displayName) == "Blackmagic DeckLink", "decklink descriptor exposes display name"); + Expect(descriptor != nullptr && std::string(descriptor->unavailableTitle) == "This application requires the selected video I/O drivers installed.", "decklink descriptor exposes unavailable title"); + Expect(IsVideoIOBackendUnavailableError(VideoIOBackendId::DeckLink, "Please install the Blackmagic DeckLink drivers to use the features of this application."), "decklink unavailable message is recognized"); + Expect(!IsVideoIOBackendUnavailableError(VideoIOBackendId::DeckLink, "some other error"), "non-driver errors are not treated as backend unavailable"); + std::string error; std::unique_ptr device = CreateVideoIODevice(VideoIOBackendId::DeckLink, error); Expect(device != nullptr, "decklink backend factory returns a device");