Files

120 lines
3.3 KiB
Python

from __future__ import annotations
from fractions import Fraction
def join_parts(parts: list[str | None]) -> str:
return ", ".join(part for part in parts if part)
def video_parts(
*,
codec: str | None = None,
width: int | None = None,
height: int | None = None,
pixel_format: str | None = None,
bit_depth: int | None = None,
matrix: str | None = None,
bit_rate: int | None = None,
) -> list[str | None]:
return [
codec,
format_size(width, height),
format_chroma(pixel_format),
f"{bit_depth}-bit" if bit_depth is not None else None,
matrix,
format_bitrate(bit_rate),
]
def audio_parts(
*,
codec: str | None = None,
channels: int | None = None,
sample_rate: int | None = None,
bit_rate: int | None = None,
) -> list[str | None]:
return [
codec,
format_channels(channels),
f"{sample_rate} Hz" if sample_rate is not None else None,
format_bitrate(bit_rate),
]
def format_size(width: int | None, height: int | None) -> str | None:
return f"{width}x{height}" if width is not None and height is not None else None
def format_chroma(pixel_format: str | None) -> str | None:
value = (pixel_format or "").lower()
for marker, label in (("420", "4:2:0"), ("422", "4:2:2"), ("444", "4:4:4")):
if marker in value:
return label
if value.startswith(("rgb", "bgr", "gbr")):
return "RGB"
if value.startswith("gray"):
return "gray"
return pixel_format
def chroma_family(pixel_format: str | None) -> str:
value = (pixel_format or "").lower()
if "444" in value:
return "444"
if "422" in value:
return "422"
return "420"
def format_channels(channels: int | None) -> str | None:
if channels is None:
return None
return f"{channels} channel{'s' if channels != 1 else ''}"
def format_bitrate(bit_rate: int | None) -> str | None:
if bit_rate is None:
return None
if bit_rate >= 1_000_000:
return f"{bit_rate / 1_000_000:.3g} Mbit/s"
return f"{bit_rate / 1_000:.3g} kbit/s"
def format_seconds(seconds: float | None) -> str:
return "unknown" if seconds is None else f"{seconds:.3f}s"
def format_frame_count(stream_count: int | None, timestamp_count: int) -> str:
if stream_count is not None and stream_count != timestamp_count:
return f"{stream_count} stream frames, {timestamp_count} timestamped frames"
count = stream_count if stream_count is not None else timestamp_count
return f"{count} frames" if count else "unknown frames"
def rate_value(value: str | None) -> float | None:
if not value:
return None
try:
rate = Fraction(value)
except (ValueError, ZeroDivisionError):
return None
return float(rate) if rate else None
def format_framerate(kind: str, value: str | None) -> str:
rate = rate_value(value)
rate_text = "unknown" if rate is None else f"{rate:.3f} fps"
kind = kind.upper()
return f"{kind}, average {rate_text}" if kind == "VFR" else f"{kind}, {rate_text}"
def matrix_name(matrix: int | None) -> str:
if matrix == 1:
return "Rec709"
if matrix in {5, 6}:
return "Rec601"
if matrix in {9, 10}:
return "Rec2020"
return "unknown" if matrix is None else f"unknown ({matrix})"