1use serde::{Deserialize, Serialize};
2
3use crate::Error;
4
5#[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
26impl 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 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}