feat(v1): compose temporal fall event pipeline

This commit is contained in:
ila
2026-07-21 11:57:22 +08:00
parent 906e06565e
commit f03662bf5a
16 changed files with 554 additions and 34 deletions
+34 -5
View File
@@ -2,7 +2,7 @@ import cv2
import numpy as np
import pytest
from v1.video_source import SourceStatus, VideoSource
from v1.video_source import SourceMode, SourceStatus, VideoSource
def _write_sample_video(path):
@@ -18,7 +18,7 @@ def _write_sample_video(path):
def test_file_source_emits_monotonic_timestamps(tmp_path):
sample_video = tmp_path / "sample.avi"
_write_sample_video(sample_video)
source = VideoSource(sample_video, reconnect=False)
source = VideoSource(sample_video, mode=SourceMode.REPLAY)
first = source.read(now=10.0)
second = source.read(now=10.1)
@@ -31,7 +31,7 @@ def test_file_source_emits_monotonic_timestamps(tmp_path):
def test_missing_source_returns_error_state_without_frame(tmp_path):
source = VideoSource(tmp_path / "missing.avi", reconnect=False)
source = VideoSource(tmp_path / "missing.avi", mode=SourceMode.REPLAY)
packet = source.read(now=1.0)
@@ -77,7 +77,7 @@ def test_reconnect_waits_then_reopens_with_bounded_backoff():
source = VideoSource(
"demo-source",
reconnect=True,
mode=SourceMode.STREAM,
retry_initial_seconds=2.0,
retry_max_seconds=2.0,
capture_factory=lambda _source: captures.pop(0),
@@ -119,7 +119,9 @@ class _NegativeFirstTimestampCapture:
def test_negative_first_timestamp_falls_back_to_frame_index_and_fps():
source = VideoSource(
"demo-source", reconnect=False, capture_factory=lambda _source: _NegativeFirstTimestampCapture()
"demo-source",
mode=SourceMode.REPLAY,
capture_factory=lambda _source: _NegativeFirstTimestampCapture(),
)
first = source.read(now=10.0)
@@ -127,3 +129,30 @@ def test_negative_first_timestamp_falls_back_to_frame_index_and_fps():
assert first.timestamp_monotonic == 0.0
assert second.timestamp_monotonic == pytest.approx(1.0 / 30.0)
def test_replay_source_reports_eof_without_restarting(tmp_path):
sample_video = tmp_path / "sample.avi"
_write_sample_video(sample_video)
source = VideoSource(sample_video, mode=SourceMode.REPLAY)
for timestamp in (0.0, 0.1, 0.2):
assert source.read(now=timestamp).status is SourceStatus.CONNECTED
eof = source.read(now=0.3)
still_eof = source.read(now=0.4)
assert eof.status is SourceStatus.EOF
assert still_eof.status is SourceStatus.EOF
def test_stream_source_uses_read_clock_instead_of_capture_timestamp():
source = VideoSource(
"rtsp://not-a-real-address",
mode=SourceMode.STREAM,
capture_factory=lambda _source: _OpenCapture(),
)
packet = source.read(now=42.0)
assert packet.status is SourceStatus.CONNECTED
assert packet.timestamp_monotonic == 42.0