1use std::io;
2
3use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
4use scuffle_bytes_util::{BitReader, BitWriter, BytesCow};
5
6#[derive(Debug, Clone, PartialEq)]
10pub struct AV1VideoDescriptor<'a> {
11 pub tag: u8,
15 pub length: u8,
19 pub codec_configuration_record: AV1CodecConfigurationRecord<'a>,
21}
22
23impl<'a> Deserialize<'a> for AV1VideoDescriptor<'a> {
24 fn deserialize<R>(mut reader: R) -> io::Result<Self>
25 where
26 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
27 {
28 let tag = u8::deserialize(&mut reader)?;
29 if tag != 0x80 {
30 return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid AV1 video descriptor tag"));
31 }
32
33 let length = u8::deserialize(&mut reader)?;
34 if length != 4 {
35 return Err(io::Error::new(
36 io::ErrorKind::InvalidData,
37 "Invalid AV1 video descriptor length",
38 ));
39 }
40
41 Ok(AV1VideoDescriptor {
42 tag,
43 length,
44 codec_configuration_record: AV1CodecConfigurationRecord::deserialize(reader)?,
45 })
46 }
47}
48
49impl Serialize for AV1VideoDescriptor<'_> {
50 fn serialize<W>(&self, mut writer: W) -> io::Result<()>
51 where
52 W: std::io::Write,
53 {
54 self.tag.serialize(&mut writer)?;
55 self.length.serialize(&mut writer)?;
56 self.codec_configuration_record.serialize(&mut writer)?;
57 Ok(())
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct AV1CodecConfigurationRecord<'a> {
66 pub seq_profile: u8,
70 pub seq_level_idx_0: u8,
74 pub seq_tier_0: bool,
79 pub high_bitdepth: bool,
83 pub twelve_bit: bool,
88 pub monochrome: bool,
93 pub chroma_subsampling_x: bool,
98 pub chroma_subsampling_y: bool,
103 pub chroma_sample_position: u8,
108 pub hdr_wcg_idc: u8,
122 pub initial_presentation_delay_minus_one: Option<u8>,
127 pub config_obu: BytesCow<'a>,
131}
132
133impl<'a> Deserialize<'a> for AV1CodecConfigurationRecord<'a> {
134 fn deserialize<R>(mut reader: R) -> io::Result<Self>
135 where
136 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
137 {
138 let mut bit_reader = BitReader::new(reader.as_std());
139
140 let marker = bit_reader.read_bit()?;
141 if !marker {
142 return Err(io::Error::new(io::ErrorKind::InvalidData, "marker is not set"));
143 }
144
145 let version = bit_reader.read_bits(7)? as u8;
146 if version != 1 {
147 return Err(io::Error::new(io::ErrorKind::InvalidData, "version is not 1"));
148 }
149
150 let seq_profile = bit_reader.read_bits(3)? as u8;
151 let seq_level_idx_0 = bit_reader.read_bits(5)? as u8;
152
153 let seq_tier_0 = bit_reader.read_bit()?;
154 let high_bitdepth = bit_reader.read_bit()?;
155 let twelve_bit = bit_reader.read_bit()?;
156 let monochrome = bit_reader.read_bit()?;
157 let chroma_subsampling_x = bit_reader.read_bit()?;
158 let chroma_subsampling_y = bit_reader.read_bit()?;
159 let chroma_sample_position = bit_reader.read_bits(2)? as u8;
160
161 let hdr_wcg_idc = bit_reader.read_bits(2)? as u8;
166
167 bit_reader.read_bits(1)?; let initial_presentation_delay_minus_one = if bit_reader.read_bit()? {
170 Some(bit_reader.read_bits(4)? as u8)
171 } else {
172 bit_reader.read_bits(4)?; None
174 };
175
176 {
177 let bit_reader = bit_reader; if !bit_reader.is_aligned() {
179 return Err(io::Error::new(io::ErrorKind::InvalidData, "Bit reader is not aligned"));
180 }
181 }
182
183 Ok(AV1CodecConfigurationRecord {
184 seq_profile,
185 seq_level_idx_0,
186 seq_tier_0,
187 high_bitdepth,
188 twelve_bit,
189 monochrome,
190 chroma_subsampling_x,
191 chroma_subsampling_y,
192 chroma_sample_position,
193 hdr_wcg_idc,
194 initial_presentation_delay_minus_one,
195 config_obu: reader.try_read_to_end()?,
196 })
197 }
198}
199
200impl Serialize for AV1CodecConfigurationRecord<'_> {
201 fn serialize<W>(&self, writer: W) -> io::Result<()>
202 where
203 W: std::io::Write,
204 {
205 let mut bit_writer = BitWriter::new(writer);
206
207 bit_writer.write_bit(true)?; bit_writer.write_bits(1, 7)?; bit_writer.write_bits(self.seq_profile as u64, 3)?;
211 bit_writer.write_bits(self.seq_level_idx_0 as u64, 5)?;
212
213 bit_writer.write_bit(self.seq_tier_0)?;
214 bit_writer.write_bit(self.high_bitdepth)?;
215 bit_writer.write_bit(self.twelve_bit)?;
216 bit_writer.write_bit(self.monochrome)?;
217 bit_writer.write_bit(self.chroma_subsampling_x)?;
218 bit_writer.write_bit(self.chroma_subsampling_y)?;
219 bit_writer.write_bits(self.chroma_sample_position as u64, 2)?;
220
221 bit_writer.write_bits(0, 3)?; if let Some(initial_presentation_delay_minus_one) = self.initial_presentation_delay_minus_one {
224 bit_writer.write_bit(true)?;
225 bit_writer.write_bits(initial_presentation_delay_minus_one as u64, 4)?;
226 } else {
227 bit_writer.write_bit(false)?;
228 bit_writer.write_bits(0, 4)?; }
230
231 bit_writer.finish()?.write_all(self.config_obu.as_bytes())?;
232
233 Ok(())
234 }
235}
236
237#[cfg(feature = "isobmff")]
238impl isobmff::IsoSized for AV1CodecConfigurationRecord<'_> {
239 fn size(&self) -> usize {
241 1 + 1 + 1 + 1 + self.config_obu.len()
246 }
247}
248
249#[cfg(test)]
250#[cfg_attr(all(test, coverage_nightly), coverage(off))]
251mod tests {
252 use bytes::Bytes;
253 use scuffle_bytes_util::zero_copy::Slice;
254
255 use super::*;
256
257 #[test]
258 fn test_config_demux() {
259 let data = b"\x81\r\x0c\0\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@";
260
261 let config = AV1CodecConfigurationRecord::deserialize(Slice::from(&data[..])).unwrap();
262
263 insta::assert_debug_snapshot!(config, @r#"
264 AV1CodecConfigurationRecord {
265 seq_profile: 0,
266 seq_level_idx_0: 13,
267 seq_tier_0: false,
268 high_bitdepth: false,
269 twelve_bit: false,
270 monochrome: false,
271 chroma_subsampling_x: true,
272 chroma_subsampling_y: true,
273 chroma_sample_position: 0,
274 hdr_wcg_idc: 0,
275 initial_presentation_delay_minus_one: None,
276 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
277 }
278 "#);
279 }
280
281 #[test]
282 fn test_marker_is_not_set() {
283 let data = [0b00000000];
284
285 let err = AV1CodecConfigurationRecord::deserialize(Slice::from(&data[..])).unwrap_err();
286
287 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
288 assert_eq!(err.to_string(), "marker is not set");
289 }
290
291 #[test]
292 fn test_version_is_not_1() {
293 let data = [0b10000000];
294
295 let err = AV1CodecConfigurationRecord::deserialize(Slice::from(&data[..])).unwrap_err();
296
297 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
298 assert_eq!(err.to_string(), "version is not 1");
299 }
300
301 #[test]
302 fn test_config_demux_with_initial_presentation_delay() {
303 let data = b"\x81\r\x0c\x3f\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
304
305 let config = AV1CodecConfigurationRecord::deserialize(Slice::from(&data[..])).unwrap();
306
307 insta::assert_debug_snapshot!(config, @r#"
308 AV1CodecConfigurationRecord {
309 seq_profile: 0,
310 seq_level_idx_0: 13,
311 seq_tier_0: false,
312 high_bitdepth: false,
313 twelve_bit: false,
314 monochrome: false,
315 chroma_subsampling_x: true,
316 chroma_subsampling_y: true,
317 chroma_sample_position: 0,
318 hdr_wcg_idc: 0,
319 initial_presentation_delay_minus_one: Some(
320 15,
321 ),
322 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
323 }
324 "#);
325 }
326
327 #[test]
328 fn test_config_mux() {
329 let config = AV1CodecConfigurationRecord {
330 seq_profile: 0,
331 seq_level_idx_0: 0,
332 seq_tier_0: false,
333 high_bitdepth: false,
334 twelve_bit: false,
335 monochrome: false,
336 chroma_subsampling_x: false,
337 chroma_subsampling_y: false,
338 chroma_sample_position: 0,
339 hdr_wcg_idc: 0,
340 initial_presentation_delay_minus_one: None,
341 config_obu: BytesCow::from_static(b"HELLO FROM THE OBU"),
342 };
343
344 let mut buf = Vec::new();
345 config.serialize(&mut buf).unwrap();
346
347 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\0HELLO FROM THE OBU""#);
348 }
349
350 #[test]
351 fn test_config_mux_with_delay() {
352 let config = AV1CodecConfigurationRecord {
353 seq_profile: 0,
354 seq_level_idx_0: 0,
355 seq_tier_0: false,
356 high_bitdepth: false,
357 twelve_bit: false,
358 monochrome: false,
359 chroma_subsampling_x: false,
360 chroma_subsampling_y: false,
361 chroma_sample_position: 0,
362 hdr_wcg_idc: 0,
363 initial_presentation_delay_minus_one: Some(0),
364 config_obu: BytesCow::from_static(b"HELLO FROM THE OBU"),
365 };
366
367 let mut buf = Vec::new();
368 config.serialize(&mut buf).unwrap();
369
370 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\x10HELLO FROM THE OBU""#);
371 }
372
373 #[test]
374 fn test_video_descriptor_demux() {
375 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();
376
377 let config = AV1VideoDescriptor::deserialize(Slice::from(&data[..])).unwrap();
378
379 insta::assert_debug_snapshot!(config, @r#"
380 AV1VideoDescriptor {
381 tag: 128,
382 length: 4,
383 codec_configuration_record: AV1CodecConfigurationRecord {
384 seq_profile: 0,
385 seq_level_idx_0: 13,
386 seq_tier_0: false,
387 high_bitdepth: false,
388 twelve_bit: false,
389 monochrome: false,
390 chroma_subsampling_x: true,
391 chroma_subsampling_y: true,
392 chroma_sample_position: 0,
393 hdr_wcg_idc: 0,
394 initial_presentation_delay_minus_one: Some(
395 15,
396 ),
397 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
398 },
399 }
400 "#);
401 }
402
403 #[test]
404 fn test_video_descriptor_demux_invalid_tag() {
405 let data = b"\x81".to_vec();
406
407 let err = AV1VideoDescriptor::deserialize(Slice::from(&data[..])).unwrap_err();
408
409 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
410 assert_eq!(err.to_string(), "Invalid AV1 video descriptor tag");
411 }
412
413 #[test]
414 fn test_video_descriptor_demux_invalid_length() {
415 let data = b"\x80\x05ju".to_vec();
416
417 let err = AV1VideoDescriptor::deserialize(Slice::from(&data[..])).unwrap_err();
418
419 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
420 assert_eq!(err.to_string(), "Invalid AV1 video descriptor length");
421 }
422}