Input GPU decoding
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 3m4s
CI / Windows Release Package (push) Has been skipped

This commit is contained in:
Aiden
2026-05-12 20:26:03 +10:00
parent ce28904891
commit fd4b70ec9c
6 changed files with 278 additions and 66 deletions

View File

@@ -3,7 +3,6 @@
#include "DeckLinkVideoIOFormat.h"
#include "../logging/Logger.h"
#include <algorithm>
#include <chrono>
#include <new>
@@ -24,28 +23,6 @@ bool FindInputDisplayMode(IDeckLinkInput* input, BMDDisplayMode targetMode, IDec
return FindDeckLinkDisplayMode(iterator, targetMode, foundMode);
}
unsigned char ClampToByte(double value)
{
if (value <= 0.0)
return 0;
if (value >= 255.0)
return 255;
return static_cast<unsigned char>(value + 0.5);
}
void StoreRec709UyvyAsBgra(unsigned char yByte, unsigned char uByte, unsigned char vByte, unsigned char* destination)
{
const double y = (static_cast<double>(yByte) - 16.0) / 219.0;
const double cb = (static_cast<double>(uByte) - 16.0) / 224.0 - 0.5;
const double cr = (static_cast<double>(vByte) - 16.0) / 224.0 - 0.5;
const double red = y + 1.5748 * cr;
const double green = y - 0.1873 * cb - 0.4681 * cr;
const double blue = y + 1.8556 * cb;
destination[0] = ClampToByte(blue * 255.0);
destination[1] = ClampToByte(green * 255.0);
destination[2] = ClampToByte(red * 255.0);
destination[3] = 255;
}
}
DeckLinkInputCallback::DeckLinkInputCallback(DeckLinkInput& owner) :
@@ -122,7 +99,7 @@ bool DeckLinkInput::Initialize(const DeckLinkInputConfig& config, std::string& e
}
Log(
"decklink-input",
std::string("DeckLink input enabled in ") + (mCapturePixelFormat == bmdFormat8BitBGRA ? "BGRA8" : "UYVY8-to-BGRA8 conversion") + " mode.");
std::string("DeckLink input enabled in ") + (mCapturePixelFormat == bmdFormat8BitBGRA ? "BGRA8" : "UYVY8 raw capture") + " mode.");
mCallback.Attach(new (std::nothrow) DeckLinkInputCallback(*this));
if (mCallback == nullptr)
@@ -197,6 +174,11 @@ DeckLinkInputMetrics DeckLinkInput::Metrics() const
return metrics;
}
VideoIOPixelFormat DeckLinkInput::CapturePixelFormat() const
{
return mCapturePixelFormat == bmdFormat8BitYUV ? VideoIOPixelFormat::Uyvy8 : VideoIOPixelFormat::Bgra8;
}
void DeckLinkInput::HandleFrameArrived(IDeckLinkVideoInputFrame* inputFrame)
{
if (inputFrame == nullptr)
@@ -263,7 +245,7 @@ void DeckLinkInput::HandleFrameArrived(IDeckLinkVideoInputFrame* inputFrame)
{
Log(
"decklink-input",
std::string("First DeckLink ") + (mCapturePixelFormat == bmdFormat8BitBGRA ? "BGRA8" : "UYVY8 converted BGRA8") + " input frame submitted to InputFrameMailbox.");
std::string("First DeckLink ") + (mCapturePixelFormat == bmdFormat8BitBGRA ? "BGRA8" : "UYVY8 raw") + " input frame submitted to InputFrameMailbox.");
}
inputFrameBuffer->EndAccess(bmdBufferAccessRead);
@@ -305,7 +287,7 @@ bool DeckLinkInput::DiscoverInput(const DeckLinkInputConfig& config, std::string
{
mInput = candidateInput;
mCapturePixelFormat = bmdFormat8BitYUV;
Log("decklink-input", "DeckLink input device selected for UYVY8 capture with CPU BGRA8 conversion.");
Log("decklink-input", "DeckLink input device selected for UYVY8 raw capture with render-thread GPU decode.");
return true;
}
}
@@ -360,34 +342,10 @@ bool DeckLinkInput::SubmitUyvy8Frame(IDeckLinkVideoInputFrame* inputFrame, const
if (width == 0 || height == 0 || sourceRowBytes < static_cast<long>(width * 2u))
return false;
const unsigned destinationRowBytes = VideoIORowBytes(VideoIOPixelFormat::Bgra8, width);
const auto convertStart = std::chrono::steady_clock::now();
mConversionBuffer.resize(static_cast<std::size_t>(destinationRowBytes) * static_cast<std::size_t>(height));
const unsigned char* sourceBytes = static_cast<const unsigned char*>(bytes);
for (unsigned y = 0; y < height; ++y)
{
const unsigned char* sourceRow = sourceBytes + static_cast<std::size_t>(y) * static_cast<std::size_t>(sourceRowBytes);
unsigned char* destinationRow = mConversionBuffer.data() + static_cast<std::size_t>(y) * static_cast<std::size_t>(destinationRowBytes);
for (unsigned x = 0; x < width; x += 2)
{
const unsigned pairOffset = x * 2u;
const unsigned char u = sourceRow[pairOffset + 0];
const unsigned char y0 = sourceRow[pairOffset + 1];
const unsigned char v = sourceRow[pairOffset + 2];
const unsigned char y1 = sourceRow[pairOffset + 3];
StoreRec709UyvyAsBgra(y0, u, v, destinationRow + static_cast<std::size_t>(x) * 4u);
if (x + 1u < width)
StoreRec709UyvyAsBgra(y1, u, v, destinationRow + static_cast<std::size_t>(x + 1u) * 4u);
}
}
const auto convertEnd = std::chrono::steady_clock::now();
mConvertMilliseconds.store(
std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(convertEnd - convertStart).count(),
std::memory_order_relaxed);
mConvertMilliseconds.store(0.0, std::memory_order_relaxed);
const uint64_t frameIndex = mCapturedFrames.load(std::memory_order_relaxed);
const auto submitStart = std::chrono::steady_clock::now();
const bool submitted = mMailbox.SubmitFrame(mConversionBuffer.data(), destinationRowBytes, frameIndex);
const bool submitted = mMailbox.SubmitFrame(bytes, static_cast<unsigned>(sourceRowBytes), frameIndex);
const auto submitEnd = std::chrono::steady_clock::now();
mSubmitMilliseconds.store(
std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(submitEnd - submitStart).count(),

View File

@@ -63,6 +63,7 @@ public:
bool IsInitialized() const { return mInput != nullptr; }
bool IsRunning() const { return mRunning.load(std::memory_order_acquire); }
VideoIOPixelFormat CapturePixelFormat() const;
DeckLinkInputMetrics Metrics() const;
void HandleFrameArrived(IDeckLinkVideoInputFrame* inputFrame);
@@ -80,7 +81,6 @@ private:
BMDPixelFormat mCapturePixelFormat = bmdFormat8BitBGRA;
CComPtr<IDeckLinkInput> mInput;
CComPtr<DeckLinkInputCallback> mCallback;
std::vector<unsigned char> mConversionBuffer;
std::atomic<bool> mRunning{ false };
std::atomic<uint64_t> mCapturedFrames{ 0 };
std::atomic<uint64_t> mNoInputSourceFrames{ 0 };