#include #include #include #include #include "protocol.h" #include "transport.h" #include "error.h" /* -- frame decoder --------------------------------------------------------- */ static const char *cmd_name(uint16_t cmd) { switch (cmd) { case PROTO_CMD_STREAM_OPEN: return "STREAM_OPEN"; case PROTO_CMD_STREAM_CLOSE: return "STREAM_CLOSE"; case PROTO_CMD_ENUM_DEVICES: return "ENUM_DEVICES"; case PROTO_CMD_ENUM_CONTROLS: return "ENUM_CONTROLS"; case PROTO_CMD_GET_CONTROL: return "GET_CONTROL"; case PROTO_CMD_SET_CONTROL: return "SET_CONTROL"; case PROTO_CMD_ENUM_MONITORS: return "ENUM_MONITORS"; default: return "unknown"; } } static const char *event_name(uint8_t code) { switch (code) { case PROTO_EVENT_INTERRUPTED: return "INTERRUPTED"; case PROTO_EVENT_RESUMED: return "RESUMED"; default: return "unknown"; } } static const char *status_name(uint16_t s) { switch (s) { case PROTO_STATUS_OK: return "OK"; case PROTO_STATUS_ERROR: return "ERROR"; case PROTO_STATUS_UNKNOWN_CMD: return "UNKNOWN_CMD"; case PROTO_STATUS_INVALID_PARAMS: return "INVALID_PARAMS"; case PROTO_STATUS_NOT_FOUND: return "NOT_FOUND"; default: return "unknown"; } } static void decode_and_print(struct Transport_Frame *frame) { switch (frame->message_type) { case PROTO_MSG_VIDEO_FRAME: { struct Proto_Video_Frame vf; struct App_Error e = proto_read_video_frame( frame->payload, frame->payload_length, &vf); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf("VIDEO_FRAME stream_id=%u data_len=%u\n", vf.stream_id, vf.data_len); break; } case PROTO_MSG_STREAM_EVENT: { struct Proto_Stream_Event ev; struct App_Error e = proto_read_stream_event( frame->payload, frame->payload_length, &ev); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf("STREAM_EVENT stream_id=%u event=%s\n", ev.stream_id, event_name(ev.event_code)); break; } case PROTO_MSG_CONTROL_REQUEST: { struct Proto_Request_Header hdr; struct App_Error e = proto_read_request_header( frame->payload, frame->payload_length, &hdr); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf("CONTROL_REQUEST request_id=%u command=%s\n", hdr.request_id, cmd_name(hdr.command)); switch (hdr.command) { case PROTO_CMD_STREAM_OPEN: { struct Proto_Stream_Open so; e = proto_read_stream_open(frame->payload, frame->payload_length, &so); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf(" stream_id=%u format=0x%04x pixel_format=0x%04x origin=0x%04x\n", so.stream_id, so.format, so.pixel_format, so.origin); break; } case PROTO_CMD_STREAM_CLOSE: { struct Proto_Stream_Close sc; e = proto_read_stream_close(frame->payload, frame->payload_length, &sc); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf(" stream_id=%u\n", sc.stream_id); break; } case PROTO_CMD_ENUM_CONTROLS: { struct Proto_Enum_Controls_Req req; e = proto_read_enum_controls_req(frame->payload, frame->payload_length, &req); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf(" device_index=%u\n", req.device_index); break; } case PROTO_CMD_GET_CONTROL: { struct Proto_Get_Control_Req req; e = proto_read_get_control_req(frame->payload, frame->payload_length, &req); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf(" device_index=%u control_id=%u\n", req.device_index, req.control_id); break; } case PROTO_CMD_SET_CONTROL: { struct Proto_Set_Control_Req req; e = proto_read_set_control_req(frame->payload, frame->payload_length, &req); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf(" device_index=%u control_id=%u value=%d\n", req.device_index, req.control_id, req.value); break; } default: break; } break; } case PROTO_MSG_CONTROL_RESPONSE: { struct Proto_Response_Header hdr; struct App_Error e = proto_read_response_header( frame->payload, frame->payload_length, &hdr); if (!APP_IS_OK(e)) { app_error_print(&e); return; } printf("CONTROL_RESPONSE request_id=%u status=%s\n", hdr.request_id, status_name(hdr.status)); /* extra bytes are command-specific; caller must know the command */ break; } default: printf("unknown message_type=0x%04x payload_length=%u\n", frame->message_type, frame->payload_length); break; } } /* -- server mode ----------------------------------------------------------- */ static void server_on_frame(struct Transport_Conn *conn, struct Transport_Frame *frame, void *ud) { (void)conn; (void)ud; decode_and_print(frame); free(frame->payload); } static void server_on_connect(struct Transport_Conn *conn, void *ud) { (void)conn; (void)ud; printf("client connected\n"); } static void server_on_disconnect(struct Transport_Conn *conn, void *ud) { (void)conn; (void)ud; printf("client disconnected\n"); } static int run_server(uint16_t port) { struct Transport_Server_Config cfg = { .port = port, .max_connections = 8, .max_payload = 16 * 1024 * 1024, .on_frame = server_on_frame, .on_connect = server_on_connect, .on_disconnect = server_on_disconnect, }; struct Transport_Server *server; struct App_Error e = transport_server_create(&server, &cfg); if (!APP_IS_OK(e)) { app_error_print(&e); return 1; } e = transport_server_start(server); if (!APP_IS_OK(e)) { app_error_print(&e); return 1; } printf("listening on port %u\n", port); pause(); return 0; } /* -- client mode ----------------------------------------------------------- */ static void client_on_frame(struct Transport_Conn *conn, struct Transport_Frame *frame, void *ud) { (void)conn; (void)ud; decode_and_print(frame); free(frame->payload); } static void client_on_disconnect(struct Transport_Conn *conn, void *ud) { (void)conn; (void)ud; printf("disconnected\n"); } static int run_client(const char *host, uint16_t port) { struct Transport_Conn *conn; struct App_Error e = transport_connect(&conn, host, port, 16 * 1024 * 1024, client_on_frame, client_on_disconnect, NULL); if (!APP_IS_OK(e)) { app_error_print(&e); return 1; } printf("connected — sending STREAM_OPEN request\n"); e = proto_write_stream_open(conn, /*request_id=*/1, /*stream_id=*/0, PROTO_FORMAT_MJPEG, 0, PROTO_ORIGIN_DEVICE_NATIVE); if (!APP_IS_OK(e)) { app_error_print(&e); return 1; } printf("sent STREAM_OPEN; waiting for response (ctrl-c to exit)\n"); pause(); return 0; } /* -- usage ----------------------------------------------------------------- */ static void usage(void) { fprintf(stderr, "usage: protocol_cli --server [port]\n" " protocol_cli --client host port\n" "\n" " --server [port] listen and decode incoming protocol frames\n" " (default port 8000)\n" " --client host port connect, send STREAM_OPEN, decode responses\n"); } int main(int argc, char **argv) { if (argc < 2) { usage(); return 1; } if (strcmp(argv[1], "--server") == 0) { uint16_t port = 8000; if (argc >= 3) { port = (uint16_t)atoi(argv[2]); } return run_server(port); } if (strcmp(argv[1], "--client") == 0) { if (argc < 4) { usage(); return 1; } uint16_t port = (uint16_t)atoi(argv[3]); return run_client(argv[2], port); } usage(); return 1; }