mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 02:00:06 +02:00
- Programs: ft-camd (the camera broker), ft-hands (the tracker), and ft-handreplay and ft-ringplay for recordings, built by hands/build.sh into hands/build/ with one Makefile. The first build fetches ncnn at frame-hands' pinned tag and builds it with the same options. - ft-camd gets its privileges from file capabilities (CAP_SYS_PTRACE, CAP_PERFMON, CAP_DAC_READ_SEARCH) that hands/run.sh install sets with sudo, and drops them once set up. It still works under sudo. It runs on the host, linked statically, as frametop-camd.service. ft-hands runs in the dev container as frametop-hands.service. Both start and stop with SteamVR. - Files move to /run/user/UID/frametop/ (cam-ring, hands, gestures), not $XDG_RUNTIME_DIR, which a terminal in the Frametop desktop has its own of. SIGUSR1 recordings go to ~/.local/share/frametop/hands. - The calibration is read through /run/host in the container. - Settings: HANDS_SWAP_SIDES and HANDS_CPUS in frametop.conf. - install.sh offers hand tracking as an optional last step. - The container gets jsoncpp-devel, glibc-static, and NumPy and OpenCV for the Python tools. - tools/ring.py reads the ring, and models/NOTICE credits the Apache-2.0 models. Checked: ft-handreplay gives identical summaries and byte-identical depth dumps to frame-hands' fh-replay on both 2026-09-29 recordings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
4.2 KiB
C
88 lines
4.2 KiB
C
/*
|
|
* fhring - the shared-memory frame ring ft-camd writes and trackers read.
|
|
*
|
|
* One file, /run/user/UID/frametop/cam-ring (FH_RING_NAME in the user's runtime
|
|
* folder; the folder is private to the user), holds a header, then for each camera
|
|
* a few slots, each a slot header followed by the image rows packed tightly
|
|
* (stride == width for 8-bit mono). Only complete, bright frames are published.
|
|
*
|
|
* Writer, for frame n of a camera: slot = n % nslots
|
|
* slot.seq = 2n+1; write slot fields and pixels; slot.seq = 2n+2; cam.latest = n
|
|
* Reader:
|
|
* n = cam.latest; read slot.seq, expect 2n+2; copy; re-read slot.seq; if it
|
|
* changed the copy is torn, retry with the new latest.
|
|
*
|
|
* All multi-byte fields are little-endian; offsets are fixed so Python can read
|
|
* them with struct (tools/ring.py mirrors this file).
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#define FH_RING_MAGIC "FHRING01"
|
|
#define FH_RING_VERSION 1
|
|
#define FH_RING_MAX_CAMS 8
|
|
#define FH_RING_SLOTS 4
|
|
#define FH_RING_NAME "frametop/cam-ring" /* in /run/user/UID */
|
|
|
|
enum {
|
|
FH_FMT_GREY8 = 0,
|
|
};
|
|
|
|
enum {
|
|
FH_CAM_DARK = 1u << 0, /* the near-black exposures between this node's */
|
|
/* normal frames (ft-camd --with-dark) */
|
|
FH_CAM_COLOR = 1u << 1, /* an Arcturus color camera's luma, downscaled */
|
|
/* (ft-camd --with-color). Not synced with the */
|
|
/* mono cameras, and capture_ns is on its own */
|
|
/* clock: line it up with them by dqbuf_ns */
|
|
};
|
|
|
|
typedef struct {
|
|
char sensor[32]; /* media entity, e.g. "og01a1b 4-0060" */
|
|
char name[32]; /* calibration name if known, else sensor slug */
|
|
int32_t node; /* N of /dev/videoN */
|
|
uint32_t format; /* FH_FMT_* */
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint32_t stride; /* bytes per row in the ring */
|
|
uint32_t nslots;
|
|
uint64_t slot_offset; /* file offset of slot 0 */
|
|
uint64_t slot_bytes; /* slot header + image, 64-byte aligned */
|
|
volatile uint64_t latest; /* newest published frame number, 0 = none yet */
|
|
uint64_t published; /* frames published */
|
|
uint64_t dropped; /* dark, stale or torn frames not published */
|
|
uint32_t flags; /* FH_CAM_* */
|
|
uint8_t reserved[28];
|
|
} fh_ring_cam_t; /* 160 bytes */
|
|
|
|
typedef struct {
|
|
volatile uint64_t seq; /* 2n+1 while frame n is written, 2n+2 when done */
|
|
uint64_t frame; /* n */
|
|
uint64_t capture_ns; /* V4L2 timestamp (camera clock) */
|
|
uint64_t dqbuf_ns; /* CLOCK_MONOTONIC when XRService dequeued it */
|
|
uint64_t publish_ns; /* CLOCK_MONOTONIC when the copy finished */
|
|
uint32_t v4l2_seq; /* V4L2 sequence number */
|
|
float mean; /* mean luma on a sparse grid */
|
|
uint8_t reserved[16];
|
|
} fh_ring_slot_t; /* 64 bytes, image follows */
|
|
|
|
typedef struct {
|
|
char magic[8]; /* FH_RING_MAGIC */
|
|
uint32_t version;
|
|
uint32_t header_bytes; /* sizeof(fh_ring_hdr_t) */
|
|
uint32_t ncams;
|
|
uint32_t reserved0;
|
|
uint64_t file_bytes;
|
|
int64_t writer_pid;
|
|
volatile uint64_t heartbeat_ns; /* CLOCK_MONOTONIC, refreshed at least every 0.2 s */
|
|
uint8_t reserved[16];
|
|
fh_ring_cam_t cams[FH_RING_MAX_CAMS];
|
|
} fh_ring_hdr_t;
|
|
|
|
static_assert(sizeof(fh_ring_cam_t) == 160, "fh_ring_cam_t layout");
|
|
static_assert(sizeof(fh_ring_slot_t) == 64, "fh_ring_slot_t layout");
|
|
static_assert(sizeof(fh_ring_hdr_t) == 64 + 160 * FH_RING_MAX_CAMS, "fh_ring_hdr_t layout");
|