Files
spoopyghosty0--frameport/native/venc/sys.c
T
spoopyghosty0 0ae754fa5d Live view: hardware H.264 encoder (fp_venc), x264 stays as fallback
native/venc/fp_venc: own clean-room driver of the Frame's iris encoder (V4L2 stateful encoder interface, written from
native/venc/SPEC.md = kernel docs + values measured on the Frame). Reads SteamVR's headset-view webcam, box-scales and
converts RGB24 to NV12 (NEON, bit-identical scalar reference), CBR H.264 with SPS/PPS on every IDR, one frame per slot
of an even fraction of the panel rate read through DRM (96 Hz -> 32 fps). stdin "k" = keyframe, EOF = stop;
--probe/--selftest. Static freestanding aarch64 binary in artifacts/linux-arm64-bin (reproducible).

livestream: the script probes fp_venc and pipes it into ffmpeg (-c:v copy + AAC), else the old x264 30 fps path; the
helper is synced to ~/.local/share/frameport/bin by sha256 at stream start (no agent change); a new viewer asks for a
keyframe and waits for it; the status line shows the encoder and fps. Tests: hardware/fallback script branches,
keyframe-on-join, helper sync; tests/test_venc.py compares convert.c with a Python reference (native marker).
Not yet run on the device.
2026-10-07 17:50:55 -04:00

132 lines
3.1 KiB
C

/* Process entry and the few libc pieces fp_venc needs (see sys.h). */
#include "sys.h"
/* The kernel starts us with sp -> argc, argv[0..argc-1], NULL, envp..., NULL (16-byte aligned). Clear the frame
* pointer and link register so backtraces end here, then hand sp to C. */
__asm__(".text\n"
".global _start\n"
".type _start,%function\n"
"_start:\n"
" mov x29, #0\n"
" mov x30, #0\n"
" mov x0, sp\n"
" bl start_c\n"
" brk #0\n");
__attribute__((noreturn, used)) void start_c(long *sp)
{
int argc = (int)sp[0];
char **argv = (char **)(sp + 1);
char **envp = argv + argc + 1;
sys_exit(main(argc, argv, envp));
}
/* The compiler may emit calls to these for struct copies/zeroing. Byte loops are enough: the hot paths (picture
* conversion) never use them. -ffreestanding/-fno-builtin keep clang from turning these loops back into calls. */
void *memcpy(void *d, const void *s, size_t n)
{
unsigned char *dp = d;
const unsigned char *sp = s;
while (n--) *dp++ = *sp++;
return d;
}
void *memset(void *d, int c, size_t n)
{
unsigned char *dp = d;
while (n--) *dp++ = (unsigned char)c;
return d;
}
void *memmove(void *d, const void *s, size_t n)
{
unsigned char *dp = d;
const unsigned char *sp = s;
if (dp < sp) {
while (n--) *dp++ = *sp++;
} else {
while (n--) dp[n] = sp[n];
}
return d;
}
size_t str_len(const char *s)
{
size_t n = 0;
while (s[n]) n++;
return n;
}
int str_eq(const char *a, const char *b)
{
while (*a && *a == *b) a++, b++;
return *a == *b;
}
int str_contains(const char *hay, const char *needle)
{
size_t n = str_len(needle);
for (; *hay; hay++) {
size_t i = 0;
while (i < n && hay[i] == needle[i]) i++;
if (i == n) return 1;
}
return n == 0;
}
int parse_uint(const char *s, long *out)
{
long v = 0;
if (!s || !*s) return 0;
for (; *s; s++) {
if (*s < '0' || *s > '9') return 0;
v = v * 10 + (*s - '0');
if (v > 0x7fffffffL) return 0;
}
*out = v;
return 1;
}
int64_t now_ns(void)
{
struct timespec ts = {0, 0};
sys_clock_gettime(1 /* CLOCK_MONOTONIC */, &ts);
return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
}
void write_all(int fd, const void *b, size_t n)
{
const char *p = b;
while (n) {
long r = sys_write(fd, p, n);
if (r == -EINTR || r == -EAGAIN) continue;
if (r <= 0) return;
p += r;
n -= (size_t)r;
}
}
void ls(struct line *l, const char *s)
{
while (*s && l->n < (int)sizeof(l->b) - 1) l->b[l->n++] = *s++;
}
void ln(struct line *l, long v)
{
char t[24];
int i = 0;
unsigned long u = v < 0 ? 0UL - (unsigned long)v : (unsigned long)v;
if (v < 0) ls(l, "-");
do t[i++] = (char)('0' + u % 10); while ((u /= 10) && i < 23);
while (i && l->n < (int)sizeof(l->b) - 1) l->b[l->n++] = t[--i];
}
void lms(struct line *l, int64_t ns)
{
int64_t us = ns / 1000;
ln(l, (long)(us / 1000));
ls(l, ".");
ls(l, us % 1000 < 100 ? (us % 1000 < 10 ? "00" : "0") : "");
ln(l, (long)(us % 1000));
}