Add display sink: START_DISPLAY/STOP_DISPLAY, multi-window xorg, random port

Protocol:
- Add PROTO_CMD_START_DISPLAY (0x000A) and PROTO_CMD_STOP_DISPLAY (0x000B)
  with write/read functions; Proto_Start_Display carries stream_id, window
  position/size, scale and anchor; PROTO_DISPLAY_SCALE_*/ANCHOR_* constants

Node display sink:
- Display_Slot struct with wanted_state/current_state (DISP_CLOSED/DISP_OPEN);
  handlers set wanted state, display_loop_tick on main thread reconciles
- Up to MAX_DISPLAYS (4) simultaneous viewer windows
- on_frame routes incoming VIDEO_FRAME messages to matching display slot;
  transport thread deposits payload, main thread consumes without holding lock
  during JPEG decode/upload
- Main thread runs GL event loop when xorg is available; headless fallback
  joins reconciler timer thread as before

Xorg multi-window:
- Ref-count glfwInit/glfwTerminate via glfw_acquire/glfw_release so closing
  one viewer does not terminate GLFW for remaining windows
- Add glfwMakeContextCurrent before GL calls in push_yuv420, push_bgra,
  push_mjpeg and poll so each viewer uses its own GL context correctly

Transport random port:
- Bind port 0 lets the OS assign a free port; getsockname reads it back
  into server->bound_port after bind
- Add transport_server_get_port() accessor
- Default tcp_port changed from 8000 to 0 (random); node prints actual
  port after server start so it is always visible in output
- Add --port PORT CLI override (before config-file argument)

controller_cli:
- Add start-display and stop-display commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 08:03:21 +00:00
parent 28216999e0
commit 32d31cbd1e
6 changed files with 199 additions and 9 deletions

View File

@@ -302,6 +302,39 @@ static void cmd_stop_ingest(struct Transport_Conn *conn,
proto_write_stop_ingest(conn, next_req_id(req), stream_id));
}
static void cmd_start_display(struct Transport_Conn *conn,
struct Ctrl_State *cs, uint16_t *req,
int ntok, char *tokens[])
{
/* Required: stream_id
* Optional: win_x win_y win_w win_h */
if (ntok < 2) {
printf("usage: start-display <stream_id> [win_x] [win_y] [win_w] [win_h]\n");
return;
}
uint16_t stream_id = (uint16_t)atoi(tokens[1]);
int16_t win_x = ntok > 2 ? (int16_t)atoi(tokens[2]) : 0;
int16_t win_y = ntok > 3 ? (int16_t)atoi(tokens[3]) : 0;
uint16_t win_w = ntok > 4 ? (uint16_t)atoi(tokens[4]) : 0;
uint16_t win_h = ntok > 5 ? (uint16_t)atoi(tokens[5]) : 0;
printf("start-display: stream=%u pos=%d,%d size=%ux%u\n",
stream_id, win_x, win_y, win_w, win_h);
SEND_AND_WAIT(cs, PROTO_CMD_START_DISPLAY,
proto_write_start_display(conn, next_req_id(req),
stream_id, win_x, win_y, win_w, win_h,
PROTO_DISPLAY_SCALE_FIT, PROTO_DISPLAY_ANCHOR_CENTER));
}
static void cmd_stop_display(struct Transport_Conn *conn,
struct Ctrl_State *cs, uint16_t *req,
const char *sid_str)
{
uint16_t stream_id = (uint16_t)atoi(sid_str);
printf("stop-display: stream=%u\n", stream_id);
SEND_AND_WAIT(cs, PROTO_CMD_STOP_DISPLAY,
proto_write_stop_display(conn, next_req_id(req), stream_id));
}
static void cmd_help(void)
{
printf("commands:\n"
@@ -312,6 +345,8 @@ static void cmd_help(void)
" start-ingest <stream_id> <device> <dest_host> <dest_port>"
" [format] [width] [height] [fps_n] [fps_d]\n"
" stop-ingest <stream_id>\n"
" start-display <stream_id> [win_x] [win_y] [win_w] [win_h]\n"
" stop-display <stream_id>\n"
" help\n"
" quit / exit\n");
}
@@ -412,6 +447,11 @@ int main(int argc, char **argv)
} else if (strcmp(cmd, "stop-ingest") == 0) {
if (ntok < 2) { printf("usage: stop-ingest <stream_id>\n"); }
else { cmd_stop_ingest(conn, &cs, &req_id, tokens[1]); }
} else if (strcmp(cmd, "start-display") == 0) {
cmd_start_display(conn, &cs, &req_id, ntok, tokens);
} else if (strcmp(cmd, "stop-display") == 0) {
if (ntok < 2) { printf("usage: stop-display <stream_id>\n"); }
else { cmd_stop_display(conn, &cs, &req_id, tokens[1]); }
} else {
printf("unknown command: %s (type 'help' for commands)\n", cmd);
}