dragonflight/services/framecache/src/slot.c

217 lines
6.9 KiB
C
Raw Normal View History

/**
* slot.c Framecache slot lifecycle: create, destroy, open.
*/
#include "slot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define SHM_DIR "/dev/shm/framecache"
#define SEM_PREFIX "/framecache-"
#define SEM_SUFFIX "-write"
/* ── helpers ─────────────────────────────────────────────────────────── */
static void build_paths(const char *slot_id,
char *shm_path, size_t sp_len,
char *sem_name, size_t sn_len)
{
snprintf(shm_path, sp_len, "%s/%s", SHM_DIR, slot_id);
snprintf(sem_name, sn_len, "%s%s%s", SEM_PREFIX, slot_id, SEM_SUFFIX);
}
/* ── server-side: create / destroy ───────────────────────────────────── */
/**
* Create a new slot. Allocates and initialises the shm region.
* Returns handle on success, NULL on error (errno set).
*/
struct fc_slot *fc_slot_create(const char *slot_id,
uint32_t width, uint32_t height,
uint32_t fps_num, uint32_t fps_den,
uint32_t pixel_format,
const char *source_type)
{
char shm_path[128], sem_name[128];
build_paths(slot_id, shm_path, sizeof shm_path, sem_name, sizeof sem_name);
uint32_t frame_size = width * height * 2; /* UYVY422 */
size_t total = fc_slot_shm_size(frame_size);
/* Ensure directory exists */
mkdir(SHM_DIR, 0755);
/* Create shm file */
int fd = open(shm_path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
perror("[framecache] open shm");
return NULL;
}
if (ftruncate(fd, (off_t)total) < 0) {
perror("[framecache] ftruncate");
close(fd);
unlink(shm_path);
return NULL;
}
void *base = mmap(NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) {
perror("[framecache] mmap");
close(fd);
unlink(shm_path);
return NULL;
}
memset(base, 0, total);
/* Initialise header */
fc_header_t *hdr = (fc_header_t *)base;
hdr->magic = FC_MAGIC;
hdr->version = FC_VERSION;
hdr->width = width;
hdr->height = height;
hdr->fps_num = fps_num;
hdr->fps_den = fps_den;
hdr->pixel_format = pixel_format;
hdr->frame_size = frame_size;
hdr->ring_depth = FC_RING_DEPTH;
atomic_store(&hdr->write_cursor, 0);
atomic_store(&hdr->dropped_frames, 0);
strncpy(hdr->source_type, source_type ? source_type : "unknown",
sizeof hdr->source_type - 1);
strncpy(hdr->slot_id, slot_id, sizeof hdr->slot_id - 1);
/* Create semaphore */
sem_unlink(sem_name); /* remove stale */
sem_t *sem = sem_open(sem_name, O_CREAT | O_EXCL, 0666, 0);
if (sem == SEM_FAILED) {
perror("[framecache] sem_open");
munmap(base, total);
close(fd);
unlink(shm_path);
return NULL;
}
struct fc_slot *s = calloc(1, sizeof *s);
if (!s) {
sem_close(sem); sem_unlink(sem_name);
munmap(base, total);
close(fd);
unlink(shm_path);
return NULL;
}
s->shm_fd = fd;
s->base = base;
s->shm_size = total;
s->sem = sem;
strncpy(s->slot_id, slot_id, sizeof s->slot_id - 1);
strncpy(s->shm_path, shm_path, sizeof s->shm_path - 1);
strncpy(s->sem_name, sem_name, sizeof s->sem_name - 1);
fprintf(stderr, "[framecache] slot created: %s (%ux%u %.2ffps %zuMB)\n",
slot_id, width, height,
fps_den ? (double)fps_num / fps_den : 0.0,
total / 1024 / 1024);
return s;
}
/**
* Destroy a slot: unmap, close fd, delete files, free handle.
*/
void fc_slot_destroy(struct fc_slot *s)
{
if (!s) return;
sem_close(s->sem);
sem_unlink(s->sem_name);
munmap(s->base, s->shm_size);
close(s->shm_fd);
unlink(s->shm_path);
fprintf(stderr, "[framecache] slot destroyed: %s\n", s->slot_id);
free(s);
}
/* ── writer: called by ingest bridges ───────────────────────────────── */
/**
* Write one frame into the ring. Never blocks advances write_cursor
* atomically and posts the semaphore. Slow consumers will be skipped.
*/
void fc_slot_write_frame(struct fc_slot *s,
const uint8_t *data, uint32_t size,
uint64_t pts_us)
{
fc_header_t *hdr = (fc_header_t *)s->base;
uint64_t cur = atomic_load_explicit(&hdr->write_cursor, memory_order_relaxed);
fc_frame_t *frame = fc_frame_at(s->base, hdr->frame_size, cur);
frame->pts_us = pts_us;
frame->wall_us = (uint64_t)({ struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; });
frame->size = size < hdr->frame_size ? size : hdr->frame_size;
memcpy(frame->data, data, frame->size);
atomic_store_explicit(&hdr->write_cursor, cur + 1, memory_order_release);
sem_post(s->sem);
}
/* ── client-side open / read / close (also used by capture-manager) ── */
/**
* Open an existing slot for reading.
* Returns NULL if slot not found or header magic mismatch.
*/
struct fc_slot *fc_slot_open(const char *slot_id)
{
char shm_path[128], sem_name[128];
build_paths(slot_id, shm_path, sizeof shm_path, sem_name, sizeof sem_name);
int fd = open(shm_path, O_RDONLY);
if (fd < 0) return NULL;
/* Read header first to get frame_size */
fc_header_t tmp_hdr;
if (pread(fd, &tmp_hdr, sizeof tmp_hdr, 0) != sizeof tmp_hdr) {
close(fd); return NULL;
}
if (tmp_hdr.magic != FC_MAGIC) {
close(fd); return NULL;
}
size_t total = fc_slot_shm_size(tmp_hdr.frame_size);
void *base = mmap(NULL, total, PROT_READ, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) { close(fd); return NULL; }
sem_t *sem = sem_open(sem_name, 0);
if (sem == SEM_FAILED) { munmap(base, total); close(fd); return NULL; }
struct fc_slot *s = calloc(1, sizeof *s);
if (!s) { sem_close(sem); munmap(base, total); close(fd); return NULL; }
s->shm_fd = fd;
s->base = base;
s->shm_size = total;
s->sem = sem;
strncpy(s->slot_id, slot_id, sizeof s->slot_id - 1);
strncpy(s->shm_path, shm_path, sizeof s->shm_path - 1);
strncpy(s->sem_name, sem_name, sizeof s->sem_name - 1);
return s;
}
/**
* Close a client-side slot handle. Does not destroy the slot.
*/
void fc_slot_close(struct fc_slot *s)
{
if (!s) return;
sem_close(s->sem);
munmap(s->base, s->shm_size);
close(s->shm_fd);
free(s);
}