1use std::io::{self, Read};
6
7use byteorder::{BigEndian, ReadBytesExt};
8use bytes::{Buf, Bytes};
9use nutype_enum::nutype_enum;
10use scuffle_bytes_util::BytesCursorExt;
11
12use crate::audio::header::enhanced::{AudioFourCc, AudioPacketType, ExAudioTagHeader, ExAudioTagHeaderContent};
13
14nutype_enum! {
15 pub enum AudioChannelOrder(u8) {
20 Unspecified = 0,
22 Native = 1,
24 Custom = 2,
26 }
27}
28
29nutype_enum! {
30 pub enum AudioChannel(u8) {
35 FrontLeft = 0,
39 FrontRight = 1,
41 FrontCenter = 2,
43 LowFrequency1 = 3,
45 BackLeft = 4,
47 BackRight = 5,
49 FrontLeftCenter = 6,
51 FrontRightCenter = 7,
53 BackCenter = 8,
55 SideLeft = 9,
57 SideRight = 10,
59 TopCenter = 11,
61 TopFrontLeft = 12,
63 TopFrontCenter = 13,
65 TopFrontRight = 14,
67 TopBackLeft = 15,
69 TopBackCenter = 16,
71 TopBackRight = 17,
73
74 LowFrequency2 = 18,
78 TopSideLeft = 19,
80 TopSideRight = 20,
82 BottomFrontCenter = 21,
84 BottomFrontLeft = 22,
86 BottomFrontRight = 23,
88 Unused = 0xfe,
90 Unknown = 0xff,
92 }
93}
94
95#[bitmask_enum::bitmask(u32)]
100pub enum AudioChannelMask {
101 FrontLeft = 0x000001,
104 FrontRight = 0x000002,
106 FrontCenter = 0x000004,
108 LowFrequency1 = 0x000008,
110 BackLeft = 0x000010,
112 BackRight = 0x000020,
114 FrontLeftCenter = 0x000040,
116 FrontRightCenter = 0x000080,
118 BackCenter = 0x000100,
120 SideLeft = 0x000200,
122 SideRight = 0x000400,
124 TopCenter = 0x000800,
126 TopFrontLeft = 0x001000,
128 TopFrontCenter = 0x002000,
130 TopFrontRight = 0x004000,
132 TopBackLeft = 0x008000,
134 TopBackCenter = 0x010000,
136 TopBackRight = 0x020000,
138
139 LowFrequency2 = 0x040000,
143 TopSideLeft = 0x080000,
145 TopSideRight = 0x100000,
147 BottomFrontCenter = 0x200000,
149 BottomFrontLeft = 0x400000,
151 BottomFrontRight = 0x800000,
153}
154
155#[derive(Debug, Clone, PartialEq)]
161pub enum MultichannelConfigOrder {
162 Custom(Vec<AudioChannel>),
166 Native(AudioChannelMask),
175 Unspecified,
177 Unknown(AudioChannelOrder),
182}
183
184#[derive(Debug, Clone, PartialEq)]
191pub enum AudioPacket {
192 MultichannelConfig {
197 channel_count: u8,
199 multichannel_config: MultichannelConfigOrder,
203 },
204 SequenceEnd,
206 SequenceStart {
208 header_data: Bytes,
210 },
211 CodedFrames {
213 data: Bytes,
215 },
216 Unknown {
218 audio_packet_type: AudioPacketType,
220 data: Bytes,
222 },
223}
224
225impl AudioPacket {
226 pub fn demux(header: &ExAudioTagHeader, reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
230 let has_multiple_tracks = !matches!(
231 header.content,
232 ExAudioTagHeaderContent::NoMultiTrack(_) | ExAudioTagHeaderContent::OneTrack(_)
233 );
234
235 let size_of_audio_track = if has_multiple_tracks {
236 Some(reader.read_u24::<BigEndian>()? as usize)
237 } else {
238 None
239 };
240
241 match header.audio_packet_type {
242 AudioPacketType::MultichannelConfig => {
243 let audio_channel_order = AudioChannelOrder::from(reader.read_u8()?);
244 let channel_count = reader.read_u8()?;
245
246 let multichannel_config = match audio_channel_order {
247 AudioChannelOrder::Custom => {
248 let channels = reader.extract_bytes(channel_count as usize)?;
249
250 MultichannelConfigOrder::Custom(channels.into_iter().map(AudioChannel::from).collect())
251 }
252 AudioChannelOrder::Native => {
253 let audio_channel_flags = AudioChannelMask::from(reader.read_u32::<BigEndian>()?);
254
255 MultichannelConfigOrder::Native(audio_channel_flags)
256 }
257 AudioChannelOrder::Unspecified => MultichannelConfigOrder::Unspecified,
258 _ => MultichannelConfigOrder::Unknown(audio_channel_order),
259 };
260
261 Ok(Self::MultichannelConfig {
262 channel_count,
263 multichannel_config,
264 })
265 }
266 AudioPacketType::SequenceEnd => Ok(Self::SequenceEnd),
267 AudioPacketType::SequenceStart => {
268 let header_data = reader.extract_bytes(size_of_audio_track.unwrap_or(reader.remaining()))?;
269
270 Ok(Self::SequenceStart { header_data })
271 }
272 AudioPacketType::CodedFrames => {
273 let data = reader.extract_bytes(size_of_audio_track.unwrap_or(reader.remaining()))?;
274
275 Ok(Self::CodedFrames { data })
276 }
277 _ => {
278 let data = reader.extract_bytes(size_of_audio_track.unwrap_or(reader.remaining()))?;
279
280 Ok(Self::Unknown {
281 audio_packet_type: header.audio_packet_type,
282 data,
283 })
284 }
285 }
286 }
287}
288
289#[derive(Debug, Clone, PartialEq)]
291pub struct AudioTrack {
292 pub audio_four_cc: AudioFourCc,
294 pub audio_track_id: u8,
306 pub packet: AudioPacket,
308}
309
310#[derive(Debug, Clone, PartialEq)]
315pub enum ExAudioTagBody {
316 NoMultitrack {
318 audio_four_cc: AudioFourCc,
320 packet: AudioPacket,
322 },
323 ManyTracks(Vec<AudioTrack>),
328}
329
330impl ExAudioTagBody {
331 pub fn demux(header: &ExAudioTagHeader, reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
335 let mut tracks = Vec::new();
336
337 loop {
338 let audio_four_cc = match header.content {
339 ExAudioTagHeaderContent::ManyTracksManyCodecs => {
340 let mut audio_four_cc = [0; 4];
341 reader.read_exact(&mut audio_four_cc)?;
342 AudioFourCc::from(audio_four_cc)
343 }
344 ExAudioTagHeaderContent::OneTrack(audio_four_cc) => audio_four_cc,
345 ExAudioTagHeaderContent::ManyTracks(audio_four_cc) => audio_four_cc,
346 ExAudioTagHeaderContent::NoMultiTrack(audio_four_cc) => audio_four_cc,
347 ExAudioTagHeaderContent::Unknown { audio_four_cc, .. } => audio_four_cc,
348 };
349
350 let audio_track_id = if !matches!(header.content, ExAudioTagHeaderContent::NoMultiTrack(_)) {
352 Some(reader.read_u8()?)
353 } else {
354 None
355 };
356
357 let packet = AudioPacket::demux(header, reader)?;
358
359 if let Some(audio_track_id) = audio_track_id {
360 tracks.push(AudioTrack {
362 audio_four_cc,
363 audio_track_id,
364 packet,
365 });
366
367 if !matches!(header.content, ExAudioTagHeaderContent::OneTrack(_)) && reader.has_remaining() {
369 continue;
370 }
371
372 break;
373 } else {
374 return Ok(Self::NoMultitrack { audio_four_cc, packet });
376 }
377 }
378
379 Ok(Self::ManyTracks(tracks))
381 }
382}
383
384#[cfg(test)]
385#[cfg_attr(all(test, coverage_nightly), coverage(off))]
386mod tests {
387 use bytes::Bytes;
388
389 use super::AudioPacket;
390 use crate::audio::body::enhanced::{
391 AudioChannel, AudioChannelMask, AudioChannelOrder, AudioTrack, ExAudioTagBody, MultichannelConfigOrder,
392 };
393 use crate::audio::header::enhanced::{AudioFourCc, AudioPacketType, ExAudioTagHeader, ExAudioTagHeaderContent};
394 use crate::common::AvMultitrackType;
395
396 #[test]
397 fn simple_audio_packets_demux() {
398 let data = &[42, 42, 42, 42];
399
400 let packet = AudioPacket::demux(
401 &ExAudioTagHeader {
402 audio_packet_mod_exs: vec![],
403 audio_packet_type: AudioPacketType::SequenceStart,
404 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
405 },
406 &mut std::io::Cursor::new(Bytes::from_static(data)),
407 )
408 .unwrap();
409 assert_eq!(
410 packet,
411 AudioPacket::SequenceStart {
412 header_data: Bytes::from_static(data)
413 }
414 );
415
416 let packet = AudioPacket::demux(
417 &ExAudioTagHeader {
418 audio_packet_mod_exs: vec![],
419 audio_packet_type: AudioPacketType::CodedFrames,
420 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
421 },
422 &mut std::io::Cursor::new(Bytes::from_static(data)),
423 )
424 .unwrap();
425 assert_eq!(
426 packet,
427 AudioPacket::CodedFrames {
428 data: Bytes::from_static(data)
429 }
430 );
431
432 let packet = AudioPacket::demux(
433 &ExAudioTagHeader {
434 audio_packet_mod_exs: vec![],
435 audio_packet_type: AudioPacketType::SequenceEnd,
436 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
437 },
438 &mut std::io::Cursor::new(Bytes::from_static(data)),
439 )
440 .unwrap();
441 assert_eq!(packet, AudioPacket::SequenceEnd,);
442
443 let packet = AudioPacket::demux(
444 &ExAudioTagHeader {
445 audio_packet_mod_exs: vec![],
446 audio_packet_type: AudioPacketType(8),
447 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
448 },
449 &mut std::io::Cursor::new(Bytes::from_static(data)),
450 )
451 .unwrap();
452 assert_eq!(
453 packet,
454 AudioPacket::Unknown {
455 audio_packet_type: AudioPacketType(8),
456 data: Bytes::from_static(data),
457 },
458 );
459 }
460
461 #[test]
462 fn audio_packet_with_size_demux() {
463 let data = &[
464 0, 0, 2, 42, 42, 13, 37, ];
468
469 let header = ExAudioTagHeader {
470 audio_packet_mod_exs: vec![],
471 audio_packet_type: AudioPacketType::CodedFrames,
472 content: ExAudioTagHeaderContent::ManyTracks(AudioFourCc::Aac),
473 };
474
475 let packet = AudioPacket::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
476
477 assert_eq!(
478 packet,
479 AudioPacket::CodedFrames {
480 data: Bytes::from_static(&[42, 42])
481 },
482 );
483 }
484
485 #[test]
486 fn audio_packet_custom_multichannel_config_demux() {
487 let data = &[
488 2, 2, 0, 1, ];
492
493 let header = ExAudioTagHeader {
494 audio_packet_mod_exs: vec![],
495 audio_packet_type: AudioPacketType::MultichannelConfig,
496 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
497 };
498
499 let packet = AudioPacket::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
500
501 assert_eq!(
502 packet,
503 AudioPacket::MultichannelConfig {
504 channel_count: 2,
505 multichannel_config: MultichannelConfigOrder::Custom(vec![
506 AudioChannel::FrontLeft,
507 AudioChannel::FrontRight
508 ])
509 },
510 );
511 }
512
513 #[test]
514 fn audio_packet_native_multichannel_config_demux() {
515 let data = &[
516 1, 2, 0, 0, 0, 3, ];
520
521 let header = ExAudioTagHeader {
522 audio_packet_mod_exs: vec![],
523 audio_packet_type: AudioPacketType::MultichannelConfig,
524 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
525 };
526
527 let packet = AudioPacket::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
528
529 assert_eq!(
530 packet,
531 AudioPacket::MultichannelConfig {
532 channel_count: 2,
533 multichannel_config: MultichannelConfigOrder::Native(
534 AudioChannelMask::FrontLeft | AudioChannelMask::FrontRight
535 )
536 },
537 );
538 }
539
540 #[test]
541 fn audio_packet_other_multichannel_config_demux() {
542 let data = &[
543 0, 2, ];
546
547 let header = ExAudioTagHeader {
548 audio_packet_mod_exs: vec![],
549 audio_packet_type: AudioPacketType::MultichannelConfig,
550 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
551 };
552
553 let packet = AudioPacket::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
554
555 assert_eq!(
556 packet,
557 AudioPacket::MultichannelConfig {
558 channel_count: 2,
559 multichannel_config: MultichannelConfigOrder::Unspecified,
560 },
561 );
562
563 let data = &[
564 4, 2, ];
567
568 let header = ExAudioTagHeader {
569 audio_packet_mod_exs: vec![],
570 audio_packet_type: AudioPacketType::MultichannelConfig,
571 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
572 };
573
574 let packet = AudioPacket::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
575
576 assert_eq!(
577 packet,
578 AudioPacket::MultichannelConfig {
579 channel_count: 2,
580 multichannel_config: MultichannelConfigOrder::Unknown(AudioChannelOrder(4)),
581 },
582 );
583 }
584
585 #[test]
586 fn simple_body_demux() {
587 let data = &[
588 42, 42, ];
590
591 let header = ExAudioTagHeader {
592 audio_packet_mod_exs: vec![],
593 audio_packet_type: AudioPacketType::CodedFrames,
594 content: ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac),
595 };
596
597 let packet = ExAudioTagBody::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
598
599 assert_eq!(
600 packet,
601 ExAudioTagBody::NoMultitrack {
602 audio_four_cc: AudioFourCc::Aac,
603 packet: AudioPacket::CodedFrames {
604 data: Bytes::from_static(data)
605 },
606 },
607 );
608 }
609
610 #[test]
611 fn multitrack_many_codecs_body_demux() {
612 let data = &[
613 b'm', b'p', b'4', b'a', 1, 0, 0, 2, 42, 42, b'O', b'p', b'u', b's', 2, 0, 0, 2, 13, 37, ];
622
623 let header = ExAudioTagHeader {
624 audio_packet_mod_exs: vec![],
625 audio_packet_type: AudioPacketType::CodedFrames,
626 content: ExAudioTagHeaderContent::ManyTracksManyCodecs,
627 };
628
629 let packet = ExAudioTagBody::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
630
631 assert_eq!(
632 packet,
633 ExAudioTagBody::ManyTracks(vec![
634 AudioTrack {
635 audio_four_cc: AudioFourCc::Aac,
636 audio_track_id: 1,
637 packet: AudioPacket::CodedFrames {
638 data: Bytes::from_static(&[42, 42])
639 },
640 },
641 AudioTrack {
642 audio_four_cc: AudioFourCc::Opus,
643 audio_track_id: 2,
644 packet: AudioPacket::CodedFrames {
645 data: Bytes::from_static(&[13, 37])
646 },
647 }
648 ]),
649 );
650 }
651
652 #[test]
653 fn multitrack_body_demux() {
654 let data = &[
655 1, 0, 0, 2, 42, 42, 2, 0, 0, 2, 13, 37, ];
662
663 let header = ExAudioTagHeader {
664 audio_packet_mod_exs: vec![],
665 audio_packet_type: AudioPacketType::CodedFrames,
666 content: ExAudioTagHeaderContent::ManyTracks(AudioFourCc::Aac),
667 };
668
669 let packet = ExAudioTagBody::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
670
671 assert_eq!(
672 packet,
673 ExAudioTagBody::ManyTracks(vec![
674 AudioTrack {
675 audio_four_cc: AudioFourCc::Aac,
676 audio_track_id: 1,
677 packet: AudioPacket::CodedFrames {
678 data: Bytes::from_static(&[42, 42])
679 },
680 },
681 AudioTrack {
682 audio_four_cc: AudioFourCc::Aac,
683 audio_track_id: 2,
684 packet: AudioPacket::CodedFrames {
685 data: Bytes::from_static(&[13, 37])
686 },
687 }
688 ]),
689 );
690 }
691
692 #[test]
693 fn multitrack_one_track_body_demux() {
694 let data = &[
695 1, 42, 42, ];
698
699 let header = ExAudioTagHeader {
700 audio_packet_mod_exs: vec![],
701 audio_packet_type: AudioPacketType::CodedFrames,
702 content: ExAudioTagHeaderContent::OneTrack(AudioFourCc::Aac),
703 };
704
705 let packet = ExAudioTagBody::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
706
707 assert_eq!(
708 packet,
709 ExAudioTagBody::ManyTracks(vec![AudioTrack {
710 audio_four_cc: AudioFourCc::Aac,
711 audio_track_id: 1,
712 packet: AudioPacket::CodedFrames {
713 data: Bytes::from_static(&[42, 42])
714 },
715 }]),
716 );
717 }
718
719 #[test]
720 fn multitrack_unknown_body_demux() {
721 let data = &[
722 1, 0, 0, 2, 42, 42, ];
726
727 let header = ExAudioTagHeader {
728 audio_packet_mod_exs: vec![],
729 audio_packet_type: AudioPacketType::CodedFrames,
730 content: ExAudioTagHeaderContent::Unknown {
731 audio_four_cc: AudioFourCc::Aac,
732 audio_multitrack_type: AvMultitrackType(4),
733 },
734 };
735
736 let packet = ExAudioTagBody::demux(&header, &mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
737
738 assert_eq!(
739 packet,
740 ExAudioTagBody::ManyTracks(vec![AudioTrack {
741 audio_track_id: 1,
742 audio_four_cc: AudioFourCc::Aac,
743 packet: AudioPacket::CodedFrames {
744 data: Bytes::from_static(&[42, 42])
745 }
746 }]),
747 );
748 }
749}