UYVY backend
This commit is contained in:
@@ -22,7 +22,27 @@ std::string ResolveSenderName(const std::string& configuredName)
|
||||
return configuredName;
|
||||
}
|
||||
|
||||
constexpr std::size_t kBgraBytesPerPixel = 4;
|
||||
bool IsSupportedNdiOutputPixelFormat(VideoIOPixelFormat pixelFormat)
|
||||
{
|
||||
return pixelFormat == VideoIOPixelFormat::Bgra8 || pixelFormat == VideoIOPixelFormat::Uyvy8;
|
||||
}
|
||||
|
||||
bool NdiFourCcForPixelFormat(VideoIOPixelFormat pixelFormat, NDIlib_FourCC_video_type_e& fourCc)
|
||||
{
|
||||
switch (pixelFormat)
|
||||
{
|
||||
case VideoIOPixelFormat::Bgra8:
|
||||
fourCc = NDIlib_FourCC_video_type_BGRA;
|
||||
return true;
|
||||
case VideoIOPixelFormat::Uyvy8:
|
||||
fourCc = NDIlib_FourCC_video_type_UYVY;
|
||||
return true;
|
||||
case VideoIOPixelFormat::V210:
|
||||
case VideoIOPixelFormat::Yuva10:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetCommonVideoFrameFields(
|
||||
NDIlib_video_frame_v2_t& ndiFrame,
|
||||
@@ -30,12 +50,13 @@ void SetCommonVideoFrameFields(
|
||||
unsigned int height,
|
||||
int rowBytes,
|
||||
void* pixels,
|
||||
NDIlib_FourCC_video_type_e fourCc,
|
||||
const VideoOutputEdgeConfig& config)
|
||||
{
|
||||
std::memset(&ndiFrame, 0, sizeof(ndiFrame));
|
||||
ndiFrame.xres = static_cast<int>(width);
|
||||
ndiFrame.yres = static_cast<int>(height);
|
||||
ndiFrame.FourCC = NDIlib_FourCC_video_type_BGRA;
|
||||
ndiFrame.FourCC = fourCc;
|
||||
ndiFrame.frame_rate_N = 60000;
|
||||
ndiFrame.frame_rate_D = 1001;
|
||||
if (config.outputVideoMode.frameRate == "60")
|
||||
@@ -80,15 +101,14 @@ void SetCommonVideoFrameFields(
|
||||
ndiFrame.line_stride_in_bytes = rowBytes;
|
||||
}
|
||||
|
||||
bool CopyBgra8FrameFlippedVertically(const VideoIOOutputFrame& frame, std::vector<unsigned char>& outputPixels, int& outputRowBytes)
|
||||
bool CopyFrameFlippedVertically(const VideoIOOutputFrame& frame, std::vector<unsigned char>& outputPixels, int& outputRowBytes)
|
||||
{
|
||||
if (frame.bytes == nullptr || frame.width == 0 || frame.height == 0 || frame.rowBytes <= 0)
|
||||
if (frame.bytes == nullptr || frame.width == 0 || frame.height == 0 || frame.rowBytes <= 0 || !IsSupportedNdiOutputPixelFormat(frame.pixelFormat))
|
||||
return false;
|
||||
|
||||
const std::size_t width = static_cast<std::size_t>(frame.width);
|
||||
const std::size_t height = static_cast<std::size_t>(frame.height);
|
||||
const std::size_t sourceRowBytes = static_cast<std::size_t>(frame.rowBytes);
|
||||
const std::size_t destinationRowBytes = width * kBgraBytesPerPixel;
|
||||
const std::size_t destinationRowBytes = VideoIORowBytes(frame.pixelFormat, frame.width);
|
||||
if (sourceRowBytes < destinationRowBytes)
|
||||
return false;
|
||||
|
||||
@@ -122,10 +142,15 @@ bool NdiOutput::Initialize(const VideoOutputEdgeConfig& config, CompletionCallba
|
||||
mConfig = config;
|
||||
mCompletionCallback = std::move(completionCallback);
|
||||
PopulateState(mState, config, mSenderName);
|
||||
if (!IsSupportedNdiOutputPixelFormat(config.systemFramePixelFormat))
|
||||
{
|
||||
error = std::string("NDI output does not support system frame format ") + VideoIOPixelFormatName(config.systemFramePixelFormat) + ".";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.outputAlphaRequired)
|
||||
{
|
||||
mState.statusMessage = "NDI output sends BGRA frames with alpha when present; output.keying.alphaRequired is a DeckLink-only requirement.";
|
||||
mState.statusMessage = "NDI output can carry BGRA alpha when configured; output.keying.alphaRequired is a DeckLink-only requirement.";
|
||||
}
|
||||
|
||||
if (!AcquireNdiRuntime())
|
||||
@@ -168,7 +193,8 @@ bool NdiOutput::StartScheduledPlayback(std::string& error)
|
||||
|
||||
bool NdiOutput::ScheduleFrame(const VideoIOOutputFrame& frame)
|
||||
{
|
||||
if (frame.bytes == nullptr || frame.pixelFormat != VideoIOPixelFormat::Bgra8)
|
||||
NDIlib_FourCC_video_type_e fourCc = NDIlib_FourCC_video_type_BGRA;
|
||||
if (frame.bytes == nullptr || !NdiFourCcForPixelFormat(frame.pixelFormat, fourCc))
|
||||
{
|
||||
++mScheduleFailures;
|
||||
return false;
|
||||
@@ -190,14 +216,14 @@ bool NdiOutput::ScheduleFrame(const VideoIOOutputFrame& frame)
|
||||
|
||||
std::vector<unsigned char>& stagingBuffer = mStagingBuffers[mNextStagingBuffer];
|
||||
int stagingRowBytes = 0;
|
||||
if (!CopyBgra8FrameFlippedVertically(frame, stagingBuffer, stagingRowBytes))
|
||||
if (!CopyFrameFlippedVertically(frame, stagingBuffer, stagingRowBytes))
|
||||
{
|
||||
++mScheduleFailures;
|
||||
return false;
|
||||
}
|
||||
|
||||
NDIlib_video_frame_v2_t ndiFrame;
|
||||
SetCommonVideoFrameFields(ndiFrame, frame.width, frame.height, stagingRowBytes, stagingBuffer.data(), mConfig);
|
||||
SetCommonVideoFrameFields(ndiFrame, frame.width, frame.height, stagingRowBytes, stagingBuffer.data(), fourCc, mConfig);
|
||||
|
||||
const auto scheduleStart = std::chrono::steady_clock::now();
|
||||
NDIlib_send_send_video_async_v2(static_cast<NDIlib_send_instance_t>(sender), &ndiFrame);
|
||||
@@ -303,14 +329,19 @@ void NdiOutput::CompleteBuffer(void* buffer, VideoIOCompletionResult result)
|
||||
|
||||
void NdiOutput::PopulateState(VideoIOState& state, const VideoOutputEdgeConfig& config, const std::string& senderName)
|
||||
{
|
||||
const VideoIOPixelFormat outputPixelFormat = IsSupportedNdiOutputPixelFormat(config.systemFramePixelFormat)
|
||||
? config.systemFramePixelFormat
|
||||
: VideoIOPixelFormat::Bgra8;
|
||||
state = VideoIOState();
|
||||
state.outputFrameSize = config.outputVideoMode.frameSize;
|
||||
state.inputFrameSize = state.outputFrameSize;
|
||||
state.outputPixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
state.inputPixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
state.outputPixelFormat = outputPixelFormat;
|
||||
state.inputPixelFormat = outputPixelFormat;
|
||||
state.outputFrameRowBytes = VideoIORowBytes(state.outputPixelFormat, state.outputFrameSize.width);
|
||||
state.inputFrameRowBytes = state.outputFrameRowBytes;
|
||||
state.outputPackTextureWidth = state.outputFrameSize.width;
|
||||
state.outputPackTextureWidth = outputPixelFormat == VideoIOPixelFormat::Uyvy8
|
||||
? state.outputFrameSize.width / 2u
|
||||
: state.outputFrameSize.width;
|
||||
state.captureTextureWidth = state.inputFrameSize.width;
|
||||
state.outputDisplayModeName = config.outputVideoMode.displayName;
|
||||
state.inputDisplayModeName = "No input - NDI output session";
|
||||
@@ -322,6 +353,6 @@ void NdiOutput::PopulateState(VideoIOState& state, const VideoOutputEdgeConfig&
|
||||
state.keyerInterfaceAvailable = false;
|
||||
state.externalKeyingActive = false;
|
||||
state.frameBudgetMilliseconds = config.outputVideoMode.frameDurationMilliseconds;
|
||||
state.formatStatusMessage = "NDI output format: BGRA8.";
|
||||
state.formatStatusMessage = std::string("NDI output format: ") + VideoIOPixelFormatName(outputPixelFormat) + ".";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user