fix(lab): make recorded replay seek-safe

This commit is contained in:
DCCONSTRUCTIONS
2026-08-24 02:15:30 +03:00
parent 7400fe2fd7
commit 992c5a8b74
12 changed files with 1096 additions and 287 deletions
+188 -4
View File
@@ -41,6 +41,7 @@ from k1link.web.session_api import (
RecordedPerceptionRequest,
RecordedPointColorsRequest,
ReplayRequest,
_recorded_media_manifest_document,
build_session_router,
)
@@ -128,6 +129,11 @@ def make_recorded_h264_fixture(
timescale: int = 1_000,
sample_duration: int = 500,
base_decode_time: int = 0,
trex_default_sample_flags: int = 0,
tfhd_default_sample_flags: int | None = None,
first_sample_flags: int | None = None,
sample_flags: int | None = None,
fragment_sample_count: int = 1,
) -> tuple[bytes, bytes]:
def box(box_type: bytes, payload: bytes = b"") -> bytes:
return (8 + len(payload)).to_bytes(4, "big") + box_type + payload
@@ -156,14 +162,32 @@ def make_recorded_h264_fixture(
track_id.to_bytes(4, "big")
+ (1).to_bytes(4, "big")
+ sample_duration.to_bytes(4, "big")
+ b"\x00" * 8,
+ b"\x00" * 4
+ trex_default_sample_flags.to_bytes(4, "big"),
)
avcc = box(b"avcC", b"\x01\x64\x00\x28")
init = box(b"ftyp", b"isom") + box(b"moov", trak + box(b"mvex", trex) + avcc)
tfhd = full_box(b"tfhd", track_id.to_bytes(4, "big"), flags=0x020000)
tfhd_flags = 0x020000
tfhd_payload = track_id.to_bytes(4, "big")
if tfhd_default_sample_flags is not None:
tfhd_flags |= 0x000020
tfhd_payload += tfhd_default_sample_flags.to_bytes(4, "big")
tfhd = full_box(b"tfhd", tfhd_payload, flags=tfhd_flags)
tfdt = full_box(b"tfdt", base_decode_time.to_bytes(4, "big"))
trun = full_box(b"trun", (1).to_bytes(4, "big"))
trun_flags = 0
trun_payload = fragment_sample_count.to_bytes(4, "big")
if first_sample_flags is not None:
trun_flags |= 0x000004
trun_payload += first_sample_flags.to_bytes(4, "big")
if sample_flags is not None:
trun_flags |= 0x000400
trun_payload += sample_flags.to_bytes(4, "big") * fragment_sample_count
trun = full_box(
b"trun",
trun_payload,
flags=trun_flags,
)
fragment = box(b"moof", box(b"traf", tfhd + tfdt + trun)) + box(b"mdat", b"frame")
return init, fragment
@@ -1644,7 +1668,7 @@ def test_session_router_exposes_opaque_recorded_media_manifest_and_ranges(
if_match=f'"sha256:{source["manifest_generation_sha256"]}"',
)
manifest = json.loads(manifest_response.body)
assert manifest["schema_version"] == "missioncore.observation-recorded-media/v3"
assert manifest["schema_version"] == "missioncore.observation-recorded-media/v4"
assert manifest["source_id"] == source["id"]
assert manifest["generation_sha256"] == source["manifest_generation_sha256"]
assert manifest["byte_length"] == source["byte_length"]
@@ -1659,6 +1683,9 @@ def test_session_router_exposes_opaque_recorded_media_manifest_and_ranges(
"timeline_end_seconds": 0.5,
"media_type": 'video/mp4; codecs="avc1.640028"',
"byte_length": len(init) + len(segment),
"segment_count": 1,
"random_access_sequences": [1],
"segment_end_times_seconds": [0.5],
"stream_url": (
f"{source['manifest_url'].removesuffix('/manifest')}/epochs/1/recording.mp4"
f"?generation={generation}"
@@ -1815,6 +1842,136 @@ def test_session_router_exposes_opaque_recorded_media_manifest_and_ranges(
assert confined.value.status_code == 409
def test_recorded_media_persists_and_exposes_random_access_sequences(
tmp_path: Path,
) -> None:
repository = tmp_path / "repo"
sessions = repository / "sessions"
session = make_legacy_session(sessions, "20260716T205632Z_viewer_live")
init, first = make_recorded_h264_fixture()
_, second = make_recorded_h264_fixture(
base_decode_time=500,
tfhd_default_sample_flags=0x00010000,
)
_, third = make_recorded_h264_fixture(
base_decode_time=1_000,
tfhd_default_sample_flags=0x00010000,
first_sample_flags=0,
)
_, fourth = make_recorded_h264_fixture(
base_decode_time=1_500,
sample_flags=0x00010000,
)
writer = CameraArchiveWriter(session, "sensor.camera.private-left", 1)
writer.append("init", init)
for sequence, fragment in enumerate((first, second, third, fourth), start=1):
writer.append(
"media",
fragment,
host_epoch_ns=1_000_000_000 + sequence * 500_000_000,
host_monotonic_ns=2_000_000_000 + sequence * 500_000_000,
)
writer.close()
store = SessionStore(repository, data_dir=tmp_path / "data")
store.reconcile_archive(xgrids_k1_archive_source(sessions))
command = store.prepare_replay(session.name)
artifact = store.list_recorded_media(session.name)[0]
cache_root = tmp_path / "prepared-media"
manifest = RecordedMediaInspector(cache_root).inspect(artifact, command)
assert [segment.random_access for segment in manifest.epochs[0].segments] == [
True,
False,
True,
False,
]
document = _recorded_media_manifest_document(manifest)
assert document["schema_version"] == "missioncore.observation-recorded-media/v4"
assert document["epochs"][0]["segment_count"] == 4
assert document["epochs"][0]["random_access_sequences"] == [1, 3]
assert document["epochs"][0]["segment_end_times_seconds"] == [0.5, 1.0, 1.5, 2.0]
assert document["epochs"][0]["segment_end_times_seconds"][-1] == (
manifest.epochs[0].timeline_end_seconds - manifest.epochs[0].timeline_start_seconds
)
sidecar = json.loads(next(cache_root.glob("*.json")).read_text(encoding="utf-8"))
assert sidecar["schema_version"] == "missioncore.recorded-media-preparation/v3"
assert sidecar["manifest"]["schema_version"] == ("missioncore.observation-recorded-media/v4")
assert [
segment["random_access"] for segment in sidecar["manifest"]["epochs"][0]["segments"]
] == [True, False, True, False]
assert [
segment["end_time_seconds"] for segment in sidecar["manifest"]["epochs"][0]["segments"]
] == [0.5, 1.0, 1.5, 2.0]
restarted = RecordedMediaInspector(cache_root).inspect(artifact, command)
assert [segment.end_time_seconds for segment in restarted.epochs[0].segments] == [
0.5,
1.0,
1.5,
2.0,
]
def test_recorded_media_rejects_ambiguous_trun_sample_flags() -> None:
init, fragment = make_recorded_h264_fixture(
first_sample_flags=0,
sample_flags=0,
)
timing = recorded_media_module._mp4_video_timing(
init,
recorded_media_module._Mp4ParseBudget(),
)
with pytest.raises(SessionIntegrityError, match="sample flags are ambiguous"):
recorded_media_module._mp4_video_fragment_timing(
fragment,
timing,
recorded_media_module._Mp4ParseBudget(),
)
def test_recorded_media_rejects_multi_sample_video_fragment() -> None:
init, fragment = make_recorded_h264_fixture(fragment_sample_count=2)
timing = recorded_media_module._mp4_video_timing(
init,
recorded_media_module._Mp4ParseBudget(),
)
with pytest.raises(SessionIntegrityError, match="exactly one sample"):
recorded_media_module._mp4_video_fragment_timing(
fragment,
timing,
recorded_media_module._Mp4ParseBudget(),
)
def test_recorded_media_rejects_codec_epoch_without_initial_random_access(
tmp_path: Path,
) -> None:
repository = tmp_path / "repo"
sessions = repository / "sessions"
session = make_legacy_session(sessions, "20260716T205632Z_viewer_live")
init, non_random_access = make_recorded_h264_fixture(sample_flags=0x00010000)
writer = CameraArchiveWriter(session, "sensor.camera.private-left", 1)
writer.append("init", init)
writer.append(
"media",
non_random_access,
host_epoch_ns=1_500_000_000,
host_monotonic_ns=2_500_000_000,
)
writer.close()
store = SessionStore(repository, data_dir=tmp_path / "data")
store.reconcile_archive(xgrids_k1_archive_source(sessions))
with pytest.raises(SessionIntegrityError, match="does not begin with a random-access"):
RecordedMediaInspector().inspect(
store.list_recorded_media(session.name)[0],
store.prepare_replay(session.name),
)
def test_replay_exposes_generation_bound_perception_video_with_native_ranges(
tmp_path: Path,
) -> None:
@@ -2373,6 +2530,10 @@ def test_recorded_media_preparation_sidecar_reuses_and_rebuilds_generation(
sidecar = sidecars[0]
assert stat.S_IMODE(sidecar.stat().st_mode) == 0o600
assert str(session) not in sidecar.read_text(encoding="utf-8")
prepared = json.loads(sidecar.read_text(encoding="utf-8"))
assert prepared["schema_version"] == "missioncore.recorded-media-preparation/v3"
assert prepared["manifest"]["epochs"][0]["segments"][0]["random_access"] is True
assert prepared["manifest"]["epochs"][0]["segments"][0]["end_time_seconds"] == 0.5
def forbidden_reparse(*_args: object, **_kwargs: object) -> None:
raise AssertionError("restart must reuse the prepared media sidecar")
@@ -2383,6 +2544,7 @@ def test_recorded_media_preparation_sidecar_reuses_and_rebuilds_generation(
restarted = RecordedMediaInspector(cache_root).inspect(artifact, command)
assert restarted.generation_sha256 == initial.generation_sha256
assert restarted.timeline_end_seconds == initial.timeline_end_seconds
assert [segment.end_time_seconds for segment in restarted.epochs[0].segments] == [0.5]
original_read_manifest = recorded_media_module._read_manifest
reparses = 0
@@ -2392,6 +2554,28 @@ def test_recorded_media_preparation_sidecar_reuses_and_rebuilds_generation(
reparses += 1
return original_read_manifest(*args, **kwargs)
old_body = json.loads(json.dumps(prepared))
old_body.pop("checksum_sha256")
old_body["schema_version"] = "missioncore.recorded-media-preparation/v2"
old_body["manifest"]["schema_version"] = "missioncore.observation-recorded-media/v3"
for epoch in old_body["manifest"]["epochs"]:
for old_segment in epoch["segments"]:
old_segment.pop("end_time_seconds")
old_checksum = hashlib.sha256(recorded_media_module._canonical_json(old_body)).hexdigest()
sidecar.write_bytes(
recorded_media_module._canonical_json({**old_body, "checksum_sha256": old_checksum})
)
with monkeypatch.context() as context:
context.setattr(recorded_media_module, "_read_manifest", counted_reparse)
migrated = RecordedMediaInspector(cache_root).inspect(artifact, command)
assert migrated.generation_sha256 == initial.generation_sha256
assert reparses == 1
migrated_sidecar = json.loads(sidecar.read_text(encoding="utf-8"))
assert migrated_sidecar["schema_version"] == "missioncore.recorded-media-preparation/v3"
assert migrated_sidecar["manifest"]["epochs"][0]["segments"][0]["random_access"] is True
assert migrated_sidecar["manifest"]["epochs"][0]["segments"][0]["end_time_seconds"] == 0.5
reparses = 0
sidecar.write_bytes(b"{corrupt")
with monkeypatch.context() as context:
context.setattr(recorded_media_module, "_read_manifest", counted_reparse)