|
| 1 | +# SPDX-License-Identifier: Apache-2.0 |
| 2 | +"""Asset-optional video-level quality and container regression checks.""" |
| 3 | + |
| 4 | +from __future__ import annotations |
| 5 | + |
| 6 | +import json |
| 7 | +import os |
| 8 | +from pathlib import Path |
| 9 | +import subprocess |
| 10 | + |
| 11 | +import numpy as np |
| 12 | +import pytest |
| 13 | + |
| 14 | + |
| 15 | +def _video_summary(path: Path) -> dict: |
| 16 | + probe = subprocess.run( |
| 17 | + [ |
| 18 | + "ffprobe", "-v", "error", "-select_streams", "v:0", "-count_frames", |
| 19 | + "-show_entries", "stream=codec_name,width,height,avg_frame_rate,nb_read_frames", |
| 20 | + "-of", "json", str(path), |
| 21 | + ], |
| 22 | + check=True, |
| 23 | + capture_output=True, |
| 24 | + text=True, |
| 25 | + ) |
| 26 | + stream = json.loads(probe.stdout)["streams"][0] |
| 27 | + width, height = int(stream["width"]), int(stream["height"]) |
| 28 | + raw = subprocess.run( |
| 29 | + ["ffmpeg", "-v", "error", "-i", str(path), "-f", "rawvideo", "-pix_fmt", "gray", "-"], |
| 30 | + check=True, |
| 31 | + capture_output=True, |
| 32 | + ).stdout |
| 33 | + frames = np.frombuffer(raw, dtype=np.uint8).reshape(-1, height, width) |
| 34 | + means = frames.mean(axis=(1, 2)) |
| 35 | + return { |
| 36 | + "codec": stream["codec_name"], |
| 37 | + "width": width, |
| 38 | + "height": height, |
| 39 | + "fps": stream["avg_frame_rate"], |
| 40 | + "frames": int(stream["nb_read_frames"]), |
| 41 | + "decoded_frames": len(frames), |
| 42 | + "mean": float(frames.mean()), |
| 43 | + "std": float(frames.std()), |
| 44 | + "black_frame_count": int((means < 1.0).sum()), |
| 45 | + "frame_mean_std": float(means.std()), |
| 46 | + } |
| 47 | + |
| 48 | + |
| 49 | +def test_helios_quality_candidate_has_valid_384x640_video(): |
| 50 | + candidate = os.environ.get("HELIOS_QUALITY_CANDIDATE") |
| 51 | + if not candidate: |
| 52 | + pytest.skip("Set HELIOS_QUALITY_CANDIDATE to run the real-video quality gate") |
| 53 | + path = Path(candidate) |
| 54 | + if not path.is_file(): |
| 55 | + pytest.skip(f"Quality candidate does not exist: {path}") |
| 56 | + summary = _video_summary(path) |
| 57 | + assert summary["codec"] in {"h264", "hevc", "av1"} |
| 58 | + assert (summary["width"], summary["height"]) == (640, 384) |
| 59 | + assert summary["fps"] == "24/1" |
| 60 | + assert summary["frames"] == summary["decoded_frames"] == 33 |
| 61 | + assert summary["std"] > 5.0 |
| 62 | + assert summary["black_frame_count"] == 0 |
| 63 | + |
| 64 | + |
| 65 | +def test_helios_quality_candidate_is_stable_against_reference(): |
| 66 | + candidate = os.environ.get("HELIOS_QUALITY_CANDIDATE") |
| 67 | + reference = os.environ.get("HELIOS_QUALITY_REFERENCE") |
| 68 | + if not candidate or not reference: |
| 69 | + pytest.skip("Set HELIOS_QUALITY_CANDIDATE and HELIOS_QUALITY_REFERENCE for comparison") |
| 70 | + candidate_summary = _video_summary(Path(candidate)) |
| 71 | + reference_summary = _video_summary(Path(reference)) |
| 72 | + assert abs(candidate_summary["mean"] - reference_summary["mean"]) < 35.0 |
| 73 | + assert abs(candidate_summary["std"] - reference_summary["std"]) < 35.0 |
| 74 | + assert abs(candidate_summary["frame_mean_std"] - reference_summary["frame_mean_std"]) < 25.0 |
0 commit comments