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 isobmff::IsoSized;
270 use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
271 use scuffle_bytes_util::{BitWriter, BytesCow};
272
273 use crate::config::{AVCDecoderConfigurationRecord, AvccExtendedConfig};
274 use crate::sps::SpsExtended;
275
276 #[test]
277 fn test_config_parse() {
278 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";
279 let mut data = Vec::new();
280 let mut writer = BitWriter::new(&mut data);
281
282 writer.write_bits(1, 8).unwrap();
284 writer.write_bits(100, 8).unwrap();
286 writer.write_bits(0, 8).unwrap();
288 writer.write_bits(31, 8).unwrap();
290 writer.write_bits(3, 8).unwrap();
292
293 writer.write_bits(1, 8).unwrap();
295 writer.write_u16::<BigEndian>(sample_sps.len() as u16).unwrap();
297 writer.write_all(sample_sps).unwrap();
300
301 writer.write_bits(1, 8).unwrap();
303 writer.write_bits(6, 16).unwrap();
305 writer.write_all(b"h\xeb\xe3\xcb\"\xc0\x00\x00").unwrap();
306
307 writer.write_bits(1, 8).unwrap();
309 writer.write_bits(0, 8).unwrap();
311 writer.write_bits(0, 8).unwrap();
313 writer.write_bits(0, 8).unwrap();
315 writer.finish().unwrap();
316
317 let result =
318 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
319
320 let sps = &result.sps[0];
321
322 assert_eq!(sps.as_bytes(), *sample_sps);
323 }
324
325 #[test]
326 fn test_config_build() {
327 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";
331
332 let config =
333 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
334
335 assert_eq!(config.size(), data.len());
336
337 let mut buf = Vec::new();
338 config.serialize(&mut buf).unwrap();
339
340 assert_eq!(buf, data.to_vec());
341 }
342
343 #[test]
344 fn test_no_ext_cfg_for_profiles_66_77_88() {
345 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";
346 let config =
347 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&data[..])).unwrap();
348
349 assert_eq!(config.extended_config, None);
350 }
351
352 #[test]
353 fn test_size_calculation_with_sequence_parameter_set_ext() {
354 let extended_config = AvccExtendedConfig {
355 chroma_format_idc: 1,
356 bit_depth_luma_minus8: 0,
357 bit_depth_chroma_minus8: 0,
358 sequence_parameter_set_ext: vec![SpsExtended {
359 chroma_format_idc: 1,
360 separate_color_plane_flag: false,
361 bit_depth_luma_minus8: 2,
362 bit_depth_chroma_minus8: 3,
363 qpprime_y_zero_transform_bypass_flag: false,
364 scaling_matrix: vec![],
365 }],
366 };
367 let config = AVCDecoderConfigurationRecord {
368 configuration_version: 1,
369 profile_indication: 100,
370 profile_compatibility: 0,
371 level_indication: 31,
372 length_size_minus_one: 3,
373 sps: vec![BytesCow::from_static(
374 b"\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x00\x08\x00\x00\x01\xE0",
375 )],
376 pps: vec![BytesCow::from_static(b"ppsdata")],
377 extended_config: Some(extended_config),
378 };
379
380 assert_eq!(config.size(), 49);
381 insta::assert_debug_snapshot!(config, @r#"
382 AVCDecoderConfigurationRecord {
383 configuration_version: 1,
384 profile_indication: 100,
385 profile_compatibility: 0,
386 level_indication: 31,
387 length_size_minus_one: 3,
388 sps: [
389 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\0\x08\0\0\x01\xe0",
390 ],
391 pps: [
392 b"ppsdata",
393 ],
394 extended_config: Some(
395 AvccExtendedConfig {
396 chroma_format_idc: 1,
397 bit_depth_luma_minus8: 0,
398 bit_depth_chroma_minus8: 0,
399 sequence_parameter_set_ext: [
400 SpsExtended {
401 chroma_format_idc: 1,
402 separate_color_plane_flag: false,
403 bit_depth_luma_minus8: 2,
404 bit_depth_chroma_minus8: 3,
405 qpprime_y_zero_transform_bypass_flag: false,
406 scaling_matrix: [],
407 },
408 ],
409 },
410 ),
411 }
412 "#);
413 }
414
415 #[test]
416 fn test_build_with_sequence_parameter_set_ext() {
417 let extended_config = AvccExtendedConfig {
418 chroma_format_idc: 1,
419 bit_depth_luma_minus8: 0,
420 bit_depth_chroma_minus8: 0,
421 sequence_parameter_set_ext: vec![SpsExtended {
422 chroma_format_idc: 1,
423 separate_color_plane_flag: false,
424 bit_depth_luma_minus8: 2,
425 bit_depth_chroma_minus8: 3,
426 qpprime_y_zero_transform_bypass_flag: false,
427 scaling_matrix: vec![],
428 }],
429 };
430 let config = AVCDecoderConfigurationRecord {
431 configuration_version: 1,
432 profile_indication: 100,
433 profile_compatibility: 0,
434 level_indication: 31,
435 length_size_minus_one: 3,
436 sps: vec![BytesCow::from_static(
437 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
438 )],
439 pps: vec![BytesCow::from_static(b"ppsdata")],
440 extended_config: Some(extended_config),
441 };
442
443 let mut buf = Vec::new();
444 config.serialize(&mut buf).unwrap();
445
446 let parsed =
447 AVCDecoderConfigurationRecord::deserialize(scuffle_bytes_util::zero_copy::Slice::from(&buf[..])).unwrap();
448 assert_eq!(parsed.extended_config.unwrap().sequence_parameter_set_ext.len(), 1);
449 insta::assert_debug_snapshot!(config, @r#"
450 AVCDecoderConfigurationRecord {
451 configuration_version: 1,
452 profile_indication: 100,
453 profile_compatibility: 0,
454 level_indication: 31,
455 length_size_minus_one: 3,
456 sps: [
457 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
458 ],
459 pps: [
460 b"ppsdata",
461 ],
462 extended_config: Some(
463 AvccExtendedConfig {
464 chroma_format_idc: 1,
465 bit_depth_luma_minus8: 0,
466 bit_depth_chroma_minus8: 0,
467 sequence_parameter_set_ext: [
468 SpsExtended {
469 chroma_format_idc: 1,
470 separate_color_plane_flag: false,
471 bit_depth_luma_minus8: 2,
472 bit_depth_chroma_minus8: 3,
473 qpprime_y_zero_transform_bypass_flag: false,
474 scaling_matrix: [],
475 },
476 ],
477 },
478 ),
479 }
480 "#);
481 }
482}