Files
cuframes/libcuframes/src/utils.c
T
gx bd7fd95fef
build / cmake build (CUDA 12.4, Ubuntu 22.04) (pull_request) Successful in 1m37s
build / ffmpeg filter patch (out-of-tree) (pull_request) Successful in 1m21s
feat(libcuframes): packet ring buffer implementation (v0.2 Step 2)
Реализация encoded packet ring per docs/protocol.md §10.

Files:
- internal.h: cuframes_pkt_slot_t (64b packed), cuframes_pkt_header_t
  (0x1040 fixed header), cuframes_pkt_ring_t handle, constants for
  default sizing, packet flags, helper inline functions for slot/data
  pointer arithmetic.
- packet_ring.c (new, ~290 LOC): create/open/publish/read/destroy.
  Stale recovery симметрично frames SHM (pid liveness check). Seqlock
  pattern для subscriber защиты от overrun mid-read (post-check seq
  после copy). Wraparound memcpy helpers для variable-length data ring.
- utils.c: cuframes_internal_pkt_shm_name helper + strerror entries.
- cuframes.h: 4 новых error codes (PACKET_OVERSIZED, NO_PACKET_RING,
  NO_CODEC_PARAMS, PACKET_OVERRUN).
- CMakeLists.txt: src/packet_ring.c в sources.

API внутренний (cuframes_internal_pkt_ring_*) — publicly exposed
функции будут в Step 3 (cuframes.h API extension).

Связано: #2 (v0.2), PR #4 (draft).
2026-05-19 16:11:42 +01:00

163 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Utility functions: error strings, frame size calc, timestamps. */
#include "internal.h"
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
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;
}