kvm/internal/audio/c/ipc_protocol.h

211 lines
7.1 KiB
C

/*
* JetKVM Audio IPC Protocol
*
* Wire protocol for Unix domain socket communication between main process
* and audio subprocesses. This protocol is 100% compatible with the Go
* implementation in internal/audio/ipc_*.go
*
* CRITICAL: All multi-byte integers use LITTLE-ENDIAN byte order.
*/
#ifndef JETKVM_IPC_PROTOCOL_H
#define JETKVM_IPC_PROTOCOL_H
#include <stdint.h>
#include <sys/types.h>
// ============================================================================
// PROTOCOL CONSTANTS
// ============================================================================
// Magic numbers (ASCII representation when read as little-endian)
#define IPC_MAGIC_OUTPUT 0x4A4B4F55 // "JKOU" - JetKVM Output (device → browser)
#define IPC_MAGIC_INPUT 0x4A4B4D49 // "JKMI" - JetKVM Microphone Input (browser → device)
// Message types (matches Go UnifiedMessageType enum)
#define IPC_MSG_TYPE_OPUS_FRAME 0 // Audio frame data (Opus encoded)
#define IPC_MSG_TYPE_CONFIG 1 // Basic audio config (12 bytes)
#define IPC_MSG_TYPE_OPUS_CONFIG 2 // Complete Opus config (36 bytes)
#define IPC_MSG_TYPE_STOP 3 // Shutdown signal
#define IPC_MSG_TYPE_HEARTBEAT 4 // Keep-alive ping
#define IPC_MSG_TYPE_ACK 5 // Acknowledgment
// Size constraints
#define IPC_HEADER_SIZE 17 // Fixed header size
#define IPC_MAX_FRAME_SIZE 4096 // Maximum payload size (matches Go Config.MaxFrameSize)
// Socket paths
#define IPC_SOCKET_OUTPUT "/var/run/audio_output.sock"
#define IPC_SOCKET_INPUT "/var/run/audio_input.sock"
// ============================================================================
// WIRE FORMAT STRUCTURES
// ============================================================================
/**
* IPC message header (17 bytes, little-endian)
*
* Byte layout:
* [0-3] magic uint32_t LE Magic number (0x4A4B4F55 or 0x4A4B4D49)
* [4] type uint8_t Message type (0-5)
* [5-8] length uint32_t LE Payload size in bytes
* [9-16] timestamp int64_t LE Unix nanoseconds (time.Now().UnixNano())
* [17+] data uint8_t[] Variable payload
*
* CRITICAL: Must use __attribute__((packed)) to prevent padding.
*/
typedef struct __attribute__((packed)) {
uint32_t magic; // Magic number (LE)
uint8_t type; // Message type
uint32_t length; // Payload length in bytes (LE)
int64_t timestamp; // Unix nanoseconds (LE)
} ipc_header_t;
/**
* Basic audio configuration (12 bytes)
* Message type: IPC_MSG_TYPE_CONFIG
*
* All fields are uint32_t little-endian.
*/
typedef struct __attribute__((packed)) {
uint32_t sample_rate; // Samples per second (e.g., 48000)
uint32_t channels; // Number of channels (e.g., 2 for stereo)
uint32_t frame_size; // Samples per frame (e.g., 960)
} ipc_config_t;
/**
* Complete Opus encoder/decoder configuration (36 bytes)
* Message type: IPC_MSG_TYPE_OPUS_CONFIG
*
* All fields are uint32_t little-endian.
* Note: Negative values (like signal_type=-1000) are stored as two's complement uint32.
*/
typedef struct __attribute__((packed)) {
uint32_t sample_rate; // Samples per second (48000)
uint32_t channels; // Number of channels (2)
uint32_t frame_size; // Samples per frame (960)
uint32_t bitrate; // Bits per second (96000)
uint32_t complexity; // Encoder complexity 0-10 (1=fast, 10=best quality)
uint32_t vbr; // Variable bitrate: 0=disabled, 1=enabled
uint32_t signal_type; // Signal type: -1000=auto, 3001=music, 3002=voice
uint32_t bandwidth; // Bandwidth: 1101=narrowband, 1102=mediumband, 1103=wideband
uint32_t dtx; // Discontinuous transmission: 0=disabled, 1=enabled
} ipc_opus_config_t;
/**
* Complete IPC message (header + payload)
*/
typedef struct {
ipc_header_t header;
uint8_t *data; // Dynamically allocated payload (NULL if length=0)
} ipc_message_t;
// ============================================================================
// FUNCTION DECLARATIONS
// ============================================================================
/**
* Read a complete IPC message from socket.
*
* This function:
* 1. Reads exactly 17 bytes (header)
* 2. Validates magic number
* 3. Validates length <= IPC_MAX_FRAME_SIZE
* 4. Allocates and reads payload if length > 0
* 5. Stores result in msg->header and msg->data
*
* @param sock Socket file descriptor
* @param msg Output message (data will be malloc'd if length > 0)
* @param expected_magic Expected magic number (IPC_MAGIC_OUTPUT or IPC_MAGIC_INPUT)
* @return 0 on success, -1 on error
*
* CALLER MUST FREE msg->data if non-NULL!
*/
int ipc_read_message(int sock, ipc_message_t *msg, uint32_t expected_magic);
/**
* Write a complete IPC message to socket.
*
* This function writes header + payload atomically (if possible via writev).
* Sets timestamp to current time.
*
* @param sock Socket file descriptor
* @param magic Magic number (IPC_MAGIC_OUTPUT or IPC_MAGIC_INPUT)
* @param type Message type (IPC_MSG_TYPE_*)
* @param data Payload data (can be NULL if length=0)
* @param length Payload length in bytes
* @return 0 on success, -1 on error
*/
int ipc_write_message(int sock, uint32_t magic, uint8_t type,
const uint8_t *data, uint32_t length);
/**
* Parse Opus configuration from message data.
*
* @param data Payload data (must be exactly 36 bytes)
* @param length Payload length (must be 36)
* @param config Output Opus configuration
* @return 0 on success, -1 if length != 36
*/
int ipc_parse_opus_config(const uint8_t *data, uint32_t length, ipc_opus_config_t *config);
/**
* Parse basic audio configuration from message data.
*
* @param data Payload data (must be exactly 12 bytes)
* @param length Payload length (must be 12)
* @param config Output audio configuration
* @return 0 on success, -1 if length != 12
*/
int ipc_parse_config(const uint8_t *data, uint32_t length, ipc_config_t *config);
/**
* Free message resources.
*
* @param msg Message to free (frees msg->data if non-NULL)
*/
void ipc_free_message(ipc_message_t *msg);
/**
* Get current time in nanoseconds (Unix epoch).
*
* @return Time in nanoseconds (compatible with Go time.Now().UnixNano())
*/
int64_t ipc_get_time_ns(void);
/**
* Create Unix domain socket server.
*
* This function:
* 1. Creates socket with AF_UNIX, SOCK_STREAM
* 2. Removes existing socket file
* 3. Binds to specified path
* 4. Listens with backlog=1 (single client)
*
* @param socket_path Path to Unix socket (e.g., "/var/run/audio_output.sock")
* @return Socket fd on success, -1 on error
*/
int ipc_create_server(const char *socket_path);
/**
* Accept client connection with automatic retry.
*
* Blocks until client connects or error occurs.
*
* @param server_sock Server socket fd from ipc_create_server()
* @return Client socket fd on success, -1 on error
*/
int ipc_accept_client(int server_sock);
/**
* Helper: Read exactly N bytes from socket (loops until complete or error).
*
* @param sock Socket file descriptor
* @param buf Output buffer
* @param len Number of bytes to read
* @return 0 on success, -1 on error
*/
int ipc_read_full(int sock, void *buf, size_t len);
#endif // JETKVM_IPC_PROTOCOL_H