mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 07:00:14 +02:00
Eye tracker: ft-eyes sleeps until the next frame is due
ft-eyes looked for new frames about 1,000 times a second: each pass of its loop asked the control socket with a non-blocking recvfrom (a BlockingIOError nearly every time), read both cameras' counters, and slept 1 ms. The frames come every 11.1 ms per camera, and only as counters in ft-eyegrab's shared memory, so there's no fd to wait on. Now each pass ends in select() on the control socket, with a timeout until 2 ms before the next frame of either camera is due (from when its last one was seen), then every 1 ms until it comes. A command wakes it at once. A camera with no frame for 0.1 s (headset off, grabber idle) isn't waited for, and with both stopped it looks every 20 ms. Waiting for the frame grabber's file uses the same select, 0.2 s at a time, instead of sleeping through commands. A new frame is still seen within about 1 ms of when it lands. On a synthetic share at 90 Hz per camera, on a heavily loaded headset (load average 23, so ft-eyes rarely sat idle), its waits went from 178 to 81 a second; unloaded, the old loop's 1 ms sleeps add up to about 1,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a11cef49ba
commit
7851ce90cc
1 file changed
+19
-3
+19
-3
@@ -50,6 +50,7 @@ import json
|
||||
import math
|
||||
import mmap
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
@@ -87,6 +88,15 @@ GAP = 3.0 # s without frames: the headset was off, and may sit diffe
|
||||
CLICK_BEFORE = 0.3 # a click's frames: the 300 ms before it (like the probe's fixation)
|
||||
CALIB_MIN = 15 # frames an eye needs in a calibration dot's window
|
||||
CALIB_SPREAD = 4.0 # px: more than this and the eye moved during the dot
|
||||
# Waiting for frames. They come only as a counter in shared memory, so there's nothing to block
|
||||
# on: sleep until a camera's next frame is due, then look every POLL. ft-eyegrab passes each one
|
||||
# on within a ms or two of when its camera starts the one after, so they arrive 11.1 ms apart,
|
||||
# give or take that.
|
||||
PERIOD = 1 / 90 # s between a camera's frames
|
||||
EARLY = 0.002 # start looking this long before a frame is due
|
||||
POLL = 0.001 # then this often until it comes
|
||||
STALLED = 0.1 # s without a frame: that camera stopped (headset off), and isn't waited for
|
||||
IDLE_POLL = 0.02 # how often to look while both are stopped
|
||||
|
||||
|
||||
class Cams:
|
||||
@@ -357,7 +367,7 @@ def wait_for_cams(sock, tracker, want):
|
||||
return Cams()
|
||||
except (OSError, ValueError, RuntimeError):
|
||||
serve(sock, tracker)
|
||||
time.sleep(0.2)
|
||||
select.select([sock], [], [], 0.2)
|
||||
|
||||
|
||||
def serve(sock, tracker):
|
||||
@@ -420,6 +430,7 @@ def main():
|
||||
print("ft-eyes: frames found, tracking", file=sys.stderr, flush=True)
|
||||
seen = [cams.count(0), cams.count(1)]
|
||||
report = time.monotonic()
|
||||
arrived = [0.0, 0.0] # when each camera's newest frame was seen (monotonic)
|
||||
while True:
|
||||
serve(sock, tracker)
|
||||
want()
|
||||
@@ -429,6 +440,7 @@ def main():
|
||||
if n == seen[c]:
|
||||
continue
|
||||
seen[c] = n
|
||||
arrived[c] = time.monotonic()
|
||||
got = cams.frame(c, n - 1) # only the newest: never fall behind
|
||||
if got:
|
||||
eyes[c].feed(*got)
|
||||
@@ -447,8 +459,6 @@ def main():
|
||||
shifts += [float(v) for v in e.shift.value]
|
||||
pupils += [e.gaze[3], e.gaze[4]] if fresh else [math.nan, math.nan]
|
||||
out.write(latest, (yaw, pitch), flags, per, shifts, pupils)
|
||||
else:
|
||||
time.sleep(0.001)
|
||||
now = time.monotonic()
|
||||
if now - report >= 5:
|
||||
if verbose:
|
||||
@@ -466,6 +476,12 @@ def main():
|
||||
print("ft-eyes: frames went away; waiting", file=sys.stderr, flush=True)
|
||||
cams = wait_for_cams(sock, tracker, want)
|
||||
seen = [cams.count(0), cams.count(1)]
|
||||
# Until the next frame is due (a command on the socket wakes us sooner).
|
||||
wait = IDLE_POLL
|
||||
for c in (0, 1):
|
||||
if now - arrived[c] < STALLED:
|
||||
wait = min(wait, max(arrived[c] + PERIOD - EARLY - now, POLL))
|
||||
select.select([sock], [], [], wait)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user