1use std::io::{
2 Write, {self},
3};
4
5use byteorder::{BigEndian, WriteBytesExt};
6use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
7use scuffle_bytes_util::{BitReader, BitWriter, BytesCow, IoResultExt};
8
9use crate::sps::SpsExtended;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct AVCDecoderConfigurationRecord<'a> {
15 pub configuration_version: u8,
19
20 pub profile_indication: u8,
24
25 pub profile_compatibility: u8,
29
30 pub level_indication: u8,
34
35 pub length_size_minus_one: u8,
39
40 pub sps: Vec<BytesCow<'a>>,
46
47 pub pps: Vec<BytesCow<'a>>,
55
56 pub extended_config: Option<AvccExtendedConfig>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct AvccExtendedConfig {
66 pub chroma_format_idc: u8,
73
74 pub bit_depth_luma_minus8: u8,
80
81 pub bit_depth_chroma_minus8: u8,
87
88 pub sequence_parameter_set_ext: Vec<SpsExtended>,
92}
93
94impl<'a> Deserialize<'a> for AVCDecoderConfigurationRecord<'a> {
95 fn deserialize<R>(mut reader: R) -> io::Result<Self>
96 where
97 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
98 {
99 let configuration_version = u8::deserialize(&mut reader)?;
100 let profile_indication = u8::deserialize(&mut reader)?;
101 let profile_compatibility = u8::deserialize(&mut reader)?;
102 let level_indication = u8::deserialize(&mut reader)?;
103 let length_size_minus_one = u8::deserialize(&mut reader)? & 0b00000011;
104 let num_of_sequence_parameter_sets = u8::deserialize(&mut reader)? & 0b00011111;
105
106 let mut sps = Vec::with_capacity(num_of_sequence_parameter_sets as usize);
107 for _ in 0..num_of_sequence_parameter_sets {
108 let sps_length = u16::deserialize(&mut reader)?;
109 let sps_data = reader.try_read(sps_length as usize)?;
110 sps.push(sps_data);
111 }
112
113 let num_of_picture_parameter_sets = u8::deserialize(&mut reader)?;
114 let mut pps = Vec::with_capacity(num_of_picture_parameter_sets as usize);
115 for _ in 0..num_of_picture_parameter_sets {
116 let pps_length = u16::deserialize(&mut reader)?;
117 let pps_data = reader.try_read(pps_length as usize)?;
118 pps.push(pps_data);
119 }
120
121 let extended_config = match profile_indication {
125 66 | 77 | 88 => None,
126 _ => {
127 let chroma_format_idc = u8::deserialize(&mut reader).eof_to_none()?;
128 if let Some(chroma_format_idc) = chroma_format_idc {
129 let chroma_format_idc = chroma_format_idc & 0b00000011; let bit_depth_luma_minus8 = u8::deserialize(&mut reader)? & 0b00000111; let bit_depth_chroma_minus8 = u8::deserialize(&mut reader)? & 0b00000111; let number_of_sequence_parameter_set_ext = u8::deserialize(&mut reader)?; let mut sequence_parameter_set_ext = Vec::with_capacity(number_of_sequence_parameter_set_ext as usize);
135 for _ in 0..number_of_sequence_parameter_set_ext {
136 let sps_ext_length = u16::deserialize(&mut reader)?;
137 let sps_ext_data = reader.try_read(sps_ext_length as usize)?;
138
139 let mut bit_reader = BitReader::new_from_slice(sps_ext_data);
140 let sps_ext_parsed = SpsExtended::parse(&mut bit_reader)?;
141 sequence_parameter_set_ext.push(sps_ext_parsed);
142 }
143
144 Some(AvccExtendedConfig {
145 chroma_format_idc,
146 bit_depth_luma_minus8,
147 bit_depth_chroma_minus8,
148 sequence_parameter_set_ext,
149 })
150 } else {
151 None
154 }
155 }
156 };
157
158 Ok(Self {
159 configuration_version,
160 profile_indication,
161 profile_compatibility,
162 level_indication,
163 length_size_minus_one,
164 sps,
165 pps,
166 extended_config,
167 })
168 }
169}
170
171impl Serialize for AVCDecoderConfigurationRecord<'_> {
172 fn serialize<W>(&self, writer: W) -> io::Result<()>
173 where
174 W: std::io::Write,
175 {
176 let mut bit_writer = BitWriter::new(writer);
177
178 bit_writer.write_u8(self.configuration_version)?;
179 bit_writer.write_u8(self.profile_indication)?;
180 bit_writer.write_u8(self.profile_compatibility)?;
181 bit_writer.write_u8(self.level_indication)?;
182 bit_writer.write_bits(0b111111, 6)?;
183 bit_writer.write_bits(self.length_size_minus_one as u64, 2)?;
184 bit_writer.write_bits(0b111, 3)?;
185
186 bit_writer.write_bits(self.sps.len() as u64, 5)?;
187 for sps in &self.sps {
188 bit_writer.write_u16::<BigEndian>(sps.len() as u16)?;
189 bit_writer.write_all(sps.as_bytes())?;
190 }
191
192 bit_writer.write_bits(self.pps.len() as u64, 8)?;
193 for pps in &self.pps {
194 bit_writer.write_u16::<BigEndian>(pps.len() as u16)?;
195 bit_writer.write_all(pps.as_bytes())?;
196 }
197
198 if let Some(config) = &self.extended_config {
199 bit_writer.write_bits(0b111111, 6)?;
200 bit_writer.write_bits(config.chroma_format_idc as u64, 2)?;
201 bit_writer.write_bits(0b11111, 5)?;
202 bit_writer.write_bits(config.bit_depth_luma_minus8 as u64, 3)?;
203 bit_writer.write_bits(0b11111, 5)?;
204 bit_writer.write_bits(config.bit_depth_chroma_minus8 as u64, 3)?;
205
206 bit_writer.write_bits(config.sequence_parameter_set_ext.len() as u64, 8)?;
207 for sps_ext in &config.sequence_parameter_set_ext {
208 bit_writer.write_u16::<BigEndian>(sps_ext.bytesize() as u16)?;
209 sps_ext.build(&mut bit_writer)?;
218 bit_writer.align()?;
219 }
220 }
221
222 bit_writer.finish()?;
223
224 Ok(())
225 }
226}
227
228#[cfg(feature = "isobmff")]
229impl isobmff::IsoSized for AVCDecoderConfigurationRecord<'_> {
230 fn size(&self) -> usize {
232 1 + 1 + 1 + 1 + 1 + 1 + self.sps.iter().map(|sps| {
239 2 + sps.len()
241 }).sum::<usize>() + 1 + self.pps.iter().map(|pps| {
244 2 + pps.len()
246 }).sum::<usize>() + match &self.extended_config {
248 Some(config) => {
249 1 + 1 + 1 + 1 + config.sequence_parameter_set_ext.iter().map(|sps_ext| {
254 2 + sps_ext.bytesize() as usize }).sum::<usize>()
257 }
258 None => 0,
259 }
260 }
261}
262
263#[cfg(test)]
264#[cfg_attr(all(test, coverage_nightly), coverage(off))]
265mod tests {
266 use std::io::Write;
267
268 use byteorder::{BigEndian, WriteBytesExt};
269 use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
270 use scuffle_bytes_util::{BitWriter, BytesCow};
271
272 use crate::config::{AVCDecoderConfigurationRecord, AvccExtendedConfig};
273 use crate::sps::SpsExtended;
274
275 #[test]
276 fn test_config_parse() {
277 let sample_sps = b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0";
278 let mut data = Vec::new();
279 let mut writer = BitWriter::new(&mut data);
280
281 writer.write_bits(1, 8).unwrap();
283 writer.write_bits(100, 8).unwrap();
285 writer.write_bits(0, 8).unwrap();
287 writer.write_bits(31, 8).unwrap();
289 writer.write_bits(3, 8).unwrap();
291
292 writer.write_bits(1, 8).unwrap();
294 writer.write_u16::<BigEndian>(sample_sps.len() as u16).unwrap();
296 writer.write_all(sample_sps).unwrap();
299
300 writer.write_bits(1, 8).unwrap();
302 writer.write_bits(6, 16).unwrap();
304 writer.write_all(b"h\xeb\xe3\xcb\"\xc0\x00\x00").unwrap();
305
306 writer.write_bits(1, 8).unwrap();
308 writer.write_bits(0, 8).unwrap();
310 writer.write_bits(0, 8).unwrap();
312 writer.write_bits(0, 8).unwrap();
314 writer.finish().unwrap();
315
316 let result =
317 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
318
319 let sps = &result.sps[0];
320
321 assert_eq!(sps.as_bytes(), *sample_sps);
322 }
323
324 #[test]
325 #[cfg(feature = "isobmff")]
326 fn test_config_build() {
327 use isobmff::IsoSized;
328
329 let data = b"\x01d\0\x1f\xff\xe1\0\x19\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x03\x00\x08\x00\x00\x03\x01\xE0\x01\0\x06h\xeb\xe3\xcb\"\xc0\xfd\xf8\xf8\0";
333
334 let config =
335 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
336
337 assert_eq!(config.size(), data.len());
338
339 let mut buf = Vec::new();
340 config.serialize(&mut buf).unwrap();
341
342 assert_eq!(buf, data.to_vec());
343 }
344
345 #[test]
346 fn test_no_ext_cfg_for_profiles_66_77_88() {
347 let data = b"\x01B\x00\x1F\xFF\xE1\x00\x1Dgd\x00\x1F\xAC\xD9A\xE0m\xF9\xE6\xA0 (\x00\x00\x03\x00\x08\x00\x00\x03\x01\xE0x\xC1\x8C\xB0\x01\x00\x06h\xEB\xE3\xCB\"\xC0\xFD\xF8\xF8\x00";
348 let config =
349 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
350
351 assert_eq!(config.extended_config, None);
352 }
353
354 #[test]
355 #[cfg(feature = "isobmff")]
356 fn test_size_calculation_with_sequence_parameter_set_ext() {
357 use isobmff::IsoSized;
358
359 let extended_config = AvccExtendedConfig {
360 chroma_format_idc: 1,
361 bit_depth_luma_minus8: 0,
362 bit_depth_chroma_minus8: 0,
363 sequence_parameter_set_ext: vec![SpsExtended {
364 chroma_format_idc: 1,
365 separate_color_plane_flag: false,
366 bit_depth_luma_minus8: 2,
367 bit_depth_chroma_minus8: 3,
368 qpprime_y_zero_transform_bypass_flag: false,
369 scaling_matrix: vec![],
370 }],
371 };
372 let config = AVCDecoderConfigurationRecord {
373 configuration_version: 1,
374 profile_indication: 100,
375 profile_compatibility: 0,
376 level_indication: 31,
377 length_size_minus_one: 3,
378 sps: vec![BytesCow::from_static(
379 b"\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x00\x08\x00\x00\x01\xE0",
380 )],
381 pps: vec![BytesCow::from_static(b"ppsdata")],
382 extended_config: Some(extended_config),
383 };
384
385 assert_eq!(config.size(), 49);
386 insta::assert_debug_snapshot!(config, @r#"
387 AVCDecoderConfigurationRecord {
388 configuration_version: 1,
389 profile_indication: 100,
390 profile_compatibility: 0,
391 level_indication: 31,
392 length_size_minus_one: 3,
393 sps: [
394 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\0\x08\0\0\x01\xe0",
395 ],
396 pps: [
397 b"ppsdata",
398 ],
399 extended_config: Some(
400 AvccExtendedConfig {
401 chroma_format_idc: 1,
402 bit_depth_luma_minus8: 0,
403 bit_depth_chroma_minus8: 0,
404 sequence_parameter_set_ext: [
405 SpsExtended {
406 chroma_format_idc: 1,
407 separate_color_plane_flag: false,
408 bit_depth_luma_minus8: 2,
409 bit_depth_chroma_minus8: 3,
410 qpprime_y_zero_transform_bypass_flag: false,
411 scaling_matrix: [],
412 },
413 ],
414 },
415 ),
416 }
417 "#);
418 }
419
420 #[test]
421 fn test_build_with_sequence_parameter_set_ext() {
422 let extended_config = AvccExtendedConfig {
423 chroma_format_idc: 1,
424 bit_depth_luma_minus8: 0,
425 bit_depth_chroma_minus8: 0,
426 sequence_parameter_set_ext: vec![SpsExtended {
427 chroma_format_idc: 1,
428 separate_color_plane_flag: false,
429 bit_depth_luma_minus8: 2,
430 bit_depth_chroma_minus8: 3,
431 qpprime_y_zero_transform_bypass_flag: false,
432 scaling_matrix: vec![],
433 }],
434 };
435 let config = AVCDecoderConfigurationRecord {
436 configuration_version: 1,
437 profile_indication: 100,
438 profile_compatibility: 0,
439 level_indication: 31,
440 length_size_minus_one: 3,
441 sps: vec![BytesCow::from_static(
442 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
443 )],
444 pps: vec![BytesCow::from_static(b"ppsdata")],
445 extended_config: Some(extended_config),
446 };
447
448 let mut buf = Vec::new();
449 config.serialize(&mut buf).unwrap();
450
451 let parsed =
452 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&buf[..])).unwrap();
453 assert_eq!(parsed.extended_config.unwrap().sequence_parameter_set_ext.len(), 1);
454 insta::assert_debug_snapshot!(config, @r#"
455 AVCDecoderConfigurationRecord {
456 configuration_version: 1,
457 profile_indication: 100,
458 profile_compatibility: 0,
459 level_indication: 31,
460 length_size_minus_one: 3,
461 sps: [
462 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
463 ],
464 pps: [
465 b"ppsdata",
466 ],
467 extended_config: Some(
468 AvccExtendedConfig {
469 chroma_format_idc: 1,
470 bit_depth_luma_minus8: 0,
471 bit_depth_chroma_minus8: 0,
472 sequence_parameter_set_ext: [
473 SpsExtended {
474 chroma_format_idc: 1,
475 separate_color_plane_flag: false,
476 bit_depth_luma_minus8: 2,
477 bit_depth_chroma_minus8: 3,
478 qpprime_y_zero_transform_bypass_flag: false,
479 scaling_matrix: [],
480 },
481 ],
482 },
483 ),
484 }
485 "#);
486 }
487}