#pragma once #include #include "error.h" #include "transport.h" /* ------------------------------------------------------------------------- * Message type constants * ------------------------------------------------------------------------- */ #define PROTO_MSG_VIDEO_FRAME 0x0001u #define PROTO_MSG_CONTROL_REQUEST 0x0002u #define PROTO_MSG_CONTROL_RESPONSE 0x0003u #define PROTO_MSG_STREAM_EVENT 0x0004u /* ------------------------------------------------------------------------- * Command codes (carried in CONTROL_REQUEST payload offset 2) * ------------------------------------------------------------------------- */ #define PROTO_CMD_STREAM_OPEN 0x0001u #define PROTO_CMD_STREAM_CLOSE 0x0002u #define PROTO_CMD_ENUM_DEVICES 0x0003u #define PROTO_CMD_ENUM_CONTROLS 0x0004u #define PROTO_CMD_GET_CONTROL 0x0005u #define PROTO_CMD_SET_CONTROL 0x0006u #define PROTO_CMD_ENUM_MONITORS 0x0007u #define PROTO_CMD_START_INGEST 0x0008u #define PROTO_CMD_STOP_INGEST 0x0009u #define PROTO_CMD_START_DISPLAY 0x000Au #define PROTO_CMD_STOP_DISPLAY 0x000Bu /* ------------------------------------------------------------------------- * Response status codes (carried in CONTROL_RESPONSE payload offset 2) * ------------------------------------------------------------------------- */ #define PROTO_STATUS_OK 0x0000u #define PROTO_STATUS_ERROR 0x0001u #define PROTO_STATUS_UNKNOWN_CMD 0x0002u #define PROTO_STATUS_INVALID_PARAMS 0x0003u #define PROTO_STATUS_NOT_FOUND 0x0004u /* ------------------------------------------------------------------------- * Stream event codes (carried in STREAM_EVENT payload offset 2) * ------------------------------------------------------------------------- */ #define PROTO_EVENT_INTERRUPTED 0x01u #define PROTO_EVENT_RESUMED 0x02u /* ------------------------------------------------------------------------- * Codec format codes (STREAM_OPEN format field) * ------------------------------------------------------------------------- */ #define PROTO_FORMAT_MJPEG 0x0001u #define PROTO_FORMAT_H264 0x0002u #define PROTO_FORMAT_H265 0x0003u #define PROTO_FORMAT_AV1 0x0004u #define PROTO_FORMAT_FFV1 0x0005u #define PROTO_FORMAT_PRORES 0x0006u #define PROTO_FORMAT_QOI 0x0007u #define PROTO_FORMAT_RAW 0x0008u #define PROTO_FORMAT_RAW_ZSTD 0x0009u /* ------------------------------------------------------------------------- * Pixel format codes (STREAM_OPEN pixel_format field; 0 for compressed) * ------------------------------------------------------------------------- */ #define PROTO_PIXEL_BGRA8888 0x0001u #define PROTO_PIXEL_RGBA8888 0x0002u #define PROTO_PIXEL_BGR888 0x0003u #define PROTO_PIXEL_YUV420P 0x0004u #define PROTO_PIXEL_YUV422 0x0005u /* ------------------------------------------------------------------------- * Transport mode codes (START_INGEST transport_mode field) * ------------------------------------------------------------------------- */ #define PROTO_TRANSPORT_ENCAPSULATED 0x0001u /* framed: message_type + payload_length header */ #define PROTO_TRANSPORT_OPAQUE 0x0002u /* raw byte stream, no frame boundaries */ /* ------------------------------------------------------------------------- * Origin codes (STREAM_OPEN origin field; informational only) * ------------------------------------------------------------------------- */ #define PROTO_ORIGIN_DEVICE_NATIVE 0x0001u #define PROTO_ORIGIN_LIBJPEG_TURBO 0x0002u #define PROTO_ORIGIN_FFMPEG_LIBAV 0x0003u #define PROTO_ORIGIN_FFMPEG_PROC 0x0004u #define PROTO_ORIGIN_VAAPI 0x0005u #define PROTO_ORIGIN_NVENC 0x0006u #define PROTO_ORIGIN_SOFTWARE 0x0007u /* ------------------------------------------------------------------------- * Structs used by write functions (variable-length response payloads) * ------------------------------------------------------------------------- */ struct Proto_Menu_Item { uint32_t index; const char *name; int64_t int_value; }; struct Proto_Control_Info { uint32_t id; uint8_t type; uint32_t flags; const char *name; int32_t min, max, step, default_val, current_val; uint8_t menu_count; const struct Proto_Menu_Item *menu_items; }; /* * A video node associated with a media controller device. * entity_type and entity_flags are MEDIA_ENT_F_* / MEDIA_ENT_FL_* values. * pad_flags uses MEDIA_PAD_FLAG_SOURCE / MEDIA_PAD_FLAG_SINK. * is_capture: 1 if this node is the primary video capture output. */ struct Proto_Video_Node_Info { const char *path; const char *entity_name; uint32_t entity_type; uint32_t entity_flags; uint32_t device_caps; /* V4L2_CAP_* bits from VIDIOC_QUERYCAP */ uint8_t pad_flags; uint8_t is_capture; }; /* * A media controller device and its associated video nodes. * video_node_count must be <= 255. */ struct Proto_Media_Device_Info { const char *path; const char *driver; const char *model; const char *bus_info; uint8_t video_node_count; const struct Proto_Video_Node_Info *video_nodes; }; /* * A standalone V4L2 device with no associated media controller. * name is the card name from VIDIOC_QUERYCAP. */ struct Proto_Standalone_Device_Info { const char *path; const char *name; }; /* * An active display window (video sink role). * device_id is the flat device index (follows all V4L2 devices). * stream_id is the stream being displayed; win_* are current geometry. */ struct Proto_Display_Device_Info { uint16_t device_id; uint16_t stream_id; int16_t win_x, win_y; uint16_t win_w, win_h; uint8_t scale_mode; uint8_t anchor; }; /* ------------------------------------------------------------------------- * Display device pseudo-control IDs — used in ENUM_CONTROLS / GET_CONTROL / * SET_CONTROL for display device indices returned by ENUM_DEVICES. * ------------------------------------------------------------------------- */ #define PROTO_DISPLAY_CTRL_SCALE_MODE 0x00D00001u /* int 0-3: stretch/fit/fill/1:1 */ #define PROTO_DISPLAY_CTRL_ANCHOR 0x00D00002u /* int 0-1: center/topleft */ #define PROTO_DISPLAY_CTRL_NO_SIGNAL_FPS 0x00D00003u /* int 1-60: no-signal animation fps */ struct Proto_Monitor_Info { int32_t x, y; uint32_t width, height; const char *name; }; /* ------------------------------------------------------------------------- * Structs used by read functions * Fields with pointer types point INTO the caller's payload buffer. * The caller must keep the payload alive while using those pointers. * Strings are NOT NUL-terminated. * ------------------------------------------------------------------------- */ struct Proto_Video_Frame { uint16_t stream_id; const uint8_t *data; uint32_t data_len; }; struct Proto_Stream_Event { uint16_t stream_id; uint8_t event_code; }; struct Proto_Request_Header { uint16_t request_id; uint16_t command; }; struct Proto_Stream_Open { uint16_t request_id; uint16_t stream_id; uint16_t format; uint16_t pixel_format; uint16_t origin; }; struct Proto_Stream_Close { uint16_t request_id; uint16_t stream_id; }; struct Proto_Enum_Controls_Req { uint16_t request_id; uint16_t device_index; }; struct Proto_Get_Control_Req { uint16_t request_id; uint16_t device_index; uint32_t control_id; }; struct Proto_Set_Control_Req { uint16_t request_id; uint16_t device_index; uint32_t control_id; int32_t value; }; /* * START_INGEST: controller tells a source node to open a V4L2 device and * connect outbound to a sink at dest_host:dest_port. * format/width/height/fps_n/fps_d of 0 mean auto-select. * Strings point into the caller's payload buffer; not NUL-terminated. */ struct Proto_Start_Ingest { uint16_t request_id; uint16_t stream_id; uint16_t format; /* PROTO_FORMAT_* code; 0 = auto (best MJPEG) */ uint16_t width; /* 0 = auto */ uint16_t height; /* 0 = auto */ uint16_t fps_n; /* 0 = auto */ uint16_t fps_d; uint16_t dest_port; uint16_t transport_mode; /* PROTO_TRANSPORT_ENCAPSULATED or PROTO_TRANSPORT_OPAQUE */ const char *device_path; uint8_t device_path_len; const char *dest_host; uint8_t dest_host_len; }; struct Proto_Stop_Ingest { uint16_t request_id; uint16_t stream_id; }; /* * START_DISPLAY: controller tells a sink node to open a viewer window and * display incoming VIDEO_FRAME messages for the given stream_id. * win_x/win_y are screen-space window position (signed: multi-monitor). * win_w/win_h of 0 mean use a default size. * scale_mode: 0=stretch 1=fit 2=fill 3=1:1 (PROTO_DISPLAY_SCALE_*) * anchor: 0=center 1=topleft (PROTO_DISPLAY_ANCHOR_*) */ struct Proto_Start_Display { uint16_t request_id; uint16_t stream_id; int16_t win_x; int16_t win_y; uint16_t win_w; uint16_t win_h; uint8_t scale_mode; uint8_t anchor; uint8_t no_signal_fps; /* 0 = default (15); no-signal animation frame rate */ /* 1 byte reserved */ }; struct Proto_Stop_Display { uint16_t request_id; uint16_t stream_id; }; /* Scale/anchor constants for Proto_Start_Display */ #define PROTO_DISPLAY_SCALE_STRETCH 0u #define PROTO_DISPLAY_SCALE_FIT 1u #define PROTO_DISPLAY_SCALE_FILL 2u #define PROTO_DISPLAY_SCALE_1_1 3u #define PROTO_DISPLAY_ANCHOR_CENTER 0u #define PROTO_DISPLAY_ANCHOR_TOPLEFT 1u struct Proto_Response_Header { uint16_t request_id; uint16_t status; }; struct Proto_Get_Control_Resp { uint16_t request_id; uint16_t status; int32_t value; }; /* ------------------------------------------------------------------------- * Write functions — serialize and send via transport_send_frame. * All return APP_OK or an error from the transport layer. * ------------------------------------------------------------------------- */ /* * VIDEO_FRAME: prepends stream_id (2 bytes) to data and sends. * data/data_len is the compressed frame; the stream must already be open. */ struct App_Error proto_write_video_frame(struct Transport_Conn *conn, uint16_t stream_id, const uint8_t *data, uint32_t data_len); /* STREAM_EVENT (3 bytes) */ struct App_Error proto_write_stream_event(struct Transport_Conn *conn, uint16_t stream_id, uint8_t event_code); /* CONTROL_REQUEST: STREAM_OPEN (12 bytes) */ struct App_Error proto_write_stream_open(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id, uint16_t format, uint16_t pixel_format, uint16_t origin); /* CONTROL_REQUEST: STREAM_CLOSE (6 bytes) */ struct App_Error proto_write_stream_close(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id); /* CONTROL_REQUEST: ENUM_DEVICES (4 bytes, no extra fields) */ struct App_Error proto_write_enum_devices(struct Transport_Conn *conn, uint16_t request_id); /* CONTROL_REQUEST: ENUM_CONTROLS (6 bytes) */ struct App_Error proto_write_enum_controls(struct Transport_Conn *conn, uint16_t request_id, uint16_t device_index); /* CONTROL_REQUEST: GET_CONTROL (10 bytes) */ struct App_Error proto_write_get_control(struct Transport_Conn *conn, uint16_t request_id, uint16_t device_index, uint32_t control_id); /* CONTROL_REQUEST: SET_CONTROL (14 bytes) */ struct App_Error proto_write_set_control(struct Transport_Conn *conn, uint16_t request_id, uint16_t device_index, uint32_t control_id, int32_t value); /* CONTROL_REQUEST: ENUM_MONITORS (4 bytes, no extra fields) */ struct App_Error proto_write_enum_monitors(struct Transport_Conn *conn, uint16_t request_id); /* CONTROL_REQUEST: START_INGEST */ struct App_Error proto_write_start_ingest(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id, uint16_t format, uint16_t width, uint16_t height, uint16_t fps_n, uint16_t fps_d, uint16_t transport_mode, const char *device_path, const char *dest_host, uint16_t dest_port); /* CONTROL_REQUEST: STOP_INGEST */ struct App_Error proto_write_stop_ingest(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id); /* CONTROL_REQUEST: START_DISPLAY */ struct App_Error proto_write_start_display(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id, int16_t win_x, int16_t win_y, uint16_t win_w, uint16_t win_h, uint8_t scale_mode, uint8_t anchor, uint8_t no_signal_fps); /* CONTROL_REQUEST: STOP_DISPLAY */ struct App_Error proto_write_stop_display(struct Transport_Conn *conn, uint16_t request_id, uint16_t stream_id); /* * CONTROL_RESPONSE: generic. * payload/payload_len are the command-specific bytes after request_id+status. * Pass payload=NULL, payload_len=0 for responses with no extra fields * (e.g. STREAM_OPEN ok, STREAM_CLOSE, SET_CONTROL). */ struct App_Error proto_write_control_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const uint8_t *payload, uint32_t payload_len); /* CONTROL_RESPONSE: ENUM_DEVICES */ struct App_Error proto_write_enum_devices_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const struct Proto_Media_Device_Info *media_devices, uint16_t media_count, const struct Proto_Standalone_Device_Info *standalone, uint16_t standalone_count, const struct Proto_Display_Device_Info *displays, uint16_t display_count); /* CONTROL_RESPONSE: ENUM_CONTROLS */ struct App_Error proto_write_enum_controls_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const struct Proto_Control_Info *controls, uint16_t count); /* CONTROL_RESPONSE: GET_CONTROL */ struct App_Error proto_write_get_control_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, int32_t value); /* CONTROL_RESPONSE: ENUM_MONITORS */ struct App_Error proto_write_enum_monitors_response(struct Transport_Conn *conn, uint16_t request_id, uint16_t status, const struct Proto_Monitor_Info *monitors, uint16_t count); /* ------------------------------------------------------------------------- * Read functions — parse raw payload bytes into typed structs. * All return APP_INVALID_ERROR_MSG(0, ...) on malformed payloads. * ------------------------------------------------------------------------- */ struct App_Error proto_read_video_frame( const uint8_t *payload, uint32_t length, struct Proto_Video_Frame *out); struct App_Error proto_read_stream_event( const uint8_t *payload, uint32_t length, struct Proto_Stream_Event *out); /* * Read the common 4-byte request header (request_id + command). * Dispatch on header.command, then call the appropriate specific reader. * ENUM_DEVICES and ENUM_MONITORS have no extra fields beyond the header. */ struct App_Error proto_read_request_header( const uint8_t *payload, uint32_t length, struct Proto_Request_Header *out); struct App_Error proto_read_stream_open( const uint8_t *payload, uint32_t length, struct Proto_Stream_Open *out); struct App_Error proto_read_stream_close( const uint8_t *payload, uint32_t length, struct Proto_Stream_Close *out); struct App_Error proto_read_enum_controls_req( const uint8_t *payload, uint32_t length, struct Proto_Enum_Controls_Req *out); struct App_Error proto_read_get_control_req( const uint8_t *payload, uint32_t length, struct Proto_Get_Control_Req *out); struct App_Error proto_read_set_control_req( const uint8_t *payload, uint32_t length, struct Proto_Set_Control_Req *out); struct App_Error proto_read_start_ingest( const uint8_t *payload, uint32_t length, struct Proto_Start_Ingest *out); struct App_Error proto_read_stop_ingest( const uint8_t *payload, uint32_t length, struct Proto_Stop_Ingest *out); struct App_Error proto_read_start_display( const uint8_t *payload, uint32_t length, struct Proto_Start_Display *out); struct App_Error proto_read_stop_display( const uint8_t *payload, uint32_t length, struct Proto_Stop_Display *out); /* * Read the common 4-byte response header (request_id + status). * For responses with no extra fields (STREAM_OPEN, STREAM_CLOSE, SET_CONTROL), * this is the complete parse. */ struct App_Error proto_read_response_header( const uint8_t *payload, uint32_t length, struct Proto_Response_Header *out); struct App_Error proto_read_get_control_response( const uint8_t *payload, uint32_t length, struct Proto_Get_Control_Resp *out); /* * Variable-length response readers use callbacks to avoid heap allocation. * Strings point into the payload and are NOT NUL-terminated; use *_len. */ /* * on_media_device is called once per media controller device. * on_video_node is called video_node_count times immediately after, * once per video node belonging to that media device. * on_standalone is called once per V4L2 device with no media controller. * Any callback may be NULL. */ struct App_Error proto_read_enum_devices_response( const uint8_t *payload, uint32_t length, struct Proto_Response_Header *header_out, void (*on_media_device)( const char *path, uint8_t path_len, const char *driver, uint8_t driver_len, const char *model, uint8_t model_len, const char *bus_info, uint8_t bus_info_len, uint8_t video_node_count, void *userdata), void (*on_video_node)( const char *path, uint8_t path_len, const char *entity_name, uint8_t entity_name_len, uint32_t entity_type, uint32_t entity_flags, uint32_t device_caps, uint8_t pad_flags, uint8_t is_capture, void *userdata), void (*on_standalone)( const char *path, uint8_t path_len, const char *name, uint8_t name_len, void *userdata), void (*on_display)( uint16_t device_id, uint16_t stream_id, int16_t win_x, int16_t win_y, uint16_t win_w, uint16_t win_h, uint8_t scale_mode, uint8_t anchor, void *userdata), void *userdata); /* * on_control is called once per control. * on_menu_item is called once per menu item immediately after its on_control * call; menu_count in on_control says how many to expect. * on_menu_item may be NULL if the caller does not need menu items. */ struct App_Error proto_read_enum_controls_response( const uint8_t *payload, uint32_t length, struct Proto_Response_Header *header_out, void (*on_control)( uint32_t id, uint8_t type, uint32_t flags, const char *name, uint8_t name_len, int32_t min, int32_t max, int32_t step, int32_t default_val, int32_t current_val, uint8_t menu_count, void *userdata), void (*on_menu_item)( uint32_t index, const char *name, uint8_t name_len, int64_t int_value, void *userdata), void *userdata); struct App_Error proto_read_enum_monitors_response( const uint8_t *payload, uint32_t length, struct Proto_Response_Header *header_out, void (*on_monitor)( int32_t x, int32_t y, uint32_t width, uint32_t height, const char *name, uint8_t name_len, void *userdata), void *userdata);