moq_karp/video/
av1.rs

1use serde::{Deserialize, Serialize};
2
3use crate::Error;
4
5// https://aomediacodec.github.io/av1-isobmff/#codecsparam
6#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
7pub struct AV1 {
8	pub profile: u8,
9	pub level: u8,
10	pub tier: char,
11	pub bitdepth: u8,
12	pub mono_chrome: bool,
13	pub chroma_subsampling_x: bool,
14	pub chroma_subsampling_y: bool,
15	pub chroma_sample_position: u8,
16	pub color_primaries: u8,
17	pub transfer_characteristics: u8,
18	pub matrix_coefficients: u8,
19	pub full_range: bool,
20}
21
22impl AV1 {
23	pub const PREFIX: &'static str = "av01";
24}
25
26// av01.<profile>.<level><tier>.<bitDepth>.<monochrome>.<chromaSubsampling>.
27// <colorPrimaries>.<transferCharacteristics>.<matrixCoefficients>.<videoFullRangeFlag>
28impl std::fmt::Display for AV1 {
29	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30		write!(
31			f,
32			"av01.{:01}.{:02}{}.{:02}",
33			self.profile, self.level, self.tier, self.bitdepth
34		)?;
35
36		let short = AV1 {
37			profile: self.profile,
38			level: self.level,
39			tier: self.tier,
40			bitdepth: self.bitdepth,
41			..Default::default()
42		};
43
44		if self == &short {
45			return Ok(());
46		}
47
48		write!(
49			f,
50			".{:01}.{}{}{}.{:02}.{:02}.{:02}.{:01}",
51			self.mono_chrome as u8,
52			self.chroma_subsampling_x as u8,
53			self.chroma_subsampling_y as u8,
54			{ self.chroma_sample_position },
55			self.color_primaries,
56			self.transfer_characteristics,
57			self.matrix_coefficients,
58			self.full_range as u8,
59		)
60	}
61}
62
63lazy_static::lazy_static! {
64	static ref AV1_REGEX: regex::Regex = regex::Regex::new(&r#"
65		^av01
66		\.(?<profile>[01])
67		\.(?<level>\d{2})(?<tier>\w)
68		\.(?<bitdepth>\d{2})
69		(?<extra>
70			\.(?<mono>\d)
71			\.(?<chroma_subsampling_x>[01])(?<chroma_subsampling_y>[01])(?<chroma_sample_position>[0-3])
72			\.(?<color>\d{2})
73			\.(?<transfer>\d{2})
74			\.(?<matrix>\d{2})
75			\.(?<full>[01])
76		)?
77		$"#.replace(['\n', ' ', '\t'], "")
78	).unwrap();
79}
80
81impl std::str::FromStr for AV1 {
82	type Err = Error;
83
84	fn from_str(s: &str) -> Result<Self, Self::Err> {
85		let captures = AV1_REGEX.captures(s).ok_or(Error::InvalidCodec)?;
86
87		let mut av1 = AV1 {
88			profile: u8::from_str(&captures["profile"])?,
89			level: u8::from_str(&captures["level"])?,
90			tier: captures["tier"].chars().next().unwrap(),
91			bitdepth: u8::from_str(&captures["bitdepth"])?,
92			..Default::default()
93		};
94
95		if captures.name("extra").is_none() {
96			return Ok(av1);
97		}
98
99		av1.mono_chrome = &captures["mono"] == "1";
100		av1.chroma_subsampling_x = &captures["chroma_subsampling_x"] == "1";
101		av1.chroma_subsampling_y = &captures["chroma_subsampling_y"] == "1";
102		av1.chroma_sample_position = u8::from_str(&captures["chroma_sample_position"])?;
103		av1.color_primaries = u8::from_str(&captures["color"])?;
104		av1.transfer_characteristics = u8::from_str(&captures["transfer"])?;
105		av1.matrix_coefficients = u8::from_str(&captures["matrix"])?;
106		av1.full_range = &captures["full"] == "1";
107
108		Ok(av1)
109	}
110}
111
112impl Default for AV1 {
113	fn default() -> Self {
114		// .0.110.01.01.01.0
115		Self {
116			profile: 0,
117			level: 0,
118			tier: 'M',
119			bitdepth: 8,
120			mono_chrome: false,
121			chroma_subsampling_x: true,
122			chroma_subsampling_y: true,
123			chroma_sample_position: 0,
124			color_primaries: 1,
125			transfer_characteristics: 1,
126			matrix_coefficients: 1,
127			full_range: false,
128		}
129	}
130}
131
132#[cfg(test)]
133mod test {
134	use std::str::FromStr;
135
136	use super::*;
137
138	#[test]
139	fn test_av1() {
140		let encoded = "av01.0.04M.10.0.112.09.16.09.0";
141		let decoded = AV1 {
142			profile: 0,
143			level: 4,
144			tier: 'M',
145			bitdepth: 10,
146			mono_chrome: false,
147			chroma_subsampling_x: true,
148			chroma_subsampling_y: true,
149			chroma_sample_position: 2,
150			color_primaries: 9,
151			transfer_characteristics: 16,
152			matrix_coefficients: 9,
153			full_range: false,
154		};
155
156		let output = AV1::from_str(encoded).expect("failed to parse");
157		assert_eq!(output, decoded);
158
159		let output = decoded.to_string();
160		assert_eq!(output, encoded);
161	}
162
163	#[test]
164	fn test_av1_short() {
165		let encoded = "av01.0.01M.08";
166		let decoded = AV1 {
167			profile: 0,
168			level: 1,
169			tier: 'M',
170			bitdepth: 8,
171			..Default::default()
172		};
173
174		let output = AV1::from_str(encoded).expect("failed to parse");
175		assert_eq!(output, decoded);
176
177		let output = decoded.to_string();
178		assert_eq!(output, encoded);
179	}
180}