diff --git a/src/video/ndi/NdiInput.cpp b/src/video/ndi/NdiInput.cpp index ae515fd..52e6075 100644 --- a/src/video/ndi/NdiInput.cpp +++ b/src/video/ndi/NdiInput.cpp @@ -1,11 +1,11 @@ #include "NdiInput.h" +#include "NdiInputFormat.h" #include "logging/Logger.h" #include "video/core/VideoIOFormat.h" #include -#include #include #include #include @@ -44,32 +44,6 @@ bool IsDefaultDeviceName(const std::string& device) return device.empty() || device == "default" || device == "auto"; } -std::string FourCcName(NDIlib_FourCC_video_type_e fourCc) -{ - char text[5] = {}; - const auto value = static_cast(fourCc); - text[0] = static_cast(value & 0xff); - text[1] = static_cast((value >> 8) & 0xff); - text[2] = static_cast((value >> 16) & 0xff); - text[3] = static_cast((value >> 24) & 0xff); - return text; -} - -bool MapNdiPixelFormat(NDIlib_FourCC_video_type_e fourCc, VideoIOPixelFormat& pixelFormat) -{ - switch (fourCc) - { - case NDIlib_FourCC_video_type_BGRA: - case NDIlib_FourCC_video_type_BGRX: - pixelFormat = VideoIOPixelFormat::Bgra8; - return true; - case NDIlib_FourCC_video_type_UYVY: - pixelFormat = VideoIOPixelFormat::Uyvy8; - return true; - default: - return false; - } -} } NdiInput::NdiInput(InputFrameMailbox& mailbox) : @@ -262,12 +236,12 @@ void NdiInput::HandleVideoFrame(void*, const void* frame) } VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8; - if (!MapNdiPixelFormat(videoFrame.FourCC, pixelFormat)) + if (!MapNdiFourCcToVideoIOPixelFormat(videoFrame.FourCC, pixelFormat)) { mUnsupportedFrames.fetch_add(1, std::memory_order_relaxed); bool expected = false; if (mLoggedUnsupportedFrame.compare_exchange_strong(expected, true, std::memory_order_relaxed)) - TryLog(LogLevel::Warning, "ndi-input", "Unsupported NDI input frame format " + FourCcName(videoFrame.FourCC) + "."); + TryLog(LogLevel::Warning, "ndi-input", std::string("Unsupported NDI input frame format ") + NdiFourCcName(videoFrame.FourCC) + "."); return; } diff --git a/src/video/ndi/NdiInputFormat.cpp b/src/video/ndi/NdiInputFormat.cpp new file mode 100644 index 0000000..2d1c43a --- /dev/null +++ b/src/video/ndi/NdiInputFormat.cpp @@ -0,0 +1,35 @@ +#include "NdiInputFormat.h" + +#include +#include + +namespace RenderCadenceCompositor +{ +const char* NdiFourCcName(NDIlib_FourCC_video_type_e fourCc) +{ + static thread_local std::array text = {}; + const auto value = static_cast(fourCc); + text[0] = static_cast(value & 0xff); + text[1] = static_cast((value >> 8) & 0xff); + text[2] = static_cast((value >> 16) & 0xff); + text[3] = static_cast((value >> 24) & 0xff); + text[4] = '\0'; + return text.data(); +} + +bool MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_e fourCc, VideoIOPixelFormat& pixelFormat) +{ + switch (fourCc) + { + case NDIlib_FourCC_video_type_BGRA: + case NDIlib_FourCC_video_type_BGRX: + pixelFormat = VideoIOPixelFormat::Bgra8; + return true; + case NDIlib_FourCC_video_type_UYVY: + pixelFormat = VideoIOPixelFormat::Uyvy8; + return true; + default: + return false; + } +} +} diff --git a/src/video/ndi/NdiInputFormat.h b/src/video/ndi/NdiInputFormat.h new file mode 100644 index 0000000..b2bd5a5 --- /dev/null +++ b/src/video/ndi/NdiInputFormat.h @@ -0,0 +1,11 @@ +#pragma once + +#include "video/core/VideoIOFormat.h" + +#include + +namespace RenderCadenceCompositor +{ +const char* NdiFourCcName(NDIlib_FourCC_video_type_e fourCc); +bool MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_e fourCc, VideoIOPixelFormat& pixelFormat); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 133379f..74497e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -136,6 +136,13 @@ add_video_shader_test(VideoIOFormatTests "${TEST_DIR}/VideoIOFormatTests.cpp" ) +add_video_shader_test(NdiInputFormatTests + "${SRC_DIR}/video/ndi/NdiInputFormat.cpp" + ${VIDEO_FORMAT_SOURCES} + "${TEST_DIR}/NdiInputFormatTests.cpp" +) +target_include_directories(NdiInputFormatTests PRIVATE "${NDI_INCLUDE_DIR}") + add_video_shader_test(VideoPlayoutSchedulerTests "${SRC_DIR}/video/playout/VideoPlayoutScheduler.cpp" "${TEST_DIR}/VideoPlayoutSchedulerTests.cpp" diff --git a/tests/NdiInputFormatTests.cpp b/tests/NdiInputFormatTests.cpp new file mode 100644 index 0000000..c4682ab --- /dev/null +++ b/tests/NdiInputFormatTests.cpp @@ -0,0 +1,62 @@ +#include "video/ndi/NdiInputFormat.h" + +#include +#include + +namespace +{ +int gFailures = 0; + +void Expect(bool condition, const std::string& message) +{ + if (condition) + return; + + ++gFailures; + std::cerr << "FAILED: " << message << "\n"; +} + +void TestNdiFourCcMapping() +{ + using namespace RenderCadenceCompositor; + + VideoIOPixelFormat format = VideoIOPixelFormat::Uyvy8; + Expect(MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_BGRA, format), "BGRA maps successfully"); + Expect(format == VideoIOPixelFormat::Bgra8, "BGRA maps to Bgra8"); + + format = VideoIOPixelFormat::Uyvy8; + Expect(MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_BGRX, format), "BGRX maps successfully"); + Expect(format == VideoIOPixelFormat::Bgra8, "BGRX maps to Bgra8"); + + format = VideoIOPixelFormat::Bgra8; + Expect(MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_UYVY, format), "UYVY maps successfully"); + Expect(format == VideoIOPixelFormat::Uyvy8, "UYVY maps to Uyvy8"); + + format = VideoIOPixelFormat::Bgra8; + Expect(!MapNdiFourCcToVideoIOPixelFormat(NDIlib_FourCC_video_type_RGBA, format), "RGBA is unsupported until conversion is implemented"); + Expect(format == VideoIOPixelFormat::Bgra8, "unsupported mapping leaves output untouched"); +} + +void TestNdiFourCcNames() +{ + using namespace RenderCadenceCompositor; + + Expect(std::string(NdiFourCcName(NDIlib_FourCC_video_type_BGRA)) == "BGRA", "BGRA name is readable"); + Expect(std::string(NdiFourCcName(NDIlib_FourCC_video_type_UYVY)) == "UYVY", "UYVY name is readable"); +} +} + +int main() +{ + TestNdiFourCcMapping(); + TestNdiFourCcNames(); + + if (gFailures != 0) + { + std::cerr << gFailures << " NdiInputFormat test failure(s).\n"; + return 1; + } + + std::cout << "NdiInputFormat tests passed.\n"; + return 0; +}