#![allow(dead_code)]
#![allow(clippy::cast_precision_loss)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VideoPixelFormat {
Yuv420p,
Yuv422p,
Yuv444p,
Rgba,
Rgb24,
Nv12,
P010,
}
impl VideoPixelFormat {
#[must_use]
pub fn bits_per_pixel(&self) -> u8 {
match self {
Self::Yuv420p | Self::Nv12 => 12,
Self::Yuv422p => 16,
Self::Yuv444p | Self::Rgb24 => 24,
Self::Rgba => 32,
Self::P010 => 15, }
}
#[must_use]
pub fn is_planar(&self) -> bool {
matches!(
self,
Self::Yuv420p | Self::Yuv422p | Self::Yuv444p | Self::Nv12 | Self::P010
)
}
#[must_use]
pub fn chroma_subsampling(&self) -> (u8, u8) {
match self {
Self::Yuv420p | Self::Nv12 | Self::P010 => (2, 2),
Self::Yuv422p => (2, 1),
Self::Yuv444p | Self::Rgba | Self::Rgb24 => (1, 1),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AudioSampleFormat {
S16,
S32,
F32,
F64,
S16P,
S32P,
F32P,
}
impl AudioSampleFormat {
#[must_use]
pub fn bytes_per_sample(&self) -> u8 {
match self {
Self::S16 | Self::S16P => 2,
Self::S32 | Self::F32 | Self::S32P | Self::F32P => 4,
Self::F64 => 8,
}
}
#[must_use]
pub fn is_planar(&self) -> bool {
matches!(self, Self::S16P | Self::S32P | Self::F32P)
}
#[must_use]
pub fn is_float(&self) -> bool {
matches!(self, Self::F32 | Self::F64 | Self::F32P)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SampleFormatInfo {
pub video: Option<VideoPixelFormat>,
pub audio: Option<AudioSampleFormat>,
}
impl SampleFormatInfo {
#[must_use]
pub fn new(video: Option<VideoPixelFormat>, audio: Option<AudioSampleFormat>) -> Self {
Self { video, audio }
}
#[must_use]
pub fn has_video(&self) -> bool {
self.video.is_some()
}
#[must_use]
pub fn has_audio(&self) -> bool {
self.audio.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_yuv420p_bits_per_pixel() {
assert_eq!(VideoPixelFormat::Yuv420p.bits_per_pixel(), 12);
}
#[test]
fn test_rgba_bits_per_pixel() {
assert_eq!(VideoPixelFormat::Rgba.bits_per_pixel(), 32);
}
#[test]
fn test_rgb24_bits_per_pixel() {
assert_eq!(VideoPixelFormat::Rgb24.bits_per_pixel(), 24);
}
#[test]
fn test_yuv422p_bits_per_pixel() {
assert_eq!(VideoPixelFormat::Yuv422p.bits_per_pixel(), 16);
}
#[test]
fn test_nv12_is_planar() {
assert!(VideoPixelFormat::Nv12.is_planar());
}
#[test]
fn test_rgba_is_not_planar() {
assert!(!VideoPixelFormat::Rgba.is_planar());
}
#[test]
fn test_yuv420p_chroma_subsampling() {
assert_eq!(VideoPixelFormat::Yuv420p.chroma_subsampling(), (2, 2));
}
#[test]
fn test_yuv422p_chroma_subsampling() {
assert_eq!(VideoPixelFormat::Yuv422p.chroma_subsampling(), (2, 1));
}
#[test]
fn test_yuv444p_chroma_subsampling() {
assert_eq!(VideoPixelFormat::Yuv444p.chroma_subsampling(), (1, 1));
}
#[test]
fn test_p010_bits_and_subsampling() {
assert_eq!(VideoPixelFormat::P010.bits_per_pixel(), 15);
assert_eq!(VideoPixelFormat::P010.chroma_subsampling(), (2, 2));
}
#[test]
fn test_s16_bytes_per_sample() {
assert_eq!(AudioSampleFormat::S16.bytes_per_sample(), 2);
}
#[test]
fn test_f64_bytes_per_sample() {
assert_eq!(AudioSampleFormat::F64.bytes_per_sample(), 8);
}
#[test]
fn test_f32p_is_planar_and_float() {
assert!(AudioSampleFormat::F32P.is_planar());
assert!(AudioSampleFormat::F32P.is_float());
}
#[test]
fn test_s32_is_not_planar_not_float() {
assert!(!AudioSampleFormat::S32.is_planar());
assert!(!AudioSampleFormat::S32.is_float());
}
#[test]
fn test_sample_format_info_has_video() {
let info = SampleFormatInfo::new(Some(VideoPixelFormat::Yuv420p), None);
assert!(info.has_video());
assert!(!info.has_audio());
}
#[test]
fn test_sample_format_info_has_audio() {
let info = SampleFormatInfo::new(None, Some(AudioSampleFormat::F32));
assert!(!info.has_video());
assert!(info.has_audio());
}
#[test]
fn test_sample_format_info_both() {
let info =
SampleFormatInfo::new(Some(VideoPixelFormat::Nv12), Some(AudioSampleFormat::S16P));
assert!(info.has_video());
assert!(info.has_audio());
}
#[test]
fn test_sample_format_info_neither() {
let info = SampleFormatInfo::new(None, None);
assert!(!info.has_video());
assert!(!info.has_audio());
}
}