From 7e5628f44ce052e326e920f93ccf28f7380d8ab0 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Fri, 27 Mar 2026 02:04:57 +0000 Subject: [PATCH] docs: replace ASCII art with Mermaid packet-beta diagrams in protocol.md Co-Authored-By: Claude Sonnet 4.6 --- docs/protocol.md | 199 +++++++++++++++++++++++++---------------------- 1 file changed, 105 insertions(+), 94 deletions(-) diff --git a/docs/protocol.md b/docs/protocol.md index c952fa8..8f48d15 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -28,16 +28,14 @@ flowchart TD Every message on the wire is a frame: -``` -+------------------+------------------------+--------------------+ -| message_type: u16 | payload_length: u32 | payload: bytes ... | -+------------------+------------------------+--------------------+ - 2 bytes 4 bytes payload_length bytes +```mermaid +packet-beta +0-15: "message_type" +16-47: "payload_length" +48-79: "payload …" ``` -Total header size: **6 bytes**. - -`payload_length` is the byte count of the payload only — it does not include the 6-byte header itself. +Total header size: **6 bytes**. `payload_length` is the byte count of the payload only — it does not include the 6-byte header itself. A node that does not recognise `message_type` can skip the frame by consuming exactly `payload_length` bytes and discarding them. This allows relays and future nodes to forward or ignore unknown message types without understanding their structure. @@ -69,22 +67,21 @@ Values not listed are reserved. A node receiving an unknown type must skip the p ### `VIDEO_FRAME` (0x0001) -``` -+---------------+----------------------------------------------+ -| stream_id: u16 | frame data (compressed, codec per stream_open) | -+---------------+----------------------------------------------+ - 2 bytes payload_length - 2 bytes +```mermaid +packet-beta +0-15: "stream_id" +16-47: "frame data …" ``` `stream_id` identifies which video stream this frame belongs to. The codec is established at stream open time (see [Stream Lifecycle](#stream-lifecycle)) and does not appear in every frame. ### `CONTROL_REQUEST` (0x0002) -``` -+------------------+----------------+---------------------------+ -| request_id: u16 | command: u16 | command-specific fields | -+------------------+----------------+---------------------------+ - 2 bytes 2 bytes remaining bytes +```mermaid +packet-beta +0-15: "request_id" +16-31: "command" +32-63: "command-specific …" ``` `request_id` is chosen by the sender and echoed in the matching `CONTROL_RESPONSE`. It is used to correlate responses to requests when multiple requests are in flight simultaneously. @@ -103,11 +100,11 @@ Values not listed are reserved. A node receiving an unknown type must skip the p ### `CONTROL_RESPONSE` (0x0003) -``` -+------------------+---------------+---------------------------+ -| request_id: u16 | status: u16 | response-specific fields | -+------------------+---------------+---------------------------+ - 2 bytes 2 bytes remaining bytes +```mermaid +packet-beta +0-15: "request_id" +16-31: "status" +32-63: "response-specific …" ``` `request_id` matches the originating request. `status` values: @@ -122,11 +119,11 @@ Values not listed are reserved. A node receiving an unknown type must skip the p ### `STREAM_EVENT` (0x0004) -``` -+---------------+------------------+---------------------------+ -| stream_id: u16 | event_code: u8 | event-specific fields | -+---------------+------------------+---------------------------+ - 2 bytes 1 byte remaining bytes +```mermaid +packet-beta +0-15: "stream_id" +16-23: "event_code" +24-55: "event-specific …" ``` `event_code` values: @@ -144,12 +141,14 @@ Before video frames can flow, a stream must be opened. This establishes the code ### Opening a Stream (`STREAM_OPEN` request) -The sender issues a `CONTROL_REQUEST` with command `STREAM_OPEN`: - -``` -+------------------+------------------+-----------------+------------------+-------------------+----------------+ -| request_id: u16 | command: u16 | stream_id: u16 | format: u16 | pixel_format: u16 | origin: u16 | -+------------------+------------------+-----------------+------------------+-------------------+----------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "command = 0x0001" +32-47: "stream_id" +48-63: "format" +64-79: "pixel_format" +80-95: "origin" ``` | Field | Description | @@ -163,10 +162,11 @@ The receiver responds with `CONTROL_RESPONSE`. On `status = OK` the stream is op ### Closing a Stream (`STREAM_CLOSE` request) -``` -+------------------+------------------+-----------------+ -| request_id: u16 | command: u16 | stream_id: u16 | -+------------------+------------------+-----------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "command = 0x0002" +32-47: "stream_id" ``` After a close response, no further `VIDEO_FRAME` messages should be sent for that `stream_id`. @@ -225,10 +225,14 @@ Node discovery uses UDP multicast. The wire format is a standard transport frame Sent periodically by every node and immediately on startup. -``` -+----------------------+------------+-----------------+------------------+-------------------+------------------+ -| protocol_version: u8 | site_id: u16 | tcp_port: u16 | function_flags: u16 | name_len: u8 | name: bytes | -+----------------------+------------+-----------------+------------------+-------------------+------------------+ +```mermaid +packet-beta +0-7: "protocol_version" +8-23: "site_id" +24-39: "tcp_port" +40-55: "function_flags" +56-63: "name_len" +64-95: "name …" ``` | Field | Description | @@ -273,10 +277,10 @@ The gateway assigns a distinct non-zero `site_id` to each side and rewrites `sit `str8` — a length-prefixed UTF-8 string, not NUL-terminated on the wire: -``` -+------------+-------------------+ -| length: u8 | bytes: length × u8 | -+------------+-------------------+ +```mermaid +packet-beta +0-7: "length" +8-39: "bytes (≤ 255) …" ``` Maximum string length is 255 bytes. @@ -293,26 +297,17 @@ All requests follow the `CONTROL_REQUEST` frame format (request_id + command + c **Response** on status `OK`: -``` -+-------------------+ -| media_count: u16 | -+-------------------+ - × media_count: - +------------+------------+-----------+-------------+-----------------+ - | path: str8 | driver:str8| model:str8| bus_info:str8| vnode_count: u8 | - +------------+------------+-----------+-------------+-----------------+ - × vnode_count: - +------------+-----------------+------------------+-------------------+------------------+----------------+------------------+ - | path: str8 | entity_name:str8| entity_type: u32 | entity_flags: u32 | device_caps: u32 | pad_flags: u8 | is_capture: u8 | - +------------+-----------------+------------------+-------------------+------------------+----------------+------------------+ -+----------------------+ -| standalone_count: u16 | -+----------------------+ - × standalone_count: - +------------+------------+ - | path: str8 | name: str8 | - +------------+------------+ -``` +Repeated `media_count` times (u16): + +- `path` str8, `driver` str8, `model` str8, `bus_info` str8 +- `vnode_count` u8, then repeated `vnode_count` times: + - `path` str8, `entity_name` str8 + - `entity_type` u32, `entity_flags` u32, `device_caps` u32 + - `pad_flags` u8, `is_capture` u8 + +Then repeated `standalone_count` times (u16): + +- `path` str8, `name` str8 `device_caps` carries `V4L2_CAP_*` bits from `VIDIOC_QUERYCAP` (using `device_caps` if `V4L2_CAP_DEVICE_CAPS` is set, otherwise `capabilities`). Notable bits: `0x00000001` = `VIDEO_CAPTURE`, `0x00800000` = `META_CAPTURE`. @@ -322,28 +317,38 @@ All requests follow the `CONTROL_REQUEST` frame format (request_id + command + c **Request**: -``` -+--------------------+ -| device_index: u16 | -+--------------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "command = 0x0004" +32-47: "device_index" ``` -**Response** on status `OK`: +**Response** on status `OK` — repeated `count` times (u16): +Fixed prefix per control: + +```mermaid +packet-beta +0-31: "id" +32-39: "type" +40-71: "flags" ``` -+---------------+ -| count: u16 | -+---------------+ - × count: - +---------+---------+----------+------------+----------+---------+----------+-------------+-----------------+------------------+ - | id: u32 | type: u8 | flags: u32 | name: str8 | min: i32 | max: i32 | step: i32 | default: i32 | current: i32 | menu_count: u8 | - +---------+---------+----------+------------+----------+---------+----------+-------------+-----------------+------------------+ - × menu_count: - +------------+------------+------------------+ - | index: u32 | name: str8 | int_value: i64 | - +------------+------------+------------------+ + +Followed by `name` str8, then the fixed suffix: + +```mermaid +packet-beta +0-31: "min" +32-63: "max" +64-95: "step" +96-127: "default_val" +128-159: "current_val" +160-167: "menu_count" ``` +Followed by `menu_count` menu items. Each menu item: `index` u32, then `name` str8, then `int_value` i64. + `type` values match `V4L2_CTRL_TYPE_*`: | Value | Type | @@ -364,28 +369,34 @@ Menu items may have non-contiguous `index` values (gaps where the driver returns **Request**: -``` -+--------------------+------------------+ -| device_index: u16 | control_id: u32 | -+--------------------+------------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "command = 0x0005" +32-47: "device_index" +48-79: "control_id" ``` **Response** on status `OK`: -``` -+-------------+ -| value: i32 | -+-------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "status" +32-63: "value" ``` ### `SET_CONTROL` (0x0006) **Request**: -``` -+--------------------+------------------+-------------+ -| device_index: u16 | control_id: u32 | value: i32 | -+--------------------+------------------+-------------+ +```mermaid +packet-beta +0-15: "request_id" +16-31: "command = 0x0006" +32-47: "device_index" +48-79: "control_id" +80-111: "value" ``` **Response** — no extra fields beyond request_id and status.