From 02a8a6436006ed950175571b0da36c426864d2e7 Mon Sep 17 00:00:00 2001 From: Aiden Date: Wed, 6 May 2026 11:41:27 +1000 Subject: [PATCH] com updates --- .gitea/workflows/ci.yml | 2 +- README.md | 25 +-- .../decklink/DeckLinkSession.cpp | 166 +++++++----------- .../decklink/DeckLinkSession.h | 15 +- 4 files changed, 81 insertions(+), 127 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 6e93355..038d7d7 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: ui-ubuntu: name: React UI Build - runs-on: nubuntu-latest + runs-on: ubuntu-latest steps: - name: Checkout diff --git a/README.md b/README.md index bbf01a6..64dfe08 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Current native test coverage includes: - JSON parsing and serialization. - Parameter normalization and preset filename safety. -- Shader manifest parsing and package registry scanning. +- Shader manifest parsing, temporal manifest validation, and package registry scanning. ## Runtime Configuration @@ -230,14 +230,15 @@ The Gitea workflow expects two act runners: If your Windows runner stores the Blackmagic SDK outside the repo, configure `GPUDIRECT_DIR` in the runner environment or adjust the workflow configure command to pass `-DGPUDIRECT_DIR=...`. -## Still todo -Audio -improve text rendering -genlock -find a better UI libary -Logs -refactor, cleanup of source files -display URL (Maybe clicakable) for control in the windows app (Not on the output) -Sound shader as seperate .slang in shader package? -runtime date time UTC and offset from PCs internal clock -![alt text](image.png) \ No newline at end of file +## Still Todo + +- Audio. +- Improve text rendering. +- Genlock. +- Find a better UI library. +- Logs. +- Continue source cleanup/refactoring. +- Display the control URL in the Windows app, ideally clickable, without rendering it on the video output. +- Support a separate sound shader `.slang` file in shader packages. +- Add runtime date/time uniforms using UTC and the PC's local offset. +![alt text](image.png) diff --git a/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.cpp b/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.cpp index d63eaa3..87b1a3a 100644 --- a/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.cpp @@ -13,7 +13,7 @@ namespace { std::string BstrToUtf8(BSTR value) { - if (value == NULL) + if (value == nullptr) return std::string(); const int requiredBytes = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL); @@ -36,84 +36,56 @@ DeckLinkSession::~DeckLinkSession() void DeckLinkSession::ReleaseResources() { if (input != nullptr) - { input->SetCallback(nullptr); - if (captureDelegate != nullptr) - { - captureDelegate->Release(); - captureDelegate = nullptr; - } - input->Release(); - input = nullptr; - } - - while (!outputVideoFrameQueue.empty()) - { - IDeckLinkMutableVideoFrame* frameToRelease = outputVideoFrameQueue.front(); - if (frameToRelease != nullptr) - frameToRelease->Release(); - outputVideoFrameQueue.pop_front(); - } + captureDelegate.Release(); + input.Release(); if (output != nullptr) - { - if (keyer != nullptr) - { - keyer->Disable(); - keyer->Release(); - keyer = nullptr; - externalKeyingActive = false; - } output->SetScheduledFrameCompletionCallback(nullptr); - if (playoutDelegate != nullptr) - { - playoutDelegate->Release(); - playoutDelegate = nullptr; - } - output->Release(); - output = nullptr; - } - if (playoutAllocator != nullptr) + if (keyer != nullptr) { - playoutAllocator->Release(); - playoutAllocator = nullptr; + keyer->Disable(); + externalKeyingActive = false; } + keyer.Release(); + + playoutDelegate.Release(); + outputVideoFrameQueue.clear(); + output.Release(); + + playoutAllocator.Release(); } bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, BMDDisplayMode outputDisplayMode, const std::string& requestedInputDisplayModeName, const std::string& requestedOutputDisplayModeName, std::string& error) { - bool success = false; - IDeckLinkIterator* deckLinkIterator = NULL; - IDeckLink* deckLink = NULL; - IDeckLinkProfileAttributes* deckLinkAttributes = NULL; - IDeckLinkDisplayModeIterator* inputDisplayModeIterator = NULL; - IDeckLinkDisplayModeIterator* outputDisplayModeIterator = NULL; - IDeckLinkDisplayMode* inputMode = NULL; - IDeckLinkDisplayMode* outputMode = NULL; + CComPtr deckLinkIterator; + CComPtr inputMode; + CComPtr outputMode; inputDisplayModeName = requestedInputDisplayModeName; outputDisplayModeName = requestedOutputDisplayModeName; - HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator); + HRESULT result = CoCreateInstance(CLSID_CDeckLinkIterator, nullptr, CLSCTX_ALL, IID_IDeckLinkIterator, reinterpret_cast(&deckLinkIterator)); if (FAILED(result)) { error = "Please install the Blackmagic DeckLink drivers to use the features of this application."; return false; } + CComPtr deckLink; while (deckLinkIterator->Next(&deckLink) == S_OK) { int64_t duplexMode; bool deviceSupportsInternalKeying = false; bool deviceSupportsExternalKeying = false; std::string modelName; + CComPtr deckLinkAttributes; if (deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&deckLinkAttributes) != S_OK) { printf("Could not obtain the IDeckLinkProfileAttributes interface\n"); - deckLink->Release(); - deckLink = NULL; + deckLink.Release(); continue; } @@ -124,20 +96,13 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B attributeFlag = FALSE; if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &attributeFlag) == S_OK) deviceSupportsExternalKeying = (attributeFlag != FALSE); - BSTR modelNameBstr = NULL; + CComBSTR modelNameBstr; if (deckLinkAttributes->GetString(BMDDeckLinkModelName, &modelNameBstr) == S_OK) - { modelName = BstrToUtf8(modelNameBstr); - if (modelNameBstr != NULL) - SysFreeString(modelNameBstr); - } - deckLinkAttributes->Release(); - deckLinkAttributes = NULL; if (result != S_OK || duplexMode == bmdDuplexInactive) { - deckLink->Release(); - deckLink = NULL; + deckLink.Release(); continue; } @@ -148,7 +113,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B if (!output && (!inputUsed || (duplexMode == bmdDuplexFull))) { if (deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&output) != S_OK) - output = NULL; + output.Release(); else { outputModelName = modelName; @@ -157,8 +122,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B } } - deckLink->Release(); - deckLink = NULL; + deckLink.Release(); if (output && input) break; @@ -167,39 +131,40 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B if (!output) { error = "Expected an Output DeckLink device"; - goto cleanup; + ReleaseResources(); + return false; } + CComPtr inputDisplayModeIterator; if (input && input->GetDisplayModeIterator(&inputDisplayModeIterator) != S_OK) { error = "Cannot get input Display Mode Iterator."; - goto cleanup; + ReleaseResources(); + return false; } if (input && !FindDeckLinkDisplayMode(inputDisplayModeIterator, inputDisplayMode, &inputMode)) { error = "Cannot get specified input BMDDisplayMode for configured mode: " + requestedInputDisplayModeName; - goto cleanup; - } - if (inputDisplayModeIterator) - { - inputDisplayModeIterator->Release(); - inputDisplayModeIterator = NULL; + ReleaseResources(); + return false; } + inputDisplayModeIterator.Release(); + CComPtr outputDisplayModeIterator; if (output->GetDisplayModeIterator(&outputDisplayModeIterator) != S_OK) { error = "Cannot get output Display Mode Iterator."; - goto cleanup; + ReleaseResources(); + return false; } if (!FindDeckLinkDisplayMode(outputDisplayModeIterator, outputDisplayMode, &outputMode)) { error = "Cannot get specified output BMDDisplayMode for configured mode: " + requestedOutputDisplayModeName; - goto cleanup; + ReleaseResources(); + return false; } - outputDisplayModeIterator->Release(); - outputDisplayModeIterator = NULL; outputFrameWidth = outputMode->GetWidth(); outputFrameHeight = outputMode->GetHeight(); @@ -209,26 +174,7 @@ bool DeckLinkSession::DiscoverDevicesAndModes(BMDDisplayMode inputDisplayMode, B inputDisplayModeName = "No input - black frame"; outputMode->GetFrameRate(&frameDuration, &frameTimescale); - success = true; - -cleanup: - if (!success) - ReleaseResources(); - if (deckLink != NULL) - deckLink->Release(); - if (deckLinkAttributes != NULL) - deckLinkAttributes->Release(); - if (inputMode != NULL) - inputMode->Release(); - if (outputMode != NULL) - outputMode->Release(); - if (inputDisplayModeIterator != NULL) - inputDisplayModeIterator->Release(); - if (outputDisplayModeIterator != NULL) - outputDisplayModeIterator->Release(); - if (deckLinkIterator != NULL) - deckLinkIterator->Release(); - return success; + return true; } bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglrc, BMDDisplayMode inputDisplayMode, std::string& error) @@ -245,14 +191,18 @@ bool DeckLinkSession::ConfigureInput(OpenGLComposite* owner, HDC hdc, HGLRC hglr if (input->EnableVideoInputWithAllocatorProvider(inputDisplayMode, bmdFormat8BitYUV, bmdVideoInputFlagDefault, captureAllocator) != S_OK) { OutputDebugStringA("DeckLink input could not be enabled; continuing in output-only black-frame mode.\n"); - input->Release(); - input = NULL; + input.Release(); hasNoInputSource = true; inputDisplayModeName = "No input - black frame"; return true; } - captureDelegate = new CaptureDelegate(owner); + captureDelegate.Attach(new (std::nothrow) CaptureDelegate(owner)); + if (captureDelegate == nullptr) + { + error = "DeckLink input setup failed while creating the capture callback."; + return false; + } if (input->SetCallback(captureDelegate) != S_OK) { error = "DeckLink input setup failed while installing the capture callback."; @@ -271,7 +221,12 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl return false; } - playoutAllocator = new PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameHeight); + playoutAllocator.Attach(new (std::nothrow) PinnedMemoryAllocator(hdc, hglrc, VideoFrameTransfer::GPUtoCPU, 1, outputFrameRowBytes * outputFrameHeight)); + if (playoutAllocator == nullptr) + { + error = "DeckLink output setup failed while creating the playout allocator."; + return false; + } if (output->EnableVideoOutput(outputDisplayMode, bmdVideoOutputFlagDefault) != S_OK) { @@ -309,8 +264,8 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl for (int i = 0; i < 10; i++) { - IDeckLinkMutableVideoFrame* outputFrame = NULL; - IDeckLinkVideoBuffer* outputFrameBuffer = NULL; + CComPtr outputFrame; + CComPtr outputFrameBuffer; if (playoutAllocator->AllocateVideoBuffer(&outputFrameBuffer) != S_OK) { @@ -321,15 +276,14 @@ bool DeckLinkSession::ConfigureOutput(OpenGLComposite* owner, HDC hdc, HGLRC hgl if (output->CreateVideoFrameWithBuffer(outputFrameWidth, outputFrameHeight, outputFrameRowBytes, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, outputFrameBuffer, &outputFrame) != S_OK) { error = "DeckLink output setup failed while creating an output video frame."; - outputFrameBuffer->Release(); return false; } outputVideoFrameQueue.push_back(outputFrame); } - playoutDelegate = new PlayoutDelegate(owner); - if (playoutDelegate == NULL) + playoutDelegate.Attach(new (std::nothrow) PlayoutDelegate(owner)); + if (playoutDelegate == nullptr) { error = "DeckLink output setup failed while creating the playout callback."; return false; @@ -353,10 +307,10 @@ double DeckLinkSession::FrameBudgetMilliseconds() const IDeckLinkMutableVideoFrame* DeckLinkSession::RotateOutputFrame() { - IDeckLinkMutableVideoFrame* outputVideoFrame = outputVideoFrameQueue.front(); + CComPtr outputVideoFrame = outputVideoFrameQueue.front(); outputVideoFrameQueue.push_back(outputVideoFrame); outputVideoFrameQueue.pop_front(); - return outputVideoFrame; + return outputVideoFrame.p; } bool DeckLinkSession::TransferPlayoutFrame(void* address, GLuint outputTexture) @@ -401,11 +355,11 @@ bool DeckLinkSession::Start() for (unsigned i = 0; i < kPrerollFrameCount; i++) { - IDeckLinkMutableVideoFrame* outputVideoFrame = outputVideoFrameQueue.front(); + CComPtr outputVideoFrame = outputVideoFrameQueue.front(); outputVideoFrameQueue.push_back(outputVideoFrame); outputVideoFrameQueue.pop_front(); - IDeckLinkVideoBuffer* outputVideoFrameBuffer; + CComPtr outputVideoFrameBuffer; if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK) { MessageBoxA(NULL, "Could not query the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR); @@ -414,7 +368,6 @@ bool DeckLinkSession::Start() if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK) { - outputVideoFrameBuffer->Release(); MessageBoxA(NULL, "Could not write to the preroll output frame buffer.", "DeckLink start failed", MB_OK | MB_ICONERROR); return false; } @@ -424,7 +377,6 @@ bool DeckLinkSession::Start() memset(pFrame, 0, outputVideoFrame->GetRowBytes() * outputFrameHeight); outputVideoFrameBuffer->EndAccess(bmdBufferAccessWrite); - outputVideoFrameBuffer->Release(); if (output->ScheduleVideoFrame(outputVideoFrame, (totalPlayoutFrames * frameDuration), frameDuration, frameTimescale) != S_OK) { diff --git a/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.h b/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.h index e640e33..1b90fb3 100644 --- a/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.h +++ b/apps/LoopThroughWithOpenGLCompositing/decklink/DeckLinkSession.h @@ -3,6 +3,7 @@ #include "DeckLinkAPI_h.h" #include "DeckLinkFrameTransfer.h" +#include #include #include @@ -45,13 +46,13 @@ public: bool ScheduleOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame); private: - CaptureDelegate* captureDelegate = nullptr; - PlayoutDelegate* playoutDelegate = nullptr; - IDeckLinkInput* input = nullptr; - IDeckLinkOutput* output = nullptr; - IDeckLinkKeyer* keyer = nullptr; - std::deque outputVideoFrameQueue; - PinnedMemoryAllocator* playoutAllocator = nullptr; + CComPtr captureDelegate; + CComPtr playoutDelegate; + CComPtr input; + CComPtr output; + CComPtr keyer; + std::deque> outputVideoFrameQueue; + CComPtr playoutAllocator; BMDTimeValue frameDuration = 0; BMDTimeScale frameTimescale = 0; unsigned totalPlayoutFrames = 0;