Add START_INGEST and STOP_INGEST protocol commands

START_INGEST carries stream_id, format/width/height/fps, dest_host:port,
transport_mode (encapsulated or opaque), and device_path. All format fields
default to 0 (auto-select). STOP_INGEST carries stream_id only.

Both commands set wanted state on the node; reconciliation is asynchronous.
Protocol doc updated with wire schemas for both commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 02:02:38 +00:00
parent 639a84b1b9
commit 6c9e0ce7dc
3 changed files with 200 additions and 0 deletions

View File

@@ -300,6 +300,54 @@ struct App_Error proto_write_enum_monitors(struct Transport_Conn *conn,
return transport_send_frame(conn, PROTO_MSG_CONTROL_REQUEST, buf, 4);
}
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)
{
size_t dp_len = device_path ? strlen(device_path) : 0;
size_t dh_len = dest_host ? strlen(dest_host) : 0;
uint8_t dp_n = dp_len > 255u ? 255u : (uint8_t)dp_len;
uint8_t dh_n = dh_len > 255u ? 255u : (uint8_t)dh_len;
/* 20 bytes fixed + 1+dp_n (device_path str8) + 1+dh_n (dest_host str8) */
uint32_t total = 20u + 1u + dp_n + 1u + dh_n;
uint8_t *buf = malloc(total);
if (!buf) { return APP_SYSCALL_ERROR(); }
uint32_t o = 0;
put_u16(buf, o, request_id); o += 2;
put_u16(buf, o, PROTO_CMD_START_INGEST); o += 2;
put_u16(buf, o, stream_id); o += 2;
put_u16(buf, o, format); o += 2;
put_u16(buf, o, width); o += 2;
put_u16(buf, o, height); o += 2;
put_u16(buf, o, fps_n); o += 2;
put_u16(buf, o, fps_d); o += 2;
put_u16(buf, o, dest_port); o += 2;
put_u16(buf, o, transport_mode); o += 2;
put_u8 (buf, o, dp_n); o += 1;
memcpy(buf + o, device_path, dp_n); o += dp_n;
put_u8 (buf, o, dh_n); o += 1;
memcpy(buf + o, dest_host, dh_n); o += dh_n;
struct App_Error e = transport_send_frame(conn, PROTO_MSG_CONTROL_REQUEST, buf, total);
free(buf);
return e;
}
struct App_Error proto_write_stop_ingest(struct Transport_Conn *conn,
uint16_t request_id, uint16_t stream_id)
{
uint8_t buf[6];
put_u16(buf, 0, request_id);
put_u16(buf, 2, PROTO_CMD_STOP_INGEST);
put_u16(buf, 4, stream_id);
return transport_send_frame(conn, PROTO_MSG_CONTROL_REQUEST, buf, 6);
}
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)
@@ -515,6 +563,44 @@ struct App_Error proto_read_set_control_req(
return APP_OK;
}
struct App_Error proto_read_start_ingest(
const uint8_t *payload, uint32_t length,
struct Proto_Start_Ingest *out)
{
/* Fixed portion: request_id(2) cmd(2) stream_id(2) format(2) width(2)
* height(2) fps_n(2) fps_d(2) dest_port(2) transport_mode(2) = 20 bytes,
* then two str8 fields. */
struct Cursor c;
cur_init(&c, payload, length);
out->request_id = cur_u16(&c);
/* skip command word at [2..3] */
(void) cur_u16(&c);
out->stream_id = cur_u16(&c);
out->format = cur_u16(&c);
out->width = cur_u16(&c);
out->height = cur_u16(&c);
out->fps_n = cur_u16(&c);
out->fps_d = cur_u16(&c);
out->dest_port = cur_u16(&c);
out->transport_mode = cur_u16(&c);
out->device_path = cur_str8(&c, &out->device_path_len);
out->dest_host = cur_str8(&c, &out->dest_host_len);
CUR_CHECK(c);
return APP_OK;
}
struct App_Error proto_read_stop_ingest(
const uint8_t *payload, uint32_t length,
struct Proto_Stop_Ingest *out)
{
if (length < 6) { return APP_INVALID_ERROR_MSG(0, "STOP_INGEST payload too short"); }
out->request_id = get_u16(payload, 0);
out->stream_id = get_u16(payload, 4);
return APP_OK;
}
struct App_Error proto_read_response_header(
const uint8_t *payload, uint32_t length,
struct Proto_Response_Header *out)