use anyhow::{Result, anyhow};
use std::sync::RwLock;
use codec::frame::VideoCodec;
use codec::pixel_format::{
Av1SequenceHeader, H264SpsInfo, HevcSpsInfo, parse_av1_sequence_header, parse_h264_sps,
parse_hevc_sps,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RungCodecInvariant {
Av1(Av1Invariant),
H26x(H26xInvariant),
}
impl RungCodecInvariant {
pub(super) fn describe_diff(&self, other: &Self) -> String {
if self == other {
return String::new();
}
match (self, other) {
(RungCodecInvariant::Av1(a), RungCodecInvariant::Av1(b)) => a.describe_diff(b),
_ => format!("rung={self:?}, this worker={other:?}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct H26xInvariant {
pub profile_idc: u8,
pub level_idc: u8,
pub chroma_format_idc: u8,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub width: u32,
pub height: u32,
}
impl H26xInvariant {
fn from_h264(sps: &H264SpsInfo) -> Self {
Self {
profile_idc: sps.profile_idc,
level_idc: sps.level_idc,
chroma_format_idc: sps.chroma_format_idc,
bit_depth_luma: sps.bit_depth_luma,
bit_depth_chroma: sps.bit_depth_chroma,
width: sps.width.unwrap_or(0),
height: sps.height.unwrap_or(0),
}
}
fn from_h265(sps: &HevcSpsInfo) -> Self {
Self {
profile_idc: sps.profile_idc,
level_idc: sps.level_idc,
chroma_format_idc: sps.chroma_format_idc,
bit_depth_luma: sps.bit_depth_luma,
bit_depth_chroma: sps.bit_depth_chroma,
width: sps.width.unwrap_or(0),
height: sps.height.unwrap_or(0),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Av1Invariant {
pub seq_profile: u8,
pub seq_level_idx_0: u8,
pub seq_tier_0: u8,
pub bit_depth: u8,
pub monochrome: bool,
pub chroma_subsampling_x: bool,
pub chroma_subsampling_y: bool,
pub color_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
pub color_range: bool,
pub max_frame_width_minus1: u32,
pub max_frame_height_minus1: u32,
pub still_picture: bool,
}
impl Av1Invariant {
pub fn from_sequence_header(sh: &Av1SequenceHeader) -> Self {
Self {
seq_profile: sh.seq_profile,
seq_level_idx_0: sh.seq_level_idx_0,
seq_tier_0: sh.seq_tier_0,
bit_depth: sh.bit_depth,
monochrome: sh.monochrome,
chroma_subsampling_x: sh.chroma_subsampling_x,
chroma_subsampling_y: sh.chroma_subsampling_y,
color_primaries: sh.color_primaries,
transfer_characteristics: sh.transfer_characteristics,
matrix_coefficients: sh.matrix_coefficients,
color_range: sh.color_range,
max_frame_width_minus1: sh.max_frame_width_minus1,
max_frame_height_minus1: sh.max_frame_height_minus1,
still_picture: sh.still_picture,
}
}
fn describe_diff(&self, other: &Self) -> String {
let mut diffs = Vec::new();
macro_rules! diff_field {
($field:ident) => {
if self.$field != other.$field {
diffs.push(format!(
"{}: rung={:?}, this worker={:?}",
stringify!($field),
self.$field,
other.$field
));
}
};
}
diff_field!(seq_profile);
diff_field!(seq_level_idx_0);
diff_field!(seq_tier_0);
diff_field!(bit_depth);
diff_field!(monochrome);
diff_field!(chroma_subsampling_x);
diff_field!(chroma_subsampling_y);
diff_field!(color_primaries);
diff_field!(transfer_characteristics);
diff_field!(matrix_coefficients);
diff_field!(color_range);
diff_field!(max_frame_width_minus1);
diff_field!(max_frame_height_minus1);
diff_field!(still_picture);
diffs.join("; ")
}
}
#[derive(Debug)]
pub enum InvariantCheck {
SetByThisWorker,
Matched,
Mismatched { diff: String },
}
pub fn validate_or_set_rung_invariant(
rung_idx: usize,
gpu_vendor: Option<codec::gpu::GpuVendor>,
slot: &RwLock<Option<RungCodecInvariant>>,
first_packet: &[u8],
codec: VideoCodec,
) -> Result<InvariantCheck> {
let observed = match codec {
VideoCodec::Av1 => {
let parsed = parse_av1_sequence_header(first_packet).ok_or_else(|| {
anyhow!(
"rung {} (vendor {:?}): could not parse AV1 sequence header from first \
encoded packet; encoder did not emit OBU_SEQUENCE_HEADER as required for \
segment alignment",
rung_idx,
gpu_vendor,
)
})?;
RungCodecInvariant::Av1(Av1Invariant::from_sequence_header(&parsed))
}
VideoCodec::H264 => {
let sps = parse_h264_sps(first_packet).ok_or_else(|| {
anyhow!(
"rung {} (vendor {:?}): could not parse H.264 SPS from first encoded packet; \
encoder did not emit an SPS NAL on the first IDR",
rung_idx,
gpu_vendor,
)
})?;
RungCodecInvariant::H26x(H26xInvariant::from_h264(&sps))
}
VideoCodec::H265 => {
let sps = parse_hevc_sps(first_packet).ok_or_else(|| {
anyhow!(
"rung {} (vendor {:?}): could not parse H.265 SPS from first encoded packet; \
encoder did not emit an SPS NAL on the first IRAP",
rung_idx,
gpu_vendor,
)
})?;
RungCodecInvariant::H26x(H26xInvariant::from_h265(&sps))
}
};
if let Some(existing) = &*slot.read().unwrap() {
if existing == &observed {
return Ok(InvariantCheck::Matched);
}
return Ok(InvariantCheck::Mismatched {
diff: existing.describe_diff(&observed),
});
}
let mut w = slot.write().unwrap();
match &*w {
Some(existing) if existing != &observed => Ok(InvariantCheck::Mismatched {
diff: existing.describe_diff(&observed),
}),
Some(_) => Ok(InvariantCheck::Matched),
None => {
tracing::info!(
rung_idx,
gpu_vendor = ?gpu_vendor,
?codec,
invariant = ?observed,
"rung codec invariant captured from first worker"
);
*w = Some(observed);
Ok(InvariantCheck::SetByThisWorker)
}
}
}