fix(pose): clear iphone hands on stream drop
iphone_usb_source.py: after stream ends, clear persons_hands_iphone, persons_hands_chirality, and persons_hands (when write_hands=True) under state.lock() to prevent renderer display freeze. test_iphone_usb_reconnect.py: add test_hands_cleared_on_stream_drop to verify lists are empty after simulated USB disconnect.
This commit is contained in:
@@ -239,6 +239,14 @@ class IphoneUSBSource:
|
||||
# USB hiccup ends iter_frames; we re-establish :7000).
|
||||
with self._lock:
|
||||
self._frame = None
|
||||
# Clear stale hand state so the renderer doesn't freeze on the
|
||||
# last known hands after a stream drop.
|
||||
if self.state is not None:
|
||||
with self.state.lock():
|
||||
self.state.persons_hands_iphone = []
|
||||
self.state.persons_hands_chirality = []
|
||||
if self._write_hands:
|
||||
self.state.persons_hands = []
|
||||
self._opened = False
|
||||
sock = None # forces the reconnect at the loop top
|
||||
if not self._stop.is_set():
|
||||
|
||||
@@ -4,6 +4,7 @@ race (app not ready when data_only_viz starts), not only a mid-session drop."""
|
||||
import time
|
||||
|
||||
import data_only_viz.iphone_usb_source as m
|
||||
from data_only_viz.state import PoseKp, State
|
||||
|
||||
|
||||
def test_start_keeps_retrying_when_no_device(monkeypatch):
|
||||
@@ -23,3 +24,42 @@ def test_start_keeps_retrying_when_no_device(monkeypatch):
|
||||
"start() should keep retrying; connect_device called %d time(s)" % calls["n"])
|
||||
finally:
|
||||
src.release()
|
||||
|
||||
|
||||
def test_hands_cleared_on_stream_drop(monkeypatch):
|
||||
"""After a USB stream drop, persons_hands* lists must be emptied so the
|
||||
renderer does not freeze on the last known hand positions."""
|
||||
|
||||
class FakeSock:
|
||||
def close(self): pass
|
||||
def shutdown(self, *a): pass
|
||||
|
||||
connect_calls = [0]
|
||||
|
||||
def fake_connect():
|
||||
connect_calls[0] += 1
|
||||
if connect_calls[0] == 1:
|
||||
return FakeSock() # initial connection succeeds
|
||||
return None # reconnect fails; release() will interrupt the wait
|
||||
|
||||
def fake_iter_frames(sock):
|
||||
raise OSError("stream dropped") # simulate immediate disconnect
|
||||
|
||||
state = State()
|
||||
dummy_hand = [PoseKp(x=0.5, y=0.5, z=0.0, c=1.0)] * 21
|
||||
with state.lock():
|
||||
state.persons_hands = [dummy_hand]
|
||||
state.persons_hands_iphone = [dummy_hand]
|
||||
state.persons_hands_chirality = [1]
|
||||
|
||||
monkeypatch.setattr(m, "connect_device", fake_connect)
|
||||
monkeypatch.setattr(m, "iter_frames", fake_iter_frames)
|
||||
|
||||
src = m.IphoneUSBSource(state=state, write_hands=True)
|
||||
src.start()
|
||||
src.release() # joins thread; ensures clearing happened before we check
|
||||
|
||||
with state.lock():
|
||||
assert state.persons_hands == [], "persons_hands not cleared on drop"
|
||||
assert state.persons_hands_iphone == [], "persons_hands_iphone not cleared on drop"
|
||||
assert state.persons_hands_chirality == [], "persons_hands_chirality not cleared on drop"
|
||||
|
||||
Reference in New Issue
Block a user