/* Utility functions: error strings, frame size calc, timestamps. */ #include "internal.h" #include #include #include #include #include #include const char *cuframes_version_string(void) { static char buf[32]; snprintf(buf, sizeof(buf), "%d.%d.%d", CUFRAMES_VERSION_MAJOR, CUFRAMES_VERSION_MINOR, CUFRAMES_VERSION_PATCH); return buf; } uint32_t cuframes_protocol_version(void) { return CUFRAMES_PROTOCOL_V1; } const char *cuframes_strerror(int err) { switch (err) { case CUFRAMES_OK: return "ok"; case CUFRAMES_ERR_INVALID_ARG: return "invalid argument"; case CUFRAMES_ERR_OUT_OF_MEMORY: return "out of memory"; case CUFRAMES_ERR_CUDA: return "cuda error"; case CUFRAMES_ERR_IO: return "I/O error (socket/mmap/eventfd)"; case CUFRAMES_ERR_NOT_FOUND: return "publisher not found"; case CUFRAMES_ERR_ALREADY_EXISTS: return "publisher/subscriber-name already exists"; case CUFRAMES_ERR_TIMEOUT: return "timeout"; case CUFRAMES_ERR_PROTOCOL: return "wire protocol mismatch"; case CUFRAMES_ERR_DISCONNECTED: return "disconnected"; case CUFRAMES_ERR_FORMAT: return "unsupported format or size mismatch"; case CUFRAMES_ERR_WOULD_BLOCK: return "would block"; case CUFRAMES_ERR_TOO_MANY: return "too many subscribers (max 32)"; case CUFRAMES_ERR_PACKET_OVERSIZED: return "packet exceeds max_packet_size"; case CUFRAMES_ERR_NO_PACKET_RING: return "publisher has no packet ring"; case CUFRAMES_ERR_NO_CODEC_PARAMS: return "codec extradata not set by publisher"; case CUFRAMES_ERR_PACKET_OVERRUN: return "packet ring overrun — resync on keyframe"; case CUFRAMES_ERR_INTERNAL: return "internal error (please report)"; default: return "unknown error"; } } int64_t cuframes_now_ns(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec; } int cuframes_calc_frame_size(cuframes_format_t format, int32_t width, int32_t height, size_t *size_out, int32_t *pitch_y_out, int32_t *pitch_uv_out) { return cuframes_internal_calc_size(format, width, height, size_out, pitch_y_out, pitch_uv_out); } /* ─── Internal helpers ────────────────────────────────────────────────── */ int cuframes_internal_validate_key(const char *key) { if (!key) return CUFRAMES_ERR_INVALID_ARG; size_t len = strlen(key); if (len < 1 || len > CUFRAMES_MAX_KEY_LEN) return CUFRAMES_ERR_INVALID_ARG; for (size_t i = 0; i < len; ++i) { char c = key[i]; if (!(isalnum((unsigned char)c) || c == '_' || c == '-')) { return CUFRAMES_ERR_INVALID_ARG; } } return CUFRAMES_OK; } int cuframes_internal_socket_path(const char *key, char *out, size_t out_size) { int r = cuframes_internal_validate_key(key); if (r != CUFRAMES_OK) return r; int n = snprintf(out, out_size, "%s/%s.sock", CUFRAMES_RUNTIME_DIR, key); if (n < 0 || (size_t)n >= out_size) return CUFRAMES_ERR_INVALID_ARG; return CUFRAMES_OK; } int cuframes_internal_shm_name(const char *key, char *out, size_t out_size) { int r = cuframes_internal_validate_key(key); if (r != CUFRAMES_OK) return r; int n = snprintf(out, out_size, "%s%s", CUFRAMES_SHM_PREFIX, key); if (n < 0 || (size_t)n >= out_size) return CUFRAMES_ERR_INVALID_ARG; return CUFRAMES_OK; } int cuframes_internal_pkt_shm_name(const char *key, char *out, size_t out_size) { int r = cuframes_internal_validate_key(key); if (r != CUFRAMES_OK) return r; int n = snprintf(out, out_size, "%s%s%s", CUFRAMES_SHM_PREFIX, key, CUFRAMES_PKT_SHM_SUFFIX); if (n < 0 || (size_t)n >= out_size) return CUFRAMES_ERR_INVALID_ARG; return CUFRAMES_OK; } int cuframes_internal_ensure_runtime_dir(void) { if (mkdir(CUFRAMES_RUNTIME_DIR, 0755) == 0) return CUFRAMES_OK; if (errno == EEXIST) return CUFRAMES_OK; CUFRAMES_LOG_ERROR("cannot create %s: %s", CUFRAMES_RUNTIME_DIR, strerror(errno)); return CUFRAMES_ERR_IO; } int cuframes_internal_pid_alive(pid_t pid) { if (pid <= 0) return 0; if (kill(pid, 0) == 0) return 1; return (errno == EPERM) ? 1 : 0; } /* Возвращает размер frame'а + pitch'ы. Pitch aligned 256. * NV12: Y w×h + UV w×h/2 (interleaved) — оба с тем же pitch_y. * YUV420P: Y w×h + U w/2×h/2 + V w/2×h/2. * RGB/BGR: 3 bytes per pixel, single plane. * RGBA: 4 bytes per pixel. * GRAYSCALE: 1 byte per pixel. */ int cuframes_internal_calc_size(cuframes_format_t format, int32_t w, int32_t h, size_t *size_out, int32_t *pitch_y_out, int32_t *pitch_uv_out) { if (w <= 0 || h <= 0) return CUFRAMES_ERR_INVALID_ARG; int32_t py = 0, puv = 0; size_t total = 0; /* round up to 256-byte alignment for CUDA */ #define ALIGN256(x) (((x) + 255u) & ~255u) switch (format) { case CUFRAMES_FORMAT_NV12: py = (int32_t)ALIGN256((uint32_t)w); puv = py; total = (size_t)py * (size_t)h + (size_t)puv * (size_t)(h / 2); break; case CUFRAMES_FORMAT_YUV420P: py = (int32_t)ALIGN256((uint32_t)w); puv = (int32_t)ALIGN256((uint32_t)(w / 2)); total = (size_t)py * (size_t)h + 2 * (size_t)puv * (size_t)(h / 2); break; case CUFRAMES_FORMAT_RGB: case CUFRAMES_FORMAT_BGR: py = (int32_t)ALIGN256((uint32_t)(w * 3)); total = (size_t)py * (size_t)h; break; case CUFRAMES_FORMAT_RGBA: py = (int32_t)ALIGN256((uint32_t)(w * 4)); total = (size_t)py * (size_t)h; break; case CUFRAMES_FORMAT_GRAYSCALE: py = (int32_t)ALIGN256((uint32_t)w); total = (size_t)py * (size_t)h; break; default: return CUFRAMES_ERR_FORMAT; } if (size_out) *size_out = total; if (pitch_y_out) *pitch_y_out = py; if (pitch_uv_out) *pitch_uv_out = puv; return CUFRAMES_OK; }