1use std::io::{
2 Write, {self},
3};
4
5use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6use bytes::{Buf, Bytes};
7use scuffle_bytes_util::{BitReader, BitWriter, BytesCursorExt};
8
9use crate::sps::SpsExtended;
10
11#[derive(Debug, Clone, PartialEq)]
14pub struct AVCDecoderConfigurationRecord {
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<Bytes>,
46
47 pub pps: Vec<Bytes>,
55
56 pub extended_config: Option<AvccExtendedConfig>,
60}
61
62#[derive(Debug, Clone, PartialEq)]
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 AVCDecoderConfigurationRecord {
95 pub fn parse(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
98 let configuration_version = reader.read_u8()?;
99 let profile_indication = reader.read_u8()?;
100 let profile_compatibility = reader.read_u8()?;
101 let level_indication = reader.read_u8()?;
102 let length_size_minus_one = reader.read_u8()? & 0b00000011;
103 let num_of_sequence_parameter_sets = reader.read_u8()? & 0b00011111;
104
105 let mut sps = Vec::with_capacity(num_of_sequence_parameter_sets as usize);
106 for _ in 0..num_of_sequence_parameter_sets {
107 let sps_length = reader.read_u16::<BigEndian>()?;
108 let sps_data = reader.extract_bytes(sps_length as usize)?;
109 sps.push(sps_data);
110 }
111
112 let num_of_picture_parameter_sets = reader.read_u8()?;
113 let mut pps = Vec::with_capacity(num_of_picture_parameter_sets as usize);
114 for _ in 0..num_of_picture_parameter_sets {
115 let pps_length = reader.read_u16::<BigEndian>()?;
116 let pps_data = reader.extract_bytes(pps_length as usize)?;
117 pps.push(pps_data);
118 }
119
120 let extended_config = match profile_indication {
124 66 | 77 | 88 => None,
125 _ => {
126 if reader.has_remaining() {
127 let chroma_format_idc = reader.read_u8()? & 0b00000011; let bit_depth_luma_minus8 = reader.read_u8()? & 0b00000111; let bit_depth_chroma_minus8 = reader.read_u8()? & 0b00000111; let number_of_sequence_parameter_set_ext = reader.read_u8()?; let mut sequence_parameter_set_ext = Vec::with_capacity(number_of_sequence_parameter_set_ext as usize);
133 for _ in 0..number_of_sequence_parameter_set_ext {
134 let sps_ext_length = reader.read_u16::<BigEndian>()?;
135 let sps_ext_data = reader.extract_bytes(sps_ext_length as usize)?;
136
137 let mut bit_reader = BitReader::new_from_slice(sps_ext_data);
138 let sps_ext_parsed = SpsExtended::parse(&mut bit_reader)?;
139 sequence_parameter_set_ext.push(sps_ext_parsed);
140 }
141
142 Some(AvccExtendedConfig {
143 chroma_format_idc,
144 bit_depth_luma_minus8,
145 bit_depth_chroma_minus8,
146 sequence_parameter_set_ext,
147 })
148 } else {
149 None
152 }
153 }
154 };
155
156 Ok(Self {
157 configuration_version,
158 profile_indication,
159 profile_compatibility,
160 level_indication,
161 length_size_minus_one,
162 sps,
163 pps,
164 extended_config,
165 })
166 }
167
168 pub fn size(&self) -> u64 {
170 1 + 1 + 1 + 1 + 1 + 1 + self.sps.iter().map(|sps| {
177 2 + sps.len() as u64
179 }).sum::<u64>() + 1 + self.pps.iter().map(|pps| {
182 2 + pps.len() as u64
184 }).sum::<u64>() + match &self.extended_config {
186 Some(config) => {
187 1 + 1 + 1 + 1 + config.sequence_parameter_set_ext.iter().map(|sps_ext| {
192 2 + sps_ext.bytesize() }).sum::<u64>()
195 }
196 None => 0,
197 }
198 }
199
200 pub fn build<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
203 let mut bit_writer = BitWriter::new(writer);
204
205 bit_writer.write_u8(self.configuration_version)?;
206 bit_writer.write_u8(self.profile_indication)?;
207 bit_writer.write_u8(self.profile_compatibility)?;
208 bit_writer.write_u8(self.level_indication)?;
209 bit_writer.write_bits(0b111111, 6)?;
210 bit_writer.write_bits(self.length_size_minus_one as u64, 2)?;
211 bit_writer.write_bits(0b111, 3)?;
212
213 bit_writer.write_bits(self.sps.len() as u64, 5)?;
214 for sps in &self.sps {
215 bit_writer.write_u16::<BigEndian>(sps.len() as u16)?;
216 bit_writer.write_all(sps)?;
217 }
218
219 bit_writer.write_bits(self.pps.len() as u64, 8)?;
220 for pps in &self.pps {
221 bit_writer.write_u16::<BigEndian>(pps.len() as u16)?;
222 bit_writer.write_all(pps)?;
223 }
224
225 if let Some(config) = &self.extended_config {
226 bit_writer.write_bits(0b111111, 6)?;
227 bit_writer.write_bits(config.chroma_format_idc as u64, 2)?;
228 bit_writer.write_bits(0b11111, 5)?;
229 bit_writer.write_bits(config.bit_depth_luma_minus8 as u64, 3)?;
230 bit_writer.write_bits(0b11111, 5)?;
231 bit_writer.write_bits(config.bit_depth_chroma_minus8 as u64, 3)?;
232
233 bit_writer.write_bits(config.sequence_parameter_set_ext.len() as u64, 8)?;
234 for sps_ext in &config.sequence_parameter_set_ext {
235 bit_writer.write_u16::<BigEndian>(sps_ext.bytesize() as u16)?;
236 sps_ext.build(&mut bit_writer)?;
245 bit_writer.align()?;
246 }
247 }
248
249 bit_writer.finish()?;
250
251 Ok(())
252 }
253}
254
255#[cfg(test)]
256#[cfg_attr(all(test, coverage_nightly), coverage(off))]
257mod tests {
258 use std::io::{self, Write};
259
260 use byteorder::{BigEndian, WriteBytesExt};
261 use bytes::Bytes;
262 use scuffle_bytes_util::BitWriter;
263
264 use crate::config::{AVCDecoderConfigurationRecord, AvccExtendedConfig};
265 use crate::sps::SpsExtended;
266
267 #[test]
268 fn test_config_parse() {
269 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";
270 let mut data = Vec::new();
271 let mut writer = BitWriter::new(&mut data);
272
273 writer.write_bits(1, 8).unwrap();
275 writer.write_bits(100, 8).unwrap();
277 writer.write_bits(0, 8).unwrap();
279 writer.write_bits(31, 8).unwrap();
281 writer.write_bits(3, 8).unwrap();
283
284 writer.write_bits(1, 8).unwrap();
286 writer.write_u16::<BigEndian>(sample_sps.len() as u16).unwrap();
288 writer.write_all(sample_sps).unwrap();
291
292 writer.write_bits(1, 8).unwrap();
294 writer.write_bits(6, 16).unwrap();
296 writer.write_all(b"h\xeb\xe3\xcb\"\xc0\x00\x00").unwrap();
297
298 writer.write_bits(1, 8).unwrap();
300 writer.write_bits(0, 8).unwrap();
302 writer.write_bits(0, 8).unwrap();
304 writer.write_bits(0, 8).unwrap();
306 writer.finish().unwrap();
307
308 let result = AVCDecoderConfigurationRecord::parse(&mut io::Cursor::new(data.into())).unwrap();
309
310 let sps = &result.sps[0];
311
312 assert_eq!(**sps, *sample_sps);
313 }
314
315 #[test]
316 fn test_config_build() {
317 let data = Bytes::from(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".to_vec());
321
322 let config = AVCDecoderConfigurationRecord::parse(&mut io::Cursor::new(data.clone())).unwrap();
323
324 assert_eq!(config.size(), data.len() as u64);
325
326 let mut buf = Vec::new();
327 config.build(&mut buf).unwrap();
328
329 assert_eq!(buf, data.to_vec());
330 }
331
332 #[test]
333 fn test_no_ext_cfg_for_profiles_66_77_88() {
334 let data = Bytes::from(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".to_vec());
335 let config = AVCDecoderConfigurationRecord::parse(&mut io::Cursor::new(data)).unwrap();
336
337 assert_eq!(config.extended_config, None);
338 }
339
340 #[test]
341 fn test_size_calculation_with_sequence_parameter_set_ext() {
342 let extended_config = AvccExtendedConfig {
343 chroma_format_idc: 1,
344 bit_depth_luma_minus8: 0,
345 bit_depth_chroma_minus8: 0,
346 sequence_parameter_set_ext: vec![SpsExtended {
347 chroma_format_idc: 1,
348 separate_color_plane_flag: false,
349 bit_depth_luma_minus8: 2,
350 bit_depth_chroma_minus8: 3,
351 qpprime_y_zero_transform_bypass_flag: false,
352 scaling_matrix: vec![],
353 }],
354 };
355 let config = AVCDecoderConfigurationRecord {
356 configuration_version: 1,
357 profile_indication: 100,
358 profile_compatibility: 0,
359 level_indication: 31,
360 length_size_minus_one: 3,
361 sps: vec![Bytes::from_static(
362 b"\x67\x64\x00\x1F\xAC\xD9\x41\xE0\x6D\xF9\xE6\xA0\x20\x20\x28\x00\x00\x00\x08\x00\x00\x01\xE0",
363 )],
364 pps: vec![Bytes::from_static(b"ppsdata")],
365 extended_config: Some(extended_config),
366 };
367
368 assert_eq!(config.size(), 49);
369 insta::assert_debug_snapshot!(config, @r#"
370 AVCDecoderConfigurationRecord {
371 configuration_version: 1,
372 profile_indication: 100,
373 profile_compatibility: 0,
374 level_indication: 31,
375 length_size_minus_one: 3,
376 sps: [
377 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\0\x08\0\0\x01\xe0",
378 ],
379 pps: [
380 b"ppsdata",
381 ],
382 extended_config: Some(
383 AvccExtendedConfig {
384 chroma_format_idc: 1,
385 bit_depth_luma_minus8: 0,
386 bit_depth_chroma_minus8: 0,
387 sequence_parameter_set_ext: [
388 SpsExtended {
389 chroma_format_idc: 1,
390 separate_color_plane_flag: false,
391 bit_depth_luma_minus8: 2,
392 bit_depth_chroma_minus8: 3,
393 qpprime_y_zero_transform_bypass_flag: false,
394 scaling_matrix: [],
395 },
396 ],
397 },
398 ),
399 }
400 "#);
401 }
402
403 #[test]
404 fn test_build_with_sequence_parameter_set_ext() {
405 let extended_config = AvccExtendedConfig {
406 chroma_format_idc: 1,
407 bit_depth_luma_minus8: 0,
408 bit_depth_chroma_minus8: 0,
409 sequence_parameter_set_ext: vec![SpsExtended {
410 chroma_format_idc: 1,
411 separate_color_plane_flag: false,
412 bit_depth_luma_minus8: 2,
413 bit_depth_chroma_minus8: 3,
414 qpprime_y_zero_transform_bypass_flag: false,
415 scaling_matrix: vec![],
416 }],
417 };
418 let config = AVCDecoderConfigurationRecord {
419 configuration_version: 1,
420 profile_indication: 100,
421 profile_compatibility: 0,
422 level_indication: 31,
423 length_size_minus_one: 3,
424 sps: vec![Bytes::from_static(
425 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
426 )],
427 pps: vec![Bytes::from_static(b"ppsdata")],
428 extended_config: Some(extended_config),
429 };
430
431 let mut buf = Vec::new();
432 config.build(&mut buf).unwrap();
433
434 let parsed = AVCDecoderConfigurationRecord::parse(&mut io::Cursor::new(buf.into())).unwrap();
435 assert_eq!(parsed.extended_config.unwrap().sequence_parameter_set_ext.len(), 1);
436 insta::assert_debug_snapshot!(config, @r#"
437 AVCDecoderConfigurationRecord {
438 configuration_version: 1,
439 profile_indication: 100,
440 profile_compatibility: 0,
441 level_indication: 31,
442 length_size_minus_one: 3,
443 sps: [
444 b"gd\0\x1f\xac\xd9A\xe0m\xf9\xe6\xa0 (\0\0\x03\0\x08\0\0\x03\x01\xe0x\xc1\x8c\xb0",
445 ],
446 pps: [
447 b"ppsdata",
448 ],
449 extended_config: Some(
450 AvccExtendedConfig {
451 chroma_format_idc: 1,
452 bit_depth_luma_minus8: 0,
453 bit_depth_chroma_minus8: 0,
454 sequence_parameter_set_ext: [
455 SpsExtended {
456 chroma_format_idc: 1,
457 separate_color_plane_flag: false,
458 bit_depth_luma_minus8: 2,
459 bit_depth_chroma_minus8: 3,
460 qpprime_y_zero_transform_bypass_flag: false,
461 scaling_matrix: [],
462 },
463 ],
464 },
465 ),
466 }
467 "#);
468 }
469}