Files
rerun-io--rerun/rerun_py/tests/unit/test_dataloader_decoder_helpers.py
2026-07-13 13:05:14 +08:00

385 lines
15 KiB
Python

"""Tests for the low-level helpers in `rerun.experimental.dataloader._decoders`."""
from __future__ import annotations
import numpy as np
import pyarrow as pa
import pytest
from rerun.experimental.dataloader import Field
from rerun.experimental.dataloader._decoders import (
VideoFrameDecoder,
_avcc_to_annex_b,
_flatten_blob,
_h264_annex_b_has_idr,
_hevc_annex_b_has_irap,
_is_annex_b,
_is_av1_keyframe_packet,
_unwrap_to_numpy,
)
from rerun.experimental.dataloader._utils import _field_index_range, _prior_keyframe
@pytest.mark.parametrize(
("data", "expected"),
[
(b"\x00\x00\x00\x01\xab\xcd", True), # 4-byte start code
(b"\x00\x00\x01\xab\xcd", True), # 3-byte short start code
(b"\x00\x00\x00\x01", True), # exactly the start code
(b"\x00\x00\x01", True), # exactly the short start code
(b"\x00\x00\x02\xab", False),
(b"\xab\xcd\xef\x01", False),
(b"", False),
(b"\x00", False),
(b"\x00\x00", False),
],
)
def test_is_annex_b(data: bytes, expected: bool) -> None:
assert _is_annex_b(data) is expected
def _make_obu_header(obu_type: int) -> int:
"""Return a byte whose OBU-type field (bits [3:6]) matches *obu_type*."""
return (obu_type & 0xF) << 3
@pytest.mark.parametrize(
("obu_type", "expected"),
[
(1, True), # OBU_SEQUENCE_HEADER
(2, True), # OBU_TEMPORAL_DELIMITER
(3, False), # OBU_FRAME_HEADER
(6, False), # OBU_FRAME
(0, False),
(15, False),
],
)
def test_is_av1_keyframe_packet(obu_type: int, expected: bool) -> None:
sample = bytes([_make_obu_header(obu_type), 0x00, 0x00])
assert _is_av1_keyframe_packet(sample) is expected
def test_is_av1_keyframe_packet_empty() -> None:
assert _is_av1_keyframe_packet(b"") is False
def test_is_av1_keyframe_packet_ignores_low_bits() -> None:
# Low three bits (extension/has-size/reserved) must not affect detection.
header = _make_obu_header(1) | 0b111
assert _is_av1_keyframe_packet(bytes([header])) is True
def _avcc_encode(nal_units: list[bytes], nal_length_size: int = 4) -> bytes:
out = bytearray()
for unit in nal_units:
out.extend(len(unit).to_bytes(nal_length_size, "big"))
out.extend(unit)
return bytes(out)
def _h264_annex_b(nal_units: list[tuple[int, bytes]], use_4byte: bool = True) -> bytes:
"""Build an Annex B H.264 stream from `(nal_unit_type, payload)` pairs."""
start = b"\x00\x00\x00\x01" if use_4byte else b"\x00\x00\x01"
out = bytearray()
for nal_type, payload in nal_units:
out.extend(start)
# nal_ref_idc=3, forbidden_zero_bit=0
out.append((3 << 5) | (nal_type & 0x1F))
out.extend(payload)
return bytes(out)
def _hevc_annex_b(nal_units: list[tuple[int, bytes]], use_4byte: bool = True) -> bytes:
"""Build an Annex B HEVC stream from `(nal_unit_type, payload)` pairs."""
start = b"\x00\x00\x00\x01" if use_4byte else b"\x00\x00\x01"
out = bytearray()
for nal_type, payload in nal_units:
out.extend(start)
# forbidden_zero_bit=0, layer_id=0, temporal_id_plus1=1
out.append((nal_type & 0x3F) << 1)
out.append(0x01)
out.extend(payload)
return bytes(out)
@pytest.mark.parametrize("use_4byte", [True, False])
def test_h264_annex_b_has_idr_simple(use_4byte: bool) -> None:
sample = _h264_annex_b([(5, b"\xaa\xbb\xcc")], use_4byte=use_4byte)
assert _h264_annex_b_has_idr(sample) is True
def test_h264_annex_b_has_idr_after_sps_pps() -> None:
sample = _h264_annex_b([
(7, b"\x42\xc0\x1f"), # SPS
(8, b"\xce\x38\x80"), # PPS
(5, b"\x88\x84"), # IDR
])
assert _h264_annex_b_has_idr(sample) is True
def test_h264_annex_b_has_idr_after_aud_sei() -> None:
sample = _h264_annex_b([
(9, b"\x10"), # AUD
(6, b"\x01\x80"), # SEI
(5, b"\x88"), # IDR
])
assert _h264_annex_b_has_idr(sample) is True
def test_h264_annex_b_has_idr_p_slice_only() -> None:
sample = _h264_annex_b([(1, b"\xab\xcd\xef")])
assert _h264_annex_b_has_idr(sample) is False
def test_h264_annex_b_has_idr_empty() -> None:
assert _h264_annex_b_has_idr(b"") is False
@pytest.mark.parametrize("use_4byte", [True, False])
def test_hevc_annex_b_has_irap_simple(use_4byte: bool) -> None:
sample = _hevc_annex_b([(19, b"\xaa\xbb\xcc")], use_4byte=use_4byte)
assert _hevc_annex_b_has_irap(sample) is True
@pytest.mark.parametrize("nal_type", [16, 17, 18, 19, 20, 21, 22, 23])
def test_hevc_annex_b_has_irap_all_irap_types(nal_type: int) -> None:
sample = _hevc_annex_b([(nal_type, b"\xaa")])
assert _hevc_annex_b_has_irap(sample) is True
@pytest.mark.parametrize("nal_type", [1, 15, 24, 32, 35]) # TRAIL_R, RSV_VCL_*, just-out-of-IRAP, VPS, AUD
def test_hevc_annex_b_has_irap_non_irap_types(nal_type: int) -> None:
sample = _hevc_annex_b([(nal_type, b"\xaa")])
assert _hevc_annex_b_has_irap(sample) is False
def test_hevc_annex_b_has_irap_after_vps_sps_pps() -> None:
sample = _hevc_annex_b([
(32, b"\xaa"), # VPS
(33, b"\xbb"), # SPS
(34, b"\xcc"), # PPS
(19, b"\xdd"), # IDR_W_RADL
])
assert _hevc_annex_b_has_irap(sample) is True
def test_hevc_annex_b_has_irap_empty() -> None:
assert _hevc_annex_b_has_irap(b"") is False
def test_avcc_to_annex_b_single_unit() -> None:
unit = b"\x67\x42\xc0\x1f"
result = _avcc_to_annex_b(_avcc_encode([unit]))
assert result == b"\x00\x00\x00\x01" + unit
def test_avcc_to_annex_b_multiple_units() -> None:
units = [b"\x67\x42\xc0\x1f", b"\x68\xce\x38\x80", b"\x65\x88\x84"]
result = _avcc_to_annex_b(_avcc_encode(units))
expected = b"".join(b"\x00\x00\x00\x01" + u for u in units)
assert result == expected
def test_avcc_to_annex_b_length_size_2() -> None:
units = [b"\xaa\xbb", b"\xcc\xdd\xee"]
result = _avcc_to_annex_b(_avcc_encode(units, nal_length_size=2), nal_length_size=2)
expected = b"".join(b"\x00\x00\x00\x01" + u for u in units)
assert result == expected
def test_avcc_to_annex_b_truncated_stops_early() -> None:
# Well-formed first unit, then a length that claims more data than is left.
first = b"\x67\x42\xc0"
buf = len(first).to_bytes(4, "big") + first + (0xFF).to_bytes(4, "big") + b"\x00\x01"
result = _avcc_to_annex_b(buf)
assert result == b"\x00\x00\x00\x01" + first
def test_avcc_to_annex_b_empty() -> None:
assert _avcc_to_annex_b(b"") == b""
def test_unwrap_plain_numeric() -> None:
arr = pa.array([1.0, 2.0, 3.0], type=pa.float64())
np.testing.assert_array_equal(_unwrap_to_numpy(arr), np.array([1.0, 2.0, 3.0]))
def test_unwrap_list_float() -> None:
arr = pa.array([[1.0, 2.0], [3.0, 4.0, 5.0]], type=pa.list_(pa.float64()))
# Non-ragged requirement isn't enforced — the function returns the flattened values.
np.testing.assert_array_equal(_unwrap_to_numpy(arr), np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
def test_unwrap_fixed_size_list() -> None:
arr = pa.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], type=pa.list_(pa.float32(), 3))
result = _unwrap_to_numpy(arr)
np.testing.assert_array_equal(result, np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=np.float32))
def test_unwrap_nested_list() -> None:
arr = pa.array([[[1.0, 2.0], [3.0]], [[4.0]]], type=pa.list_(pa.list_(pa.float64())))
np.testing.assert_array_equal(_unwrap_to_numpy(arr), np.array([1.0, 2.0, 3.0, 4.0]))
def test_unwrap_result_is_writeable() -> None:
# Torch requires writeable arrays downstream.
arr = pa.array([1, 2, 3], type=pa.int32())
result = _unwrap_to_numpy(arr)
assert result.flags.writeable
def test_flatten_blob_list_of_list_uint8_single_row() -> None:
arr = pa.array([[[1, 2, 3, 4]]], type=pa.list_(pa.list_(pa.uint8())))
result = _flatten_blob(arr, 0)
np.testing.assert_array_equal(result, np.array([1, 2, 3, 4], dtype=np.uint8))
def test_flatten_blob_list_of_list_uint8_concatenates_inner_rows() -> None:
# Row 0 has two inner lists, which should be concatenated.
arr = pa.array(
[[[1, 2], [3]], [[10, 20, 30]]],
type=pa.list_(pa.list_(pa.uint8())),
)
np.testing.assert_array_equal(_flatten_blob(arr, 0), np.array([1, 2, 3], dtype=np.uint8))
np.testing.assert_array_equal(_flatten_blob(arr, 1), np.array([10, 20, 30], dtype=np.uint8))
def test_flatten_blob_list_of_binary() -> None:
arr = pa.array([[b"hello"], [b"world!"]], type=pa.list_(pa.binary()))
np.testing.assert_array_equal(_flatten_blob(arr, 0), np.frombuffer(b"hello", dtype=np.uint8))
np.testing.assert_array_equal(_flatten_blob(arr, 1), np.frombuffer(b"world!", dtype=np.uint8))
def test_flatten_blob_list_of_large_binary() -> None:
arr = pa.array([[b"abc"], [b"defghi"]], type=pa.list_(pa.large_binary()))
np.testing.assert_array_equal(_flatten_blob(arr, 0), np.frombuffer(b"abc", dtype=np.uint8))
np.testing.assert_array_equal(_flatten_blob(arr, 1), np.frombuffer(b"defghi", dtype=np.uint8))
def test_flatten_blob_binary_respects_offsets() -> None:
# The binary-path reads raw offsets, make sure subsequent rows don't leak into row 0.
arr = pa.array(
[[b"AAAA"], [b"BB"], [b"CCCCCC"]],
type=pa.list_(pa.binary()),
)
for row, expected in enumerate([b"AAAA", b"BB", b"CCCCCC"]):
np.testing.assert_array_equal(_flatten_blob(arr, row), np.frombuffer(expected, dtype=np.uint8))
def test_video_frame_decoder_returns_none_without_keyframe() -> None:
"""`decode` returns `None` when the prefetched window contains no keyframe."""
p_slice_only = _h264_annex_b([(1, b"\xab\xcd\xef\x01\x02\x03")])
raw = pa.chunked_array([pa.array([[p_slice_only]], type=pa.list_(pa.binary()))])
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=2)
assert decoder.decode(raw, 0, "seg") is None
def test_video_frame_decoder_is_keyframe_h264() -> None:
p_slice = _h264_annex_b([(1, b"\xab\xcd")])
idr = _h264_annex_b([(5, b"\x88")])
decoder = VideoFrameDecoder(codec="h264")
assert decoder._is_keyframe(p_slice) is False
assert decoder._is_keyframe(idr) is True
def test_video_frame_decoder_is_keyframe_hevc() -> None:
non_irap = _hevc_annex_b([(1, b"\xaa")])
irap = _hevc_annex_b([(19, b"\xaa")])
decoder = VideoFrameDecoder(codec="hevc")
assert decoder._is_keyframe(non_irap) is False
assert decoder._is_keyframe(irap) is True
def test_video_frame_decoder_is_keyframe_unknown_codec_returns_none() -> None:
assert VideoFrameDecoder(codec="vp9")._is_keyframe(b"\x00") is None
def test_video_frame_decoder_has_keyframe_h264() -> None:
p_slice = _h264_annex_b([(1, b"\xab\xcd")])
idr = _h264_annex_b([(7, b"\x42\xc0\x1f"), (8, b"\xce\x38"), (5, b"\x88")])
decoder = VideoFrameDecoder(codec="h264")
assert decoder._has_keyframe([]) is False
assert decoder._has_keyframe([p_slice]) is False
assert decoder._has_keyframe([p_slice, idr]) is True
def test_video_frame_decoder_has_keyframe_unknown_codec_trusts_decoder() -> None:
# Unknown codec: `_is_keyframe` returns None and `_has_keyframe` returns True so
# failures surface from the decoder rather than being swallowed as cold-start.
assert VideoFrameDecoder(codec="vp9")._has_keyframe([b"\x00"]) is True
def test_video_frame_decoder_derives_keyframe_path() -> None:
decoder = VideoFrameDecoder(codec="h264")
assert decoder.prior_keyframe_path("/camera:VideoStream:sample") == "/camera:VideoStream:is_keyframe"
assert (
decoder.prior_keyframe_path("/robot/cam_left:VideoStream:sample") == "/robot/cam_left:VideoStream:is_keyframe"
)
def test_video_frame_decoder_keyframe_path_no_separator() -> None:
# Defensive: a path with no `:` is non-canonical; return None rather than guessing.
assert VideoFrameDecoder(codec="h264").prior_keyframe_path("/just_an_entity") is None
def test_field_index_range_window_beats_anchor_and_heuristic() -> None:
field = Field(path="/camera:VideoStream:sample", decode=VideoFrameDecoder(codec="h264"), window=(-3, 5))
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=10)
# Anchor and heuristic must lose to the explicit window.
assert _field_index_range(100, field, decoder, prior_keyframe=42) == (97, 105)
def test_field_index_range_anchor_beats_heuristic_integer() -> None:
field = Field(path="/camera:VideoStream:sample", decode=VideoFrameDecoder(codec="h264"))
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=10)
assert _field_index_range(100, field, decoder, prior_keyframe=87) == (87, 100)
def test_field_index_range_anchor_beats_heuristic_timestamp() -> None:
field = Field(path="/camera:VideoStream:sample", decode=VideoFrameDecoder(codec="h264"))
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=30, fps_estimate=30.0)
target = np.datetime64(1_000_000_000, "ns")
result = _field_index_range(target, field, decoder, prior_keyframe=500_000_000)
assert result is not None
lo, hi = result
assert lo == np.datetime64(500_000_000, "ns")
assert hi == target
def test_field_index_range_falls_back_to_heuristic_when_anchor_missing() -> None:
# Simulates "no prior keyframe yet in this segment" — the prefetcher drops the
# entry and the field falls back to the decoder's heuristic context_range.
field = Field(path="/camera:VideoStream:sample", decode=VideoFrameDecoder(codec="h264"))
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=10)
assert _field_index_range(100, field, decoder, prior_keyframe=None) == (90, 100)
def test_field_index_range_default_kwarg_is_none() -> None:
# Existing call sites that don't pass `prior_keyframe` keep the same behavior.
field = Field(path="/camera:VideoStream:sample", decode=VideoFrameDecoder(codec="h264"))
decoder = VideoFrameDecoder(codec="h264", keyframe_interval=5)
assert _field_index_range(20, field, decoder) == (15, 20)
def test_prior_keyframe_none_or_empty_returns_none() -> None:
assert _prior_keyframe(None, 100) is None
assert _prior_keyframe(np.array([], dtype=np.int64), 100) is None
def test_prior_keyframe_target_before_first_returns_none() -> None:
assert _prior_keyframe(np.array([50, 100, 150], dtype=np.int64), 49) is None
def test_prior_keyframe_target_equals_keyframe_returns_keyframe() -> None:
assert _prior_keyframe(np.array([50, 100, 150], dtype=np.int64), 100) == 100
def test_prior_keyframe_target_between_returns_largest_leq() -> None:
kfs = np.array([50, 100, 150], dtype=np.int64)
assert _prior_keyframe(kfs, 99) == 50
assert _prior_keyframe(kfs, 149) == 100
def test_prior_keyframe_target_after_last_returns_last() -> None:
assert _prior_keyframe(np.array([50, 100, 150], dtype=np.int64), 9999) == 150