为采集帧绑定主机时间与来源信息

采集时间随图像穿过处理队列,并修正未读帧的丢帧计数。通过采集与流水线针对性测试。
This commit is contained in:
ykcheungab
2026-10-11 12:48:34 +08:00
parent 0784eacf51
commit 120b8c2cf6
7 changed files with 256 additions and 40 deletions
+83
View File
@@ -199,6 +199,89 @@ def test_capture_source_read_and_status():
assert cap.release_count == 1
def test_file_read_binds_frame_sequence_and_host_clocks():
cap = FakeCap([_frame(7), _frame(8)])
src = CaptureSource(
CaptureConfig(source="sample.mp4", reconnect=ReconnectPolicy(enabled=False)),
factory=lambda cfg: (cap, "FAKE"),
)
src.open()
before = time.perf_counter_ns()
first = src.read_raw_stamped()
second = src.read_raw_stamped()
after = time.perf_counter_ns()
assert first is not None and second is not None
assert (first.sequence, second.sequence) == (1, 2)
assert (int(first.image[0, 0, 0]), int(second.image[0, 0, 0])) == (7, 8)
assert before <= first.host_mono_ns <= second.host_mono_ns <= after
assert first.host_utc_ns > 0
assert first.media_time_ms == 0.0
src.close()
def test_background_read_keeps_its_own_stamp_when_newer_frame_arrives():
cap = FakeCap([_frame(i) for i in range(10)])
src = CaptureSource(
CaptureConfig(source=0, reconnect=ReconnectPolicy(enabled=False)),
factory=lambda cfg: (cap, "FAKE"),
)
src.open()
src.start_background()
try:
first = src.read_latest_stamped(wait_s=1.0)
assert first is not None
deadline = time.perf_counter() + 1.0
second = first
while second.sequence == first.sequence and time.perf_counter() < deadline:
second = src.read_latest_stamped(wait_s=0.01)
assert second is not None and second.sequence > first.sequence
assert int(first.image[0, 0, 0]) == min(first.sequence - 1, 9)
assert int(second.image[0, 0, 0]) == min(second.sequence - 1, 9)
assert first.host_mono_ns <= second.host_mono_ns
assert src.status.last_seq >= 2
finally:
src.close()
def test_background_drop_count_only_increases_for_unread_replacements():
class StepCap(FakeCap):
def __init__(self):
super().__init__([_frame(i) for i in range(4)])
self.allow = threading.Semaphore(0)
def read(self):
if not self.allow.acquire(timeout=0.1):
return False, None
return super().read()
cap = StepCap()
src = CaptureSource(
CaptureConfig(source=0, reconnect=ReconnectPolicy(enabled=False)),
factory=lambda cfg: (cap, "FAKE"),
)
src.open()
src.start_background()
def wait_seq(seq):
deadline = time.perf_counter() + 2.0
while src.status.last_seq < seq and time.perf_counter() < deadline:
time.sleep(0.001)
assert src.status.last_seq >= seq
try:
cap.allow.release()
wait_seq(1)
assert src.read_latest_stamped().sequence == 1
cap.allow.release()
wait_seq(2)
assert src.status.dropped_frames == 0
cap.allow.release()
wait_seq(3)
assert src.status.dropped_frames == 1
finally:
src.close()
def test_auto_reconnect_after_failed_reads():
first = FakeCap([_frame(1)], fail_after=1)
second = FakeCap([_frame(9)])