Skip to content

Commit f21438b

Browse files
committed
out_ndi: Scale output to 2x2 rendered
This lets us ignore the 4:2:2 subsampling as it's quite important in our usecase.
1 parent 96f4f20 commit f21438b

File tree

1 file changed

+27
-11
lines changed

1 file changed

+27
-11
lines changed

src/modules/out_ndi.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
#error Define MATRIX_Y as the matrix's Y size.
1919
#endif
2020

21-
#define FPS 60
21+
#define NDI_FPS 60
22+
#define NDI_SCALE_FACTOR 2 // Pixels per Axis
2223

2324
static RGB *primary_buffer;
2425
static RGB *front_buffer;
@@ -30,14 +31,29 @@ static NDIlib_send_instance_t ndi_send = NULL;
3031
static oscore_task ndi_task;
3132

3233
static void* send_task(void *arg);
33-
34+
35+
static void upscale_buffer(const RGB *src, int w, int h, int scale, RGB *dst) {
36+
int w2 = w * scale;
37+
for (int y = 0; y < h; y++) {
38+
for (int x = 0; x < w; x++) {
39+
RGB px = src[y*w + x];
40+
int base = (y*scale)*w2 + x*scale;
41+
for (int dy = 0; dy < scale; dy++) {
42+
for (int dx = 0; dx < scale; dx++) {
43+
dst[base + dy*w2 + dx] = px;
44+
}
45+
}
46+
}
47+
}
48+
}
49+
3450
int init(void) {
3551
assert(sizeof(RGB) == 4);
3652

3753
// Allocate memory for the buffers
3854
primary_buffer = (RGB*)malloc(MATRIX_X * MATRIX_Y * sizeof(RGB));
39-
front_buffer = (RGB*)malloc(MATRIX_X * MATRIX_Y * sizeof(RGB));
40-
back_buffer = (RGB*)malloc(MATRIX_X * MATRIX_Y * sizeof(RGB));
55+
front_buffer = (RGB*)malloc(MATRIX_X * MATRIX_Y * sizeof(RGB) * (2*NDI_SCALE_FACTOR));
56+
back_buffer = (RGB*)malloc(MATRIX_X * MATRIX_Y * sizeof(RGB) * (2*NDI_SCALE_FACTOR));
4157

4258
atomic_init(&keep_running, true);
4359
atomic_init(&front_or_back, true);
@@ -75,8 +91,8 @@ int render(void) {
7591
int current_fob = atomic_load(&front_or_back);
7692
RGB *current_buffer = current_fob ? front_buffer : back_buffer;
7793

78-
// Copy primary buffer to current buffer
79-
memcpy(current_buffer, primary_buffer, MATRIX_X * MATRIX_Y * sizeof(RGB));
94+
// Upscale primary buffer to current buffer
95+
upscale_buffer(primary_buffer, MATRIX_X, MATRIX_Y, NDI_SCALE_FACTOR, current_buffer);
8096

8197
// Flip buffers
8298
atomic_store(&front_or_back, !current_fob);
@@ -124,13 +140,13 @@ static void* send_task(void *arg) {
124140

125141
// Prepopulate frame with static info.
126142
NDIlib_video_frame_v2_t video_frame;
127-
video_frame.xres = MATRIX_X;
128-
video_frame.yres = MATRIX_Y;
129-
video_frame.line_stride_in_bytes = MATRIX_X * sizeof(RGB);
143+
video_frame.xres = MATRIX_X * NDI_SCALE_FACTOR;
144+
video_frame.yres = MATRIX_Y * NDI_SCALE_FACTOR;
145+
video_frame.line_stride_in_bytes = MATRIX_X * NDI_SCALE_FACTOR * sizeof(RGB);
130146
video_frame.picture_aspect_ratio = (float)MATRIX_X / (float)MATRIX_Y;
131147
video_frame.FourCC = NDIlib_FourCC_type_RGBX;
132148
video_frame.frame_format_type = NDIlib_frame_format_type_progressive;
133-
video_frame.frame_rate_N = FPS * 1000;
149+
video_frame.frame_rate_N = NDI_FPS * 1000;
134150
video_frame.frame_rate_D = 1000;
135151

136152
while (atomic_load(&keep_running)) {
@@ -142,7 +158,7 @@ static void* send_task(void *arg) {
142158
NDIlib_send_send_video_async_v2(ndi_send, &video_frame);
143159

144160
// Wait for the next frame
145-
usleep(1000000 / FPS);
161+
usleep(1000000 / NDI_FPS);
146162
}
147163

148164
// Force sync for last frame.

0 commit comments

Comments
 (0)