1use std::io;
2
3use byteorder::ReadBytesExt;
4use bytes::Bytes;
5use scuffle_bytes_util::{BitReader, BitWriter, BytesCursorExt};
6
7#[derive(Debug, Clone, PartialEq)]
11pub struct AV1VideoDescriptor {
12 pub tag: u8,
16 pub length: u8,
20 pub codec_configuration_record: AV1CodecConfigurationRecord,
22}
23
24impl AV1VideoDescriptor {
25 pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
27 let tag = reader.read_u8()?;
28 if tag != 0x80 {
29 return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid AV1 video descriptor tag"));
30 }
31
32 let length = reader.read_u8()?;
33 if length != 4 {
34 return Err(io::Error::new(
35 io::ErrorKind::InvalidData,
36 "Invalid AV1 video descriptor length",
37 ));
38 }
39
40 Ok(AV1VideoDescriptor {
41 tag,
42 length,
43 codec_configuration_record: AV1CodecConfigurationRecord::demux(reader)?,
44 })
45 }
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub struct AV1CodecConfigurationRecord {
53 pub seq_profile: u8,
57 pub seq_level_idx_0: u8,
61 pub seq_tier_0: bool,
66 pub high_bitdepth: bool,
70 pub twelve_bit: bool,
75 pub monochrome: bool,
80 pub chroma_subsampling_x: bool,
85 pub chroma_subsampling_y: bool,
90 pub chroma_sample_position: u8,
95 pub hdr_wcg_idc: u8,
109 pub initial_presentation_delay_minus_one: Option<u8>,
114 pub config_obu: Bytes,
118}
119
120impl AV1CodecConfigurationRecord {
121 pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
123 let mut bit_reader = BitReader::new(reader);
124
125 let marker = bit_reader.read_bit()?;
126 if !marker {
127 return Err(io::Error::new(io::ErrorKind::InvalidData, "marker is not set"));
128 }
129
130 let version = bit_reader.read_bits(7)? as u8;
131 if version != 1 {
132 return Err(io::Error::new(io::ErrorKind::InvalidData, "version is not 1"));
133 }
134
135 let seq_profile = bit_reader.read_bits(3)? as u8;
136 let seq_level_idx_0 = bit_reader.read_bits(5)? as u8;
137
138 let seq_tier_0 = bit_reader.read_bit()?;
139 let high_bitdepth = bit_reader.read_bit()?;
140 let twelve_bit = bit_reader.read_bit()?;
141 let monochrome = bit_reader.read_bit()?;
142 let chroma_subsampling_x = bit_reader.read_bit()?;
143 let chroma_subsampling_y = bit_reader.read_bit()?;
144 let chroma_sample_position = bit_reader.read_bits(2)? as u8;
145
146 let hdr_wcg_idc = bit_reader.read_bits(2)? as u8;
151
152 bit_reader.seek_bits(1)?; let initial_presentation_delay_minus_one = if bit_reader.read_bit()? {
155 Some(bit_reader.read_bits(4)? as u8)
156 } else {
157 bit_reader.seek_bits(4)?; None
159 };
160
161 if !bit_reader.is_aligned() {
162 return Err(io::Error::new(io::ErrorKind::InvalidData, "Bit reader is not aligned"));
163 }
164
165 let reader = bit_reader.into_inner();
166
167 Ok(AV1CodecConfigurationRecord {
168 seq_profile,
169 seq_level_idx_0,
170 seq_tier_0,
171 high_bitdepth,
172 twelve_bit,
173 monochrome,
174 chroma_subsampling_x,
175 chroma_subsampling_y,
176 chroma_sample_position,
177 hdr_wcg_idc,
178 initial_presentation_delay_minus_one,
179 config_obu: reader.extract_remaining(),
180 })
181 }
182
183 pub fn size(&self) -> u64 {
185 1 + 1 + 1 + 1 + self.config_obu.len() as u64
190 }
191
192 pub fn mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
194 let mut bit_writer = BitWriter::new(writer);
195
196 bit_writer.write_bit(true)?; bit_writer.write_bits(1, 7)?; bit_writer.write_bits(self.seq_profile as u64, 3)?;
200 bit_writer.write_bits(self.seq_level_idx_0 as u64, 5)?;
201
202 bit_writer.write_bit(self.seq_tier_0)?;
203 bit_writer.write_bit(self.high_bitdepth)?;
204 bit_writer.write_bit(self.twelve_bit)?;
205 bit_writer.write_bit(self.monochrome)?;
206 bit_writer.write_bit(self.chroma_subsampling_x)?;
207 bit_writer.write_bit(self.chroma_subsampling_y)?;
208 bit_writer.write_bits(self.chroma_sample_position as u64, 2)?;
209
210 bit_writer.write_bits(0, 3)?; if let Some(initial_presentation_delay_minus_one) = self.initial_presentation_delay_minus_one {
213 bit_writer.write_bit(true)?;
214 bit_writer.write_bits(initial_presentation_delay_minus_one as u64, 4)?;
215 } else {
216 bit_writer.write_bit(false)?;
217 bit_writer.write_bits(0, 4)?; }
219
220 bit_writer.finish()?.write_all(&self.config_obu)?;
221
222 Ok(())
223 }
224}
225
226#[cfg(test)]
227#[cfg_attr(all(test, coverage_nightly), coverage(off))]
228mod tests {
229
230 use super::*;
231
232 #[test]
233 fn test_config_demux() {
234 let data = b"\x81\r\x0c\0\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
235
236 let config = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap();
237
238 insta::assert_debug_snapshot!(config, @r#"
239 AV1CodecConfigurationRecord {
240 seq_profile: 0,
241 seq_level_idx_0: 13,
242 seq_tier_0: false,
243 high_bitdepth: false,
244 twelve_bit: false,
245 monochrome: false,
246 chroma_subsampling_x: true,
247 chroma_subsampling_y: true,
248 chroma_sample_position: 0,
249 hdr_wcg_idc: 0,
250 initial_presentation_delay_minus_one: None,
251 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
252 }
253 "#);
254 }
255
256 #[test]
257 fn test_marker_is_not_set() {
258 let data = vec![0b00000000];
259
260 let err = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap_err();
261
262 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
263 assert_eq!(err.to_string(), "marker is not set");
264 }
265
266 #[test]
267 fn test_version_is_not_1() {
268 let data = vec![0b10000000];
269
270 let err = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap_err();
271
272 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
273 assert_eq!(err.to_string(), "version is not 1");
274 }
275
276 #[test]
277 fn test_config_demux_with_initial_presentation_delay() {
278 let data = b"\x81\r\x0c\x3f\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@".to_vec();
279
280 let config = AV1CodecConfigurationRecord::demux(&mut io::Cursor::new(data.into())).unwrap();
281
282 insta::assert_debug_snapshot!(config, @r#"
283 AV1CodecConfigurationRecord {
284 seq_profile: 0,
285 seq_level_idx_0: 13,
286 seq_tier_0: false,
287 high_bitdepth: false,
288 twelve_bit: false,
289 monochrome: false,
290 chroma_subsampling_x: true,
291 chroma_subsampling_y: true,
292 chroma_sample_position: 0,
293 hdr_wcg_idc: 0,
294 initial_presentation_delay_minus_one: Some(
295 15,
296 ),
297 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
298 }
299 "#);
300 }
301
302 #[test]
303 fn test_config_mux() {
304 let config = AV1CodecConfigurationRecord {
305 seq_profile: 0,
306 seq_level_idx_0: 0,
307 seq_tier_0: false,
308 high_bitdepth: false,
309 twelve_bit: false,
310 monochrome: false,
311 chroma_subsampling_x: false,
312 chroma_subsampling_y: false,
313 chroma_sample_position: 0,
314 hdr_wcg_idc: 0,
315 initial_presentation_delay_minus_one: None,
316 config_obu: Bytes::from_static(b"HELLO FROM THE OBU"),
317 };
318
319 let mut buf = Vec::new();
320 config.mux(&mut buf).unwrap();
321
322 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\0HELLO FROM THE OBU""#);
323 }
324
325 #[test]
326 fn test_config_mux_with_delay() {
327 let config = AV1CodecConfigurationRecord {
328 seq_profile: 0,
329 seq_level_idx_0: 0,
330 seq_tier_0: false,
331 high_bitdepth: false,
332 twelve_bit: false,
333 monochrome: false,
334 chroma_subsampling_x: false,
335 chroma_subsampling_y: false,
336 chroma_sample_position: 0,
337 hdr_wcg_idc: 0,
338 initial_presentation_delay_minus_one: Some(0),
339 config_obu: Bytes::from_static(b"HELLO FROM THE OBU"),
340 };
341
342 let mut buf = Vec::new();
343 config.mux(&mut buf).unwrap();
344
345 insta::assert_snapshot!(format!("{:?}", Bytes::from(buf)), @r#"b"\x81\0\0\x10HELLO FROM THE OBU""#);
346 }
347
348 #[test]
349 fn test_video_descriptor_demux() {
350 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();
351
352 let config = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap();
353
354 insta::assert_debug_snapshot!(config, @r#"
355 AV1VideoDescriptor {
356 tag: 128,
357 length: 4,
358 codec_configuration_record: AV1CodecConfigurationRecord {
359 seq_profile: 0,
360 seq_level_idx_0: 13,
361 seq_tier_0: false,
362 high_bitdepth: false,
363 twelve_bit: false,
364 monochrome: false,
365 chroma_subsampling_x: true,
366 chroma_subsampling_y: true,
367 chroma_sample_position: 0,
368 hdr_wcg_idc: 0,
369 initial_presentation_delay_minus_one: Some(
370 15,
371 ),
372 config_obu: b"\n\x0f\0\0\0j\xef\xbf\xe1\xbc\x02\x19\x90\x10\x10\x10@",
373 },
374 }
375 "#);
376 }
377
378 #[test]
379 fn test_video_descriptor_demux_invalid_tag() {
380 let data = b"\x81".to_vec();
381
382 let err = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap_err();
383
384 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
385 assert_eq!(err.to_string(), "Invalid AV1 video descriptor tag");
386 }
387
388 #[test]
389 fn test_video_descriptor_demux_invalid_length() {
390 let data = b"\x80\x05ju".to_vec();
391
392 let err = AV1VideoDescriptor::demux(&mut io::Cursor::new(data.into())).unwrap_err();
393
394 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
395 assert_eq!(err.to_string(), "Invalid AV1 video descriptor length");
396 }
397}