use iso_bmff::bitstream::avc::AvcDecoderConfig;
use iso_bmff::bitstream::hevc::HevcDecoderConfig;
use mediaway_common::{Bytes, CodecKind, Rational};
use crate::DecodeError;
#[must_use]
pub(super) const fn is_supported_video_codec(codec: CodecKind) -> bool {
matches!(
codec,
CodecKind::H264
| CodecKind::Hevc
| CodecKind::Vp9
| CodecKind::Av1
| CodecKind::ProRes422Proxy
| CodecKind::ProRes422Lt
| CodecKind::ProRes422
| CodecKind::ProRes422Hq
| CodecKind::ProRes4444
| CodecKind::ProRes4444Xq
)
}
#[must_use]
pub(super) const fn is_prores(codec: CodecKind) -> bool {
matches!(
codec,
CodecKind::ProRes422Proxy
| CodecKind::ProRes422Lt
| CodecKind::ProRes422
| CodecKind::ProRes422Hq
| CodecKind::ProRes4444
| CodecKind::ProRes4444Xq
)
}
#[must_use]
pub(super) const fn requires_extra_data_at_open(codec: CodecKind) -> bool {
matches!(codec, CodecKind::Vp9 | CodecKind::Av1)
}
#[must_use]
pub(super) const fn raw_atom_key(codec: CodecKind) -> Option<&'static str> {
match codec {
CodecKind::Vp9 => Some("vpcC"),
CodecKind::Av1 => Some("av1C"),
_ => None,
}
}
pub(super) const fn validate_parameter_sets(config: &AvcDecoderConfig) -> Result<(), DecodeError> {
if config.nal_length_size != 4 {
return Err(DecodeError::Unsupported);
}
if config.sps.len() != 1 || config.pps.len() != 1 {
return Err(DecodeError::Unsupported);
}
Ok(())
}
pub(super) const fn validate_hevc_parameter_sets(
config: &HevcDecoderConfig,
) -> Result<(), DecodeError> {
if config.nal_length_size != 4 {
return Err(DecodeError::Unsupported);
}
if config.vps.len() != 1 || config.sps.len() != 1 || config.pps.len() != 1 {
return Err(DecodeError::Unsupported);
}
Ok(())
}
#[must_use]
pub(super) fn cmtime_value_from_ticks(ticks: i64, time_base: Rational) -> (i64, i32) {
let num = i64::try_from(time_base.num).unwrap_or(i64::MAX);
let value = ticks.saturating_mul(num);
let timescale = i32::try_from(time_base.den).unwrap_or(i32::MAX);
(value, timescale)
}
#[must_use]
pub(super) fn ticks_from_cmtime_value(value: i64, timescale: i32, time_base: Rational) -> i64 {
if timescale == 0 || time_base.num == 0 {
return 0;
}
let numerator = i128::from(value) * i128::from(time_base.den);
let denominator = i128::from(timescale) * i128::from(time_base.num);
if denominator == 0 {
return 0;
}
let ticks = numerator / denominator;
i64::try_from(ticks).unwrap_or_else(|_| {
if ticks.is_negative() {
i64::MIN
} else {
i64::MAX
}
})
}
#[must_use]
pub(super) fn duration_ticks_from_cmtime_value(
value: i64,
timescale: i32,
time_base: Rational,
) -> u64 {
u64::try_from(ticks_from_cmtime_value(value, timescale, time_base)).unwrap_or(0)
}
#[must_use]
pub(super) fn copy_nv12_planes(
y_plane: &[u8],
y_stride: usize,
uv_plane: &[u8],
uv_stride: usize,
width: u32,
height: u32,
) -> Bytes {
let width = width as usize;
let height = height as usize;
let uv_rows = height / 2;
let mut out = vec![0u8; width * height + width * uv_rows];
for row in 0..height {
let src_start = row * y_stride;
let src_end = src_start + width;
if src_end > y_plane.len() {
break;
}
let dst_start = row * width;
out[dst_start..dst_start + width].copy_from_slice(&y_plane[src_start..src_end]);
}
let y_plane_bytes = width * height;
for row in 0..uv_rows {
let src_start = row * uv_stride;
let src_end = src_start + width;
if src_end > uv_plane.len() {
break;
}
let dst_start = y_plane_bytes + row * width;
out[dst_start..dst_start + width].copy_from_slice(&uv_plane[src_start..src_end]);
}
Bytes::from(out)
}
#[cfg(test)]
#[path = "codec_tests.rs"]
mod tests;