diff --git a/nodes/testpattern/main.cpp b/nodes/testpattern/main.cpp index 5752a5d..954620d 100644 --- a/nodes/testpattern/main.cpp +++ b/nodes/testpattern/main.cpp @@ -30,9 +30,11 @@ class TestPatternNode : public dmf::NodeBase { return; } - const uint32_t stride = cfg_info.discrete.sliceSizes[0]; - log("stride=%u B/line grain=%u B ring=%u grains", - stride, stride * static_cast(height), cfg_info.discrete.grainCount); + const uint32_t stride = cfg_info.discrete.sliceSizes[0]; + const auto pattern = config().value("pattern", "bars"); + log("stride=%u B/line grain=%u B ring=%u grains pattern=%s", + stride, stride * static_cast(height), cfg_info.discrete.grainCount, + pattern.c_str()); const mxlRational rate = {fps_num, fps_den}; uint64_t index = mxlGetCurrentIndex(&rate); @@ -49,7 +51,11 @@ class TestPatternNode : public dmf::NodeBase { continue; } - dmf::v210::fill_frame(buf, width, height, stride); + if (pattern == "bars") dmf::v210::fill_colorbars(buf, width, height, stride); + else if (pattern == "ire") dmf::v210::fill_ire_ramp (buf, width, height, stride); + else if (pattern == "black") dmf::v210::fill_black (buf, width, height, stride); + else if (pattern == "white") dmf::v210::fill_white (buf, width, height, stride); + else dmf::v210::fill_colorbars (buf, width, height, stride); grain.flags = 0; grain.validSlices = grain.totalSlices; // mark grain complete so readers can consume it mxlFlowWriterCommitGrain(writer, &grain); diff --git a/shared/V210.hpp b/shared/V210.hpp index e3e9fd4..4dc48c5 100644 --- a/shared/V210.hpp +++ b/shared/V210.hpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace dmf::v210 { @@ -82,12 +83,67 @@ inline void write_bar_line(uint8_t* line, int width, uint32_t /*stride*/) } } -// Fill an entire frame buffer with color bars. +// Write one horizontal line of an arbitrary bar palette. +template +inline void write_palette_line(uint8_t* line, int width, const std::array& palette) +{ + const int n = static_cast(N); + const int blocks = width / 6; + for (int b = 0; b < blocks; b++) { + int x = b * 6; + auto color = [&](int px) -> const Color& { + return palette[static_cast(px * n / width)]; + }; + const Color& c01 = color(x); + const Color& c23 = color(x + 2); + const Color& c45 = color(x + 4); + pack_block(line + b * 16, + c01, c01.y, color(x+1).y, + c23, c23.y, color(x+3).y, + c45, c45.y, color(x+5).y); + } +} + +// Fill a frame with a solid color. +inline void fill_solid(uint8_t* buf, int width, int height, uint32_t stride, Color c) +{ + const int blocks = width / 6; + for (int b = 0; b < blocks; b++) + pack_block(buf + b * 16, c, c.y, c.y, c, c.y, c.y, c, c.y, c.y); + for (int y = 1; y < height; y++) + std::memcpy(buf + static_cast(y) * stride, buf, blocks * 16); +} + +// SMPTE 75% color bars. +inline void fill_colorbars(uint8_t* buf, int width, int height, uint32_t stride) +{ + for (int y = 0; y < height; y++) + write_palette_line(buf + static_cast(y) * stride, width, SMPTE_BARS); +} + +// IRE 11-step greyscale ramp. +inline void fill_ire_ramp(uint8_t* buf, int width, int height, uint32_t stride) +{ + for (int y = 0; y < height; y++) + write_palette_line(buf + static_cast(y) * stride, width, IRE_BARS); +} + +// 10-bit limited black (Y=64, Cb=Cr=512). +inline void fill_black(uint8_t* buf, int width, int height, uint32_t stride) +{ + fill_solid(buf, width, height, stride, {64, 512, 512}); +} + +// 10-bit limited white (Y=940, Cb=Cr=512). +inline void fill_white(uint8_t* buf, int width, int height, uint32_t stride) +{ + fill_solid(buf, width, height, stride, {940, 512, 512}); +} + +// Kept for backward compatibility. inline void fill_frame(uint8_t* buf, int width, int height, uint32_t stride) { - for (int y = 0; y < height; y++) { - write_bar_line(buf + static_cast(y) * stride, width, stride); - } + fill_colorbars(buf, width, height, stride); } inline void UYVYtoV210(uint8_t* src_buf, uint8_t* dst_buf, int width, int height, uint32_t src_stride, uint32_t dst_stride) diff --git a/studio-manager/main.cpp b/studio-manager/main.cpp index 43cc912..582ff64 100644 --- a/studio-manager/main.cpp +++ b/studio-manager/main.cpp @@ -41,9 +41,10 @@ static dmf::FlowGraph build_graph() { const std::string video_flow = gen_uuid(); const std::string audio_flow = gen_uuid(); g.nodes = { - { "ndiin", "ndiin", {} }, - { "fakesink", "fakesink", {} }, - { "ndiout", "ndiout", {} }, + { "testpattern", "testpattern", {{"pattern", "bars"}} }, + { "ndiin", "ndiin", {} }, + { "fakesink", "fakesink", {} }, + { "ndiout", "ndiout", {} }, }; const nlohmann::json video_fmt = { {"kind","video"}, {"width",1920}, {"height",1080}, {"fps_num",25}, {"fps_den",1}