Improvement
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m52s
CI / Windows Release Package (push) Successful in 3m0s

This commit is contained in:
Aiden
2026-05-12 00:00:23 +10:00
parent a434a88108
commit 9e3412712c
22 changed files with 1409 additions and 34 deletions

View File

@@ -1,6 +1,7 @@
#include "DeckLinkSession.h"
#include <atlbase.h>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <new>
@@ -10,6 +11,75 @@
namespace
{
class SystemMemoryDeckLinkVideoBuffer : public IDeckLinkVideoBuffer
{
public:
SystemMemoryDeckLinkVideoBuffer(void* bytes, unsigned long long sizeBytes) :
mBytes(bytes),
mSizeBytes(sizeBytes),
mRefCount(1)
{
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv) override
{
if (ppv == nullptr)
return E_POINTER;
if (iid == IID_IUnknown || iid == IID_IDeckLinkVideoBuffer)
{
*ppv = static_cast<IDeckLinkVideoBuffer*>(this);
AddRef();
return S_OK;
}
*ppv = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
return ++mRefCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
const ULONG refCount = --mRefCount;
if (refCount == 0)
delete this;
return refCount;
}
HRESULT STDMETHODCALLTYPE GetBytes(void** buffer) override
{
if (buffer == nullptr)
return E_POINTER;
*buffer = mBytes;
return mBytes != nullptr ? S_OK : E_FAIL;
}
HRESULT STDMETHODCALLTYPE GetSize(unsigned long long* bufferSize) override
{
if (bufferSize == nullptr)
return E_POINTER;
*bufferSize = mSizeBytes;
return S_OK;
}
HRESULT STDMETHODCALLTYPE StartAccess(BMDBufferAccessFlags) override
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE EndAccess(BMDBufferAccessFlags) override
{
return S_OK;
}
private:
void* mBytes = nullptr;
unsigned long long mSizeBytes = 0;
std::atomic<ULONG> mRefCount;
};
std::string BstrToUtf8(BSTR value)
{
if (value == nullptr)
@@ -460,6 +530,48 @@ bool DeckLinkSession::ScheduleFrame(IDeckLinkMutableVideoFrame* outputVideoFrame
output->ScheduleVideoFrame(outputVideoFrame, scheduleTime.streamTime, scheduleTime.duration, scheduleTime.timeScale) == S_OK;
}
bool DeckLinkSession::ScheduleSystemMemoryFrame(const VideoIOOutputFrame& frame)
{
if (output == nullptr || frame.bytes == nullptr || frame.rowBytes <= 0 || frame.height == 0)
return false;
CComPtr<IDeckLinkVideoBuffer> videoBuffer;
videoBuffer.Attach(new (std::nothrow) SystemMemoryDeckLinkVideoBuffer(
frame.bytes,
static_cast<unsigned long long>(frame.rowBytes) * static_cast<unsigned long long>(frame.height)));
if (videoBuffer == nullptr)
return false;
CComPtr<IDeckLinkMutableVideoFrame> outputVideoFrame;
const BMDPixelFormat pixelFormat = DeckLinkPixelFormatForVideoIO(frame.pixelFormat);
if (output->CreateVideoFrameWithBuffer(
frame.width,
frame.height,
frame.rowBytes,
pixelFormat,
bmdFrameFlagFlipVertical,
videoBuffer,
&outputVideoFrame) != S_OK)
{
return false;
}
IDeckLinkVideoFrame* scheduledFrame = outputVideoFrame;
{
std::lock_guard<std::mutex> lock(mScheduledSystemFrameMutex);
mScheduledSystemFrameBuffers[scheduledFrame] = frame.bytes;
}
if (ScheduleFrame(outputVideoFrame))
return true;
{
std::lock_guard<std::mutex> lock(mScheduledSystemFrameMutex);
mScheduledSystemFrameBuffers.erase(scheduledFrame);
}
return false;
}
bool DeckLinkSession::ScheduleBlackFrame(IDeckLinkMutableVideoFrame* outputVideoFrame)
{
if (outputVideoFrame == nullptr)
@@ -505,6 +617,9 @@ VideoPlayoutRecoveryDecision DeckLinkSession::AccountForCompletionResult(VideoIO
bool DeckLinkSession::ScheduleOutputFrame(const VideoIOOutputFrame& frame)
{
if (frame.nativeFrame == nullptr)
return ScheduleSystemMemoryFrame(frame);
IDeckLinkMutableVideoFrame* outputVideoFrame = static_cast<IDeckLinkMutableVideoFrame*>(frame.nativeFrame);
const bool scheduled = ScheduleFrame(outputVideoFrame);
if (outputVideoFrame != nullptr)
@@ -621,13 +736,29 @@ void DeckLinkSession::HandleVideoInputFrame(IDeckLinkVideoInputFrame* inputFrame
void DeckLinkSession::HandlePlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult)
{
void* completedSystemBuffer = nullptr;
if (completedFrame != nullptr)
{
CComPtr<IDeckLinkMutableVideoFrame> reusableFrame;
if (completedFrame->QueryInterface(IID_IDeckLinkMutableVideoFrame, reinterpret_cast<void**>(&reusableFrame)) == S_OK &&
reusableFrame != nullptr)
bool externalSystemFrame = false;
{
outputVideoFrameQueue.push_back(reusableFrame);
std::lock_guard<std::mutex> lock(mScheduledSystemFrameMutex);
auto externalFrame = mScheduledSystemFrameBuffers.find(completedFrame);
if (externalFrame != mScheduledSystemFrameBuffers.end())
{
completedSystemBuffer = externalFrame->second;
mScheduledSystemFrameBuffers.erase(externalFrame);
externalSystemFrame = true;
}
}
if (!externalSystemFrame)
{
CComPtr<IDeckLinkMutableVideoFrame> reusableFrame;
if (completedFrame->QueryInterface(IID_IDeckLinkMutableVideoFrame, reinterpret_cast<void**>(&reusableFrame)) == S_OK &&
reusableFrame != nullptr)
{
outputVideoFrameQueue.push_back(reusableFrame);
}
}
}
@@ -636,6 +767,7 @@ void DeckLinkSession::HandlePlayoutFrameCompleted(IDeckLinkVideoFrame* completed
VideoIOCompletion completion;
completion.result = TranslateCompletionResult(completionResult);
completion.outputFrameBuffer = completedSystemBuffer;
mOutputFrameCallback(completion);
}