feat: multiple test patterns in testpattern node
V210.hpp: add fill_colorbars, fill_ire_ramp, fill_black, fill_white.
Shared fill_solid helper stamps the first line across all rows via
memcpy. Generic write_palette_line template handles both SMPTE and
IRE bar palettes. fill_frame kept as a backward-compat alias.
testpattern/main.cpp: read "pattern" from node config and dispatch
to the appropriate fill function (bars/ire/black/white).
build_graph: pass {"pattern","bars"} to testpattern node params.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,9 +30,11 @@ class TestPatternNode : public dmf::NodeBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t stride = cfg_info.discrete.sliceSizes[0];
|
const uint32_t stride = cfg_info.discrete.sliceSizes[0];
|
||||||
log("stride=%u B/line grain=%u B ring=%u grains",
|
const auto pattern = config().value("pattern", "bars");
|
||||||
stride, stride * static_cast<uint32_t>(height), cfg_info.discrete.grainCount);
|
log("stride=%u B/line grain=%u B ring=%u grains pattern=%s",
|
||||||
|
stride, stride * static_cast<uint32_t>(height), cfg_info.discrete.grainCount,
|
||||||
|
pattern.c_str());
|
||||||
|
|
||||||
const mxlRational rate = {fps_num, fps_den};
|
const mxlRational rate = {fps_num, fps_den};
|
||||||
uint64_t index = mxlGetCurrentIndex(&rate);
|
uint64_t index = mxlGetCurrentIndex(&rate);
|
||||||
@@ -49,7 +51,11 @@ class TestPatternNode : public dmf::NodeBase {
|
|||||||
continue;
|
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.flags = 0;
|
||||||
grain.validSlices = grain.totalSlices; // mark grain complete so readers can consume it
|
grain.validSlices = grain.totalSlices; // mark grain complete so readers can consume it
|
||||||
mxlFlowWriterCommitGrain(writer, &grain);
|
mxlFlowWriterCommitGrain(writer, &grain);
|
||||||
|
|||||||
+60
-4
@@ -2,6 +2,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace dmf::v210 {
|
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<std::size_t N>
|
||||||
|
inline void write_palette_line(uint8_t* line, int width, const std::array<Color, N>& palette)
|
||||||
|
{
|
||||||
|
const int n = static_cast<int>(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<std::size_t>(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<ptrdiff_t>(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<ptrdiff_t>(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<ptrdiff_t>(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)
|
inline void fill_frame(uint8_t* buf, int width, int height, uint32_t stride)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height; y++) {
|
fill_colorbars(buf, width, height, stride);
|
||||||
write_bar_line(buf + static_cast<ptrdiff_t>(y) * stride, width, stride);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void UYVYtoV210(uint8_t* src_buf, uint8_t* dst_buf, int width, int height, uint32_t src_stride, uint32_t dst_stride)
|
inline void UYVYtoV210(uint8_t* src_buf, uint8_t* dst_buf, int width, int height, uint32_t src_stride, uint32_t dst_stride)
|
||||||
|
|||||||
@@ -41,9 +41,10 @@ static dmf::FlowGraph build_graph() {
|
|||||||
const std::string video_flow = gen_uuid();
|
const std::string video_flow = gen_uuid();
|
||||||
const std::string audio_flow = gen_uuid();
|
const std::string audio_flow = gen_uuid();
|
||||||
g.nodes = {
|
g.nodes = {
|
||||||
{ "ndiin", "ndiin", {} },
|
{ "testpattern", "testpattern", {{"pattern", "bars"}} },
|
||||||
{ "fakesink", "fakesink", {} },
|
{ "ndiin", "ndiin", {} },
|
||||||
{ "ndiout", "ndiout", {} },
|
{ "fakesink", "fakesink", {} },
|
||||||
|
{ "ndiout", "ndiout", {} },
|
||||||
};
|
};
|
||||||
const nlohmann::json video_fmt = {
|
const nlohmann::json video_fmt = {
|
||||||
{"kind","video"}, {"width",1920}, {"height",1080}, {"fps_num",25}, {"fps_den",1}
|
{"kind","video"}, {"width",1920}, {"height",1080}, {"fps_num",25}, {"fps_den",1}
|
||||||
|
|||||||
Reference in New Issue
Block a user