feat(presence-detect): back off while another app holds the camera
Before each tick, check whether another process already has the real camera open (fuser, which sees same-user holders without root — every camera app runs as us). If so, skip the read entirely for that tick: attempting would be pointless (we'd get EBUSY) and a simultaneous open could make the other app fail. A camera in use is proof the user is present, so mark presence and wait it out, touching the camera zero times for the whole duration another app holds it — instead of poking it every tick and racing the app for access. V4L2 has no way to be notified that another app wants the device, so this can't preempt an in-progress read; it just guarantees we never contend while another app is already streaming. Falls through to a normal read (and the Python helper's exit-3 busy path) if fuser isn't installed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>main
parent
83687764d8
commit
c2fa8d3e75
|
|
@ -63,6 +63,17 @@ _camera_id() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# True if any process OTHER than this daemon currently holds the real camera
|
||||||
|
# open — a video call, howdy, etc. `fuser` lists same-user holders without root
|
||||||
|
# (every camera app runs as us), and we only ever call this when we aren't
|
||||||
|
# reading the camera ourselves, so any holder is another app. If fuser isn't
|
||||||
|
# installed we return false: the tick then just tries the camera and the Python
|
||||||
|
# helper's busy/in-use path (exit 3) still covers an already-held device.
|
||||||
|
_camera_in_use() {
|
||||||
|
command -v fuser >/dev/null 2>&1 || return 1
|
||||||
|
fuser -s "/dev/video${1}" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
# Prints current CPU-or-RAM usage as a fraction (0..1): the higher of the
|
# Prints current CPU-or-RAM usage as a fraction (0..1): the higher of the
|
||||||
# core-count-normalized 1-minute load average and the used-RAM fraction. Load
|
# core-count-normalized 1-minute load average and the used-RAM fraction. Load
|
||||||
# average is a cheap, sampling-free proxy for "CPU busy" — no extra measurement
|
# average is a cheap, sampling-free proxy for "CPU busy" — no extra measurement
|
||||||
|
|
@ -147,10 +158,21 @@ while true; do
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEVICE="$(_camera_id)"
|
DEVICE="$(_camera_id)"
|
||||||
|
now="$(date +%s)"
|
||||||
|
|
||||||
|
# Back-off: if another app already holds the camera (a video call, howdy,
|
||||||
|
# ...), don't even try to read it — attempting would be pointless (we'd just
|
||||||
|
# get EBUSY) and a simultaneous open could make the app itself fail. A camera
|
||||||
|
# in use is proof you're present, so mark presence and wait it out instead.
|
||||||
|
if _camera_in_use "$DEVICE"; then
|
||||||
|
LAST_MOTION="$now"; touch "$PRESENCE_FLAG"; _start_inhibit
|
||||||
|
sleep "$(_next_interval)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
# Run the OpenCV motion detector; stderr suppressed to keep the journal clean.
|
# Run the OpenCV motion detector; stderr suppressed to keep the journal clean.
|
||||||
python3 "$PYTHON_DETECT" "$DEVICE" 2>/dev/null
|
python3 "$PYTHON_DETECT" "$DEVICE" 2>/dev/null
|
||||||
rc=$?
|
rc=$?
|
||||||
now="$(date +%s)"
|
|
||||||
case $rc in
|
case $rc in
|
||||||
# Motion (0) or camera busy/in use (3): user is present. rc=3 means
|
# Motion (0) or camera busy/in use (3): user is present. rc=3 means
|
||||||
# another app (a video call, howdy) is holding the camera, which is
|
# another app (a video call, howdy) is holding the camera, which is
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue