1use std::io;
2
3use byteorder::ReadBytesExt;
4use bytes::Bytes;
5use scuffle_bytes_util::{BitReader, BitWriter, BytesCursorExt};
6
7#[derive(Debug, Clone, PartialEq)]
10pub struct AV1VideoDescriptor {
11 pub tag: u8,
12 pub length: u8,
13 pub codec_configuration_record: AV1CodecConfigurationRecord,
14}
15
16impl AV1VideoDescriptor {
17 pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
18 let tag = reader.read_u8()?;
19 if tag != 0x80 {
20 return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid AV1 video descriptor tag"));
21 }
22
23 let length = reader.read_u8()?;
24 if length != 4 {
25 return Err(io::Error::new(
26 io::ErrorKind::InvalidData,
27 "Invalid AV1 video descriptor length",
28 ));
29 }
30
31 Ok(AV1VideoDescriptor {
32 tag,
33 length,
34 codec_configuration_record: AV1CodecConfigurationRecord::demux(reader)?,
35 })
36 }
37}
38
39#[derive(Debug, Clone, PartialEq)]
40pub struct AV1CodecConfigurationRecord {
43 pub seq_profile: u8,
44 pub seq_level_idx_0: u8,
45 pub seq_tier_0: bool,
46 pub high_bitdepth: bool,
47 pub twelve_bit: bool,
48 pub monochrome: bool,
49 pub chroma_subsampling_x: bool,
50 pub chroma_subsampling_y: bool,
51 pub chroma_sample_position: u8,
52 pub hdr_wcg_idc: u8,
53 pub initial_presentation_delay_minus_one: Option<u8>,
54 pub config_obu: Bytes,
55}
56
57impl AV1CodecConfigurationRecord {
58 pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
59 let mut bit_reader = BitReader::new(reader);
60
61 let marker = bit_reader.read_bit()?;
62 if !marker {
63 return Err(io::Error::new(io::ErrorKind::InvalidData, "marker is not set"));
64 }
65
66 let version = bit_reader.read_bits(7)? as u8;
67 if version != 1 {
68 return Err(io::Error::new(io::ErrorKind::InvalidData, "version is not 1"));
69 }
70
71 let seq_profile = bit_reader.read_bits(3)? as u8;
72 let seq_level_idx_0 = bit_reader.read_bits(5)? as u8;
73
74 let seq_tier_0 = bit_reader.read_bit()?;
75 let high_bitdepth = bit_reader.read_bit()?;
76 let twelve_bit = bit_reader.read_bit()?;
77 let monochrome = bit_reader.read_bit()?;
78 let chroma_subsampling_x = bit_reader.read_bit()?;
79 let chroma_subsampling_y = bit_reader.read_bit()?;
80 let chroma_sample_position = bit_reader.read_bits(2)? as u8;
81
82 let hdr_wcg_idc = bit_reader.read_bits(2)? as u8;
87
88 bit_reader.seek_bits(1)?; let initial_presentation_delay_minus_one = if bit_reader.read_bit()? {
91 Some(bit_reader.read_bits(4)? as u8)
92 } else {
93 bit_reader.seek_bits(4)?; None
95 };
96
97 if !bit_reader.is_aligned() {
98 return Err(io::Error::new(io::ErrorKind::InvalidData, "Bit reader is not aligned"));
99 }
100
101 let reader = bit_reader.into_inner();
102
103 Ok(AV1CodecConfigurationRecord {
104 seq_profile,
105 seq_level_idx_0,
106 seq_tier_0,
107 high_bitdepth,
108 twelve_bit,
109 monochrome,
110 chroma_subsampling_x,
111 chroma_subsampling_y,
112 chroma_sample_position,
113 hdr_wcg_idc,
114 initial_presentation_delay_minus_one,
115 config_obu: reader.extract_remaining(),
116 })
117 }
118
119 pub fn size(&self) -> u64 {
120 1 + 1 + 1 + 1 + self.config_obu.len() as u64
125 }
126
127 pub fn mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
128 let mut bit_writer = BitWriter::new(writer);
129
130 bit_writer.write_bit(true)?; bit_writer.write_bits(1, 7)?; bit_writer.write_bits(self.seq_profile as u64, 3)?;
134 bit_writer.write_bits(self.seq_level_idx_0 as u64, 5)?;
135
136 bit_writer.write_bit(self.seq_tier_0)?;
137 bit_writer.write_bit(self.high_bitdepth)?;
138 bit_writer.write_bit(self.twelve_bit)?;
139 bit_writer.write_bit(self.monochrome)?;
140 bit_writer.write_bit(self.chroma_subsampling_x)?;
141 bit_writer.write_bit(self.chroma_subsampling_y)?;
142 bit_writer.write_bits(self.chroma_sample_position as u64, 2)?;
143
144 bit_writer.write_bits(0, 3)?; if let Some(initial_presentation_delay_minus_one) = self.initial_presentation_delay_minus_one {
147 bit_writer.write_bit(true)?;
148 bit_writer.write_bits(initial_presentation_delay_minus_one as u64, 4)?;
149 } else {
150 bit_writer.write_bit(false)?;
151 bit_writer.write_bits(0, 4)?; }
153
154 bit_writer.finish()?.write_all(&self.config_obu)?;
155
156 Ok(())
157 }
158}
159
160#[cfg(test)]
161#[cfg_attr(all(test, coverage_nightly), coverage(off))]
162mod tests {
163
164 use super::*;
165
166 #[test]
167 fn test_config_demux() {
168 let data = b"\x81\r\x0c\0\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
169
170 let config = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap();
171
172 insta::assert_debug_snapshot!(config, @r#"
173 AV1CodecConfigurationRecord {
174 seq_profile: 0,
175 seq_level_idx_0: 13,
176 seq_tier_0: false,
177 high_bitdepth: false,
178 twelve_bit: false,
179 monochrome: false,
180 chroma_subsampling_x: true,
181 chroma_subsampling_y: true,
182 chroma_sample_position: 0,
183 hdr_wcg_idc: 0,
184 initial_presentation_delay_minus_one: None,
185 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
186 }
187 "#);
188 }
189
190 #[test]
191 fn test_marker_is_not_set() {
192 let data = vec![0b00000000];
193
194 let err = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap_err();
195
196 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
197 assert_eq!(err.to_string(), "marker is not set");
198 }
199
200 #[test]
201 fn test_version_is_not_1() {
202 let data = vec![0b10000000];
203
204 let err = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap_err();
205
206 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
207 assert_eq!(err.to_string(), "version is not 1");
208 }
209
210 #[test]
211 fn test_config_demux_with_initial_presentation_delay() {
212 let data = b"\x81\r\x0c\x3f\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
213
214 let config = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap();
215
216 insta::assert_debug_snapshot!(config, @r#"
217 AV1CodecConfigurationRecord {
218 seq_profile: 0,
219 seq_level_idx_0: 13,
220 seq_tier_0: false,
221 high_bitdepth: false,
222 twelve_bit: false,
223 monochrome: false,
224 chroma_subsampling_x: true,
225 chroma_subsampling_y: true,
226 chroma_sample_position: 0,
227 hdr_wcg_idc: 0,
228 initial_presentation_delay_minus_one: Some(
229 15,
230 ),
231 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
232 }
233 "#);
234 }
235
236 #[test]
237 fn test_config_mux() {
238 let config = AV1CodecConfigurationRecord {
239 seq_profile: 0,
240 seq_level_idx_0: 0,
241 seq_tier_0: false,
242 high_bitdepth: false,
243 twelve_bit: false,
244 monochrome: false,
245 chroma_subsampling_x: false,
246 chroma_subsampling_y: false,
247 chroma_sample_position: 0,
248 hdr_wcg_idc: 0,
249 initial_presentation_delay_minus_one: None,
250 config_obu: Bytes::from_static(b"HELLO FROM THE OBU"),
251 };
252
253 let mut buf = Vec::new();
254 config.mux(&mut buf).unwrap();
255
256 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\0HELLO FROM THE OBU""#);
257 }
258
259 #[test]
260 fn test_config_mux_with_delay() {
261 let config = AV1CodecConfigurationRecord {
262 seq_profile: 0,
263 seq_level_idx_0: 0,
264 seq_tier_0: false,
265 high_bitdepth: false,
266 twelve_bit: false,
267 monochrome: false,
268 chroma_subsampling_x: false,
269 chroma_subsampling_y: false,
270 chroma_sample_position: 0,
271 hdr_wcg_idc: 0,
272 initial_presentation_delay_minus_one: Some(0),
273 config_obu: Bytes::from_static(b"HELLO FROM THE OBU"),
274 };
275
276 let mut buf = Vec::new();
277 config.mux(&mut buf).unwrap();
278
279 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\x10HELLO FROM THE OBU""#);
280 }
281
282 #[test]
283 fn test_video_descriptor_demux() {
284 let data = b"\x80\x04\x81\r\x0c\x3f\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
285
286 let config = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap();
287
288 insta::assert_debug_snapshot!(config, @r#"
289 AV1VideoDescriptor {
290 tag: 128,
291 length: 4,
292 codec_configuration_record: AV1CodecConfigurationRecord {
293 seq_profile: 0,
294 seq_level_idx_0: 13,
295 seq_tier_0: false,
296 high_bitdepth: false,
297 twelve_bit: false,
298 monochrome: false,
299 chroma_subsampling_x: true,
300 chroma_subsampling_y: true,
301 chroma_sample_position: 0,
302 hdr_wcg_idc: 0,
303 initial_presentation_delay_minus_one: Some(
304 15,
305 ),
306 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
307 },
308 }
309 "#);
310 }
311
312 #[test]
313 fn test_video_descriptor_demux_invalid_tag() {
314 let data = b"\x81".to_vec();
315
316 let err = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap_err();
317
318 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
319 assert_eq!(err.to_string(), "Invalid AV1 video descriptor tag");
320 }
321
322 #[test]
323 fn test_video_descriptor_demux_invalid_length() {
324 let data = b"\x80\x05ju".to_vec();
325
326 let err = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap_err();
327
328 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
329 assert_eq!(err.to_string(), "Invalid AV1 video descriptor length");
330 }
331}