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>
98 lines
3.3 KiB
C
98 lines
3.3 KiB
C
#pragma once
|
|
|
|
#include <stdint.h>
|
|
#include "error.h"
|
|
|
|
#define TRANSPORT_FRAME_HEADER_SIZE 6u
|
|
#define TRANSPORT_DEFAULT_MAX_PAYLOAD (16u * 1024u * 1024u)
|
|
|
|
struct Transport_Conn;
|
|
struct Transport_Server;
|
|
|
|
/*
|
|
* A received frame. payload is malloc'd by the transport layer;
|
|
* the on_frame callback takes ownership and must free it.
|
|
* payload is NULL when payload_length is 0.
|
|
*
|
|
* The header carries only message_type and payload_length.
|
|
* All message-specific fields (stream_id, request_id, etc.) are
|
|
* the first bytes of the payload, interpreted by the message handler.
|
|
*/
|
|
struct Transport_Frame {
|
|
uint16_t message_type;
|
|
uint32_t payload_length;
|
|
uint8_t *payload;
|
|
};
|
|
|
|
typedef void (*Transport_Frame_Cb)(
|
|
struct Transport_Conn *conn,
|
|
struct Transport_Frame *frame,
|
|
void *userdata);
|
|
|
|
typedef void (*Transport_Connect_Cb)(
|
|
struct Transport_Conn *conn,
|
|
void *userdata);
|
|
|
|
typedef void (*Transport_Disconnect_Cb)(
|
|
struct Transport_Conn *conn,
|
|
void *userdata);
|
|
|
|
struct Transport_Server_Config {
|
|
uint16_t port;
|
|
int max_connections; /* inbound limit; excess connections are closed immediately */
|
|
uint32_t max_payload; /* max payload bytes; frames exceeding this disconnect the peer */
|
|
Transport_Frame_Cb on_frame; /* required */
|
|
Transport_Connect_Cb on_connect; /* optional; called before read loop starts */
|
|
Transport_Disconnect_Cb on_disconnect; /* optional; called from read thread before conn is freed */
|
|
void *userdata;
|
|
};
|
|
|
|
/* Create a server (does not start listening yet). */
|
|
struct App_Error transport_server_create(struct Transport_Server **out,
|
|
struct Transport_Server_Config *config);
|
|
|
|
/* Bind, listen, and spawn the accept thread.
|
|
* If config.port is 0, the OS assigns a free port; use
|
|
* transport_server_get_port() afterwards to retrieve it. */
|
|
struct App_Error transport_server_start(struct Transport_Server *server);
|
|
|
|
/* Return the port the server is actually listening on.
|
|
* Valid after a successful transport_server_start(). */
|
|
uint16_t transport_server_get_port(const struct Transport_Server *server);
|
|
|
|
/*
|
|
* Stop accepting new connections and free the server.
|
|
* Active connections continue until they disconnect naturally.
|
|
*/
|
|
void transport_server_destroy(struct Transport_Server *server);
|
|
|
|
/*
|
|
* Connect outbound to host:port and spawn a read thread.
|
|
* on_disconnect is optional. on_frame is required.
|
|
* The conn is freed when the read thread exits.
|
|
* Do not use conn after on_disconnect has been called.
|
|
*/
|
|
struct App_Error transport_connect(struct Transport_Conn **out,
|
|
const char *host,
|
|
uint16_t port,
|
|
uint32_t max_payload,
|
|
Transport_Frame_Cb on_frame,
|
|
Transport_Disconnect_Cb on_disconnect,
|
|
void *userdata);
|
|
|
|
/*
|
|
* Send a frame. Thread-safe.
|
|
* payload may be NULL when length is 0.
|
|
*/
|
|
struct App_Error transport_send_frame(struct Transport_Conn *conn,
|
|
uint16_t message_type,
|
|
const uint8_t *payload,
|
|
uint32_t length);
|
|
|
|
/*
|
|
* Close the connection fd. The read thread will detect the error,
|
|
* call on_disconnect, then free the conn.
|
|
* Do not use conn after calling this.
|
|
*/
|
|
void transport_conn_close(struct Transport_Conn *conn);
|