Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/
pcm.rs

1use crate::*;
2
3ext! {
4    name: PcmC,
5    versions: [0],
6    flags: {}
7}
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct PcmC {
12    pub big_endian: bool,
13    pub sample_size: u8,
14}
15
16impl AtomExt for PcmC {
17    const KIND_EXT: FourCC = FourCC::new(b"pcmC");
18
19    type Ext = PcmCExt;
20
21    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: PcmCExt) -> Result<Self> {
22        let format_flags = u8::decode(buf)?;
23        let sample_size = u8::decode(buf)?;
24
25        Ok(Self {
26            big_endian: format_flags == 0,
27            sample_size,
28        })
29    }
30
31    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<PcmCExt> {
32        let mut format_flags = 0u8;
33        if !self.big_endian {
34            format_flags = 1u8;
35        }
36
37        format_flags.encode(buf)?;
38        self.sample_size.encode(buf)?;
39
40        Ok(PcmCExt {
41            version: PcmCVersion::V0,
42        })
43    }
44}
45
46/// The resolved sample format of a PCM sample entry.
47///
48/// See [`Pcm::format`] for how it is derived.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51pub struct PcmFormat {
52    /// Whether the samples are stored big-endian.
53    pub big_endian: bool,
54    /// The size of each sample in bits.
55    pub sample_size: u16,
56    /// Whether the samples are floating point (otherwise integer).
57    pub float: bool,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub struct Pcm {
63    pub fourcc: FourCC,
64    pub audio: Audio,
65    pub pcmc: Option<PcmC>,
66    pub chnl: Option<Chnl>,
67    pub btrt: Option<Btrt>,
68}
69
70impl Pcm {
71    fn encode_fields<B: BufMut>(
72        fourcc: FourCC,
73        audio: &Audio,
74        pcmc: Option<&PcmC>,
75        chnl: Option<&Chnl>,
76        btrt: Option<&Btrt>,
77        buf: &mut B,
78    ) -> Result<()> {
79        // An ipcm/fpcm entry without its mandatory pcmC box is invalid (ISO/IEC 23003-5).
80        if pcmc.is_none() && Self::requires_pcmc(fourcc) {
81            return Err(Error::MissingBox(PcmC::KIND));
82        }
83
84        audio.encode(buf)?;
85
86        if let Some(pcmc) = pcmc {
87            pcmc.encode(buf)?;
88        }
89
90        if let Some(chnl) = chnl {
91            chnl.encode(buf)?;
92        }
93
94        if let Some(btrt) = btrt {
95            btrt.encode(buf)?;
96        }
97
98        Ok(())
99    }
100
101    pub fn decode_with_fourcc<B: Buf>(fourcc: FourCC, buf: &mut B) -> Result<Self> {
102        let audio = Audio::decode(buf)?;
103
104        let mut chnl = None;
105        let mut pcmc = None;
106        let mut btrt = None;
107
108        while buf.remaining() > 0 {
109            let header = match Header::decode_maybe(buf)? {
110                Some(h) => h,
111                None => break,
112            };
113
114            let size = header.size.unwrap_or(buf.remaining());
115            if size > buf.remaining() {
116                break;
117            }
118
119            let mut limited = buf.slice(size);
120
121            if header.kind == Chnl::KIND {
122                // Decode channel layout by using the channel count
123                // information. We cannot rely on the decode_body
124                // implementation of Atom for channel layout box.
125                chnl = Some(Chnl::decode_body_with_channel_count(
126                    &mut limited,
127                    audio.channel_count,
128                )?);
129            } else {
130                match Any::decode_atom(&header, &mut limited)? {
131                    Any::PcmC(atom) => pcmc = Some(atom),
132                    Any::Btrt(atom) => btrt = Some(atom),
133                    atom => crate::decode_unknown(&atom, fourcc)?,
134                }
135            }
136
137            buf.advance(size);
138        }
139
140        // ISO/IEC 23003-5 mandates the pcmC box for ipcm/fpcm.
141        if pcmc.is_none() && Self::requires_pcmc(fourcc) {
142            return Err(Error::MissingBox(PcmC::KIND));
143        }
144
145        Ok(Self {
146            fourcc,
147            audio,
148            pcmc,
149            chnl,
150            btrt,
151        })
152    }
153
154    pub fn encode_with_fourcc<B: BufMut>(&self, buf: &mut B) -> Result<()> {
155        Self::encode_fields(
156            self.fourcc,
157            &self.audio,
158            self.pcmc.as_ref(),
159            self.chnl.as_ref(),
160            self.btrt.as_ref(),
161            buf,
162        )
163    }
164
165    /// Returns the resolved sample format of this entry.
166    ///
167    /// The `pcmC` box wins when present. Otherwise the format implied by the
168    /// fourcc is used:
169    /// - `twos`: big-endian integer, `sample_size` bits (8 or 16 per QTFF-2001)
170    /// - `sowt`: little-endian integer, `sample_size` bits (8 or 16 per QTFF-2001)
171    /// - `in24` / `in32`: big-endian integer, 24 / 32 bits
172    /// - `fl32` / `fl64`: big-endian float, 32 / 64 bits
173    /// - `s16l`: little-endian integer, 16 bits
174    /// - `lpcm`: QTFF format flags are only defined for version 2 sound sample
175    ///   descriptions, which are not decoded here; defaults to big-endian
176    ///   integer, `sample_size` bits
177    ///
178    /// Returns `None` for an unknown fourcc, or for an `ipcm`/`fpcm` entry
179    /// missing its mandatory `pcmC` box (which
180    /// [`decode_with_fourcc`](Self::decode_with_fourcc) never produces).
181    pub fn format(&self) -> Option<PcmFormat> {
182        Self::resolve_format(self.fourcc, &self.audio, self.pcmc.as_ref())
183    }
184
185    /// Whether a PCM sample entry with this fourcc must carry a `pcmC` box.
186    ///
187    /// ISO/IEC 23003-5 mandates it for `ipcm`/`fpcm`. The QuickTime (QTFF-2001)
188    /// fourccs and `s16l` imply their format and don't carry one.
189    fn requires_pcmc(fourcc: FourCC) -> bool {
190        matches!(fourcc, Ipcm::KIND | Fpcm::KIND)
191    }
192
193    fn resolve_format(fourcc: FourCC, audio: &Audio, pcmc: Option<&PcmC>) -> Option<PcmFormat> {
194        // fl32/fl64 samples are float, as are fpcm samples (ISO/IEC 23003-5).
195        let float = matches!(fourcc, Fl32::KIND | Fl64::KIND | Fpcm::KIND);
196
197        if let Some(pcmc) = pcmc {
198            return Some(PcmFormat {
199                big_endian: pcmc.big_endian,
200                sample_size: pcmc.sample_size as u16,
201                float,
202            });
203        }
204
205        // ipcm/fpcm carry their format in the pcmC box, so with none present
206        // there's nothing to fall back to.
207        if Self::requires_pcmc(fourcc) {
208            return None;
209        }
210
211        let (big_endian, sample_size) = match fourcc {
212            Twos::KIND | Lpcm::KIND => (true, audio.sample_size),
213            Sowt::KIND => (false, audio.sample_size),
214            In24::KIND => (true, 24),
215            In32::KIND => (true, 32),
216            Fl32::KIND => (true, 32),
217            Fl64::KIND => (true, 64),
218            S16l::KIND => (false, 16),
219            // Unknown fourccs don't imply a format.
220            _ => return None,
221        };
222
223        Some(PcmFormat {
224            big_endian,
225            sample_size,
226            float,
227        })
228    }
229}
230
231macro_rules! define_pcm_sample_entry {
232    ($name:ident, $fourcc:expr) => {
233        #[derive(Debug, Clone, PartialEq, Eq)]
234        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
235        pub struct $name {
236            pub audio: Audio,
237            pub pcmc: Option<PcmC>,
238            pub chnl: Option<Chnl>,
239            pub btrt: Option<Btrt>,
240        }
241
242        impl $name {
243            /// Returns the resolved sample format of this entry.
244            ///
245            /// See [`Pcm::format`] for the resolution rules.
246            pub fn format(&self) -> Option<PcmFormat> {
247                Pcm::resolve_format(Self::KIND, &self.audio, self.pcmc.as_ref())
248            }
249        }
250
251        impl Atom for $name {
252            const KIND: FourCC = FourCC::new($fourcc);
253
254            fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
255                let entry = Pcm::decode_with_fourcc(Self::KIND, buf)?;
256                Ok(Self {
257                    audio: entry.audio,
258                    pcmc: entry.pcmc,
259                    chnl: entry.chnl,
260                    btrt: entry.btrt,
261                })
262            }
263
264            fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
265                Pcm::encode_fields(
266                    Self::KIND,
267                    &self.audio,
268                    self.pcmc.as_ref(),
269                    self.chnl.as_ref(),
270                    self.btrt.as_ref(),
271                    buf,
272                )
273            }
274        }
275    };
276}
277
278define_pcm_sample_entry!(Sowt, b"sowt");
279define_pcm_sample_entry!(Twos, b"twos");
280define_pcm_sample_entry!(Lpcm, b"lpcm");
281define_pcm_sample_entry!(Ipcm, b"ipcm");
282define_pcm_sample_entry!(Fpcm, b"fpcm");
283define_pcm_sample_entry!(In24, b"in24");
284define_pcm_sample_entry!(In32, b"in32");
285define_pcm_sample_entry!(Fl32, b"fl32");
286define_pcm_sample_entry!(Fl64, b"fl64");
287define_pcm_sample_entry!(S16l, b"s16l");
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn test_pcmc_encode_decode() {
295        let pcmc = PcmC {
296            big_endian: true,
297            sample_size: 16,
298        };
299
300        let mut buf = Vec::new();
301        pcmc.encode(&mut buf).unwrap();
302
303        let decoded = PcmC::decode(&mut &buf[..]).unwrap();
304        assert_eq!(pcmc, decoded);
305    }
306
307    #[test]
308    fn test_pcm_encode_decode() {
309        let pcmc = PcmC {
310            big_endian: false,
311            sample_size: 16,
312        };
313        let chnl = Chnl {
314            channel_structure: Some(ChannelStructure::DefinedLayout {
315                layout: 2, // Stereo
316                omitted_channels_map: Some(0),
317                channel_order_definition: None,
318            }),
319            object_count: None,
320            format_ordering: None,
321            base_channel_count: None,
322        };
323        let pcm = Pcm {
324            fourcc: FourCC::new(b"fpcm"),
325            audio: Audio {
326                data_reference_index: 1,
327                channel_count: 2,
328                sample_size: 16,
329                sample_rate: 48000.into(),
330            },
331            pcmc: Some(pcmc),
332            chnl: Some(chnl),
333            btrt: None,
334        };
335
336        let mut buf = Vec::new();
337        pcm.encode_with_fourcc(&mut buf).unwrap();
338
339        let decoded = Pcm::decode_with_fourcc(FourCC::new(b"fpcm"), &mut &buf[..]).unwrap();
340        assert_eq!(pcm, decoded);
341    }
342
343    #[test]
344    fn test_pcm_encode_decode_with_both_channel_and_object() {
345        let pcmc = PcmC {
346            big_endian: true,
347            sample_size: 16,
348        };
349        let chnl = Chnl {
350            channel_structure: Some(ChannelStructure::DefinedLayout {
351                layout: 2, // Stereo
352                omitted_channels_map: Some(0),
353                channel_order_definition: None,
354            }),
355            object_count: Some(2),
356            format_ordering: None,
357            base_channel_count: None,
358        };
359        let pcm = Pcm {
360            fourcc: FourCC::new(b"ipcm"),
361            audio: Audio {
362                data_reference_index: 1,
363                channel_count: 2,
364                sample_size: 16,
365                sample_rate: 48000.into(),
366            },
367            pcmc: Some(pcmc),
368            chnl: Some(chnl),
369            btrt: None,
370        };
371
372        let mut buf = Vec::new();
373        pcm.encode_with_fourcc(&mut buf).unwrap();
374
375        let decoded = Pcm::decode_with_fourcc(FourCC::new(b"ipcm"), &mut &buf[..]).unwrap();
376        assert_eq!(pcm, decoded);
377    }
378
379    #[test]
380    fn test_pcm_encode_decode_with_both_channel_and_object_explicit_position() {
381        let pcmc = PcmC {
382            big_endian: true,
383            sample_size: 16,
384        };
385        let chnl = Chnl {
386            channel_structure: Some(ChannelStructure::ExplicitPositions {
387                positions: vec![
388                    SpeakerPosition::Standard(AudioChannelPosition::FrontLeft),
389                    SpeakerPosition::Standard(AudioChannelPosition::FrontRight),
390                ],
391            }),
392            object_count: Some(2),
393            format_ordering: None,
394            base_channel_count: None,
395        };
396        let pcm = Pcm {
397            fourcc: FourCC::new(b"fpcm"),
398            audio: Audio {
399                data_reference_index: 1,
400                channel_count: 2,
401                sample_size: 16,
402                sample_rate: 48000.into(),
403            },
404            pcmc: Some(pcmc),
405            chnl: Some(chnl),
406            btrt: Some(Btrt {
407                buffer_size_db: 6,
408                max_bitrate: 2_304_096,
409                avg_bitrate: 2_304_000,
410            }),
411        };
412
413        let mut buf = Vec::new();
414        pcm.encode_with_fourcc(&mut buf).unwrap();
415
416        let decoded = Pcm::decode_with_fourcc(FourCC::new(b"fpcm"), &mut &buf[..]).unwrap();
417        assert_eq!(pcm, decoded);
418    }
419
420    #[test]
421    fn test_pcm_encode_decode_with_chnl_explicit_positions() {
422        let pcmc = PcmC {
423            big_endian: true,
424            sample_size: 16,
425        };
426        let chnl = Chnl {
427            channel_structure: Some(ChannelStructure::ExplicitPositions {
428                positions: vec![
429                    SpeakerPosition::Standard(AudioChannelPosition::FrontLeft),
430                    SpeakerPosition::Standard(AudioChannelPosition::FrontRight),
431                ],
432            }),
433            object_count: None,
434            format_ordering: None,
435            base_channel_count: None,
436        };
437        let pcm = Pcm {
438            fourcc: FourCC::new(b"fpcm"),
439            audio: Audio {
440                data_reference_index: 1,
441                channel_count: 2,
442                sample_size: 16,
443                sample_rate: 48000.into(),
444            },
445            pcmc: Some(pcmc),
446            chnl: Some(chnl),
447            btrt: None,
448        };
449
450        let mut buf = Vec::new();
451        pcm.encode_with_fourcc(&mut buf).unwrap();
452
453        let decoded = Pcm::decode_with_fourcc(FourCC::new(b"fpcm"), &mut &buf[..]).unwrap();
454        assert_eq!(pcm, decoded);
455    }
456
457    #[test]
458    fn test_pcm_encode_decode_with_chnl_explicit_speaker_position() {
459        let pcmc = PcmC {
460            big_endian: true,
461            sample_size: 16,
462        };
463        let chnl = Chnl {
464            channel_structure: Some(ChannelStructure::ExplicitPositions {
465                positions: vec![
466                    SpeakerPosition::Standard(AudioChannelPosition::FrontLeft),
467                    SpeakerPosition::Explicit(ExplicitSpeakerPosition {
468                        azimuth: 45,
469                        elevation: 10,
470                    }),
471                ],
472            }),
473            object_count: None,
474            format_ordering: None,
475            base_channel_count: None,
476        };
477        let pcm = Pcm {
478            fourcc: FourCC::new(b"fpcm"),
479            audio: Audio {
480                data_reference_index: 1,
481                channel_count: 2,
482                sample_size: 16,
483                sample_rate: 48000.into(),
484            },
485            pcmc: Some(pcmc),
486            chnl: Some(chnl),
487            btrt: None,
488        };
489
490        let mut buf = Vec::new();
491        pcm.encode_with_fourcc(&mut buf).unwrap();
492
493        let decoded = Pcm::decode_with_fourcc(FourCC::new(b"fpcm"), &mut &buf[..]).unwrap();
494        assert_eq!(pcm, decoded);
495    }
496
497    #[test]
498    fn test_pcm_v1_chnl() {
499        let pcmc = PcmC {
500            big_endian: false,
501            sample_size: 24,
502        };
503        let chnl = Chnl {
504            channel_structure: Some(ChannelStructure::ExplicitPositions {
505                positions: vec![
506                    SpeakerPosition::Standard(AudioChannelPosition::FrontLeft),
507                    SpeakerPosition::Standard(AudioChannelPosition::FrontRight),
508                ],
509            }),
510            object_count: Some(1),
511            format_ordering: Some(1),
512            base_channel_count: Some(3),
513        };
514        let pcm = Lpcm {
515            audio: Audio {
516                data_reference_index: 1,
517                channel_count: 2,
518                sample_size: 24,
519                sample_rate: 48000.into(),
520            },
521            pcmc: Some(pcmc),
522            chnl: Some(chnl),
523            btrt: None,
524        };
525
526        let mut buf = Vec::new();
527        pcm.encode(&mut buf).unwrap();
528
529        let decoded = Lpcm::decode(&mut &buf[..]).unwrap();
530        assert_eq!(pcm, decoded);
531    }
532
533    const ENCODED_IPCM: &[u8] = &[
534        0x00, 0x00, 0x00, 0x5c, 0x69, 0x70, 0x63, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00,
536        0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x70, 0x63, 0x6d, 0x43, 0x00,
537        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x16, 0x63, 0x68, 0x6e, 0x6c, 0x00, 0x00,
538        0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
539        0x14, 0x62, 0x74, 0x72, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x23, 0x28, 0x60, 0x00, 0x23,
540        0x28, 0x00,
541    ];
542
543    fn decoded_ipcm() -> Ipcm {
544        Ipcm {
545            audio: Audio {
546                data_reference_index: 1,
547                channel_count: 2,
548                sample_size: 24,
549                sample_rate: FixedPoint::new(48000, 0),
550            },
551            pcmc: Some(PcmC {
552                big_endian: true,
553                sample_size: 24,
554            }),
555            chnl: Some(Chnl {
556                channel_structure: Some(ChannelStructure::DefinedLayout {
557                    layout: 2,
558                    omitted_channels_map: Some(0),
559                    channel_order_definition: None,
560                }),
561                object_count: None,
562                format_ordering: None,
563                base_channel_count: None,
564            }),
565            btrt: Some(Btrt {
566                buffer_size_db: 6,
567                max_bitrate: 2_304_096,
568                avg_bitrate: 2_304_000,
569            }),
570        }
571    }
572
573    #[test]
574    fn test_ipcm_decode() {
575        let buf = &mut std::io::Cursor::new(ENCODED_IPCM);
576        let ipcm = Ipcm::decode(buf).expect("failed to decode ipcm");
577        assert_eq!(ipcm, decoded_ipcm());
578    }
579
580    #[test]
581    fn test_ipcm_encode() {
582        let ipcm = decoded_ipcm();
583        let mut buf = Vec::new();
584        ipcm.encode(&mut buf).unwrap();
585        assert_eq!(buf.as_slice(), ENCODED_IPCM);
586    }
587
588    // A bare QuickTime sample entry without any child boxes, as written by
589    // e.g. Sony XAVC cameras; the format is implied by the fourcc.
590    const ENCODED_BARE_TWOS: &[u8] = &[
591        0x00, 0x00, 0x00, 0x24, // size = 36
592        0x74, 0x77, 0x6f, 0x73, // "twos"
593        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
594        0x00, 0x01, // data_reference_index = 1
595        0x00, 0x00, // version = 0
596        0x00, 0x00, // revision = 0
597        0x00, 0x00, 0x00, 0x00, // vendor = 0
598        0x00, 0x02, // channelcount = 2
599        0x00, 0x10, // samplesize = 16
600        0x00, 0x00, // compression_id = 0
601        0x00, 0x00, // packet_size = 0
602        0xbb, 0x80, 0x00, 0x00, // samplerate = 48000 << 16
603    ];
604
605    #[test]
606    fn test_bare_twos_decode() {
607        let buf = &mut std::io::Cursor::new(ENCODED_BARE_TWOS);
608        let twos = Twos::decode(buf).expect("failed to decode twos");
609        assert_eq!(
610            twos,
611            Twos {
612                audio: Audio {
613                    data_reference_index: 1,
614                    channel_count: 2,
615                    sample_size: 16,
616                    sample_rate: FixedPoint::new(48000, 0),
617                },
618                pcmc: None,
619                chnl: None,
620                btrt: None,
621            }
622        );
623        assert_eq!(
624            twos.format(),
625            Some(PcmFormat {
626                big_endian: true,
627                sample_size: 16,
628                float: false,
629            })
630        );
631    }
632
633    #[test]
634    fn test_bare_twos_roundtrip() {
635        let buf = &mut std::io::Cursor::new(ENCODED_BARE_TWOS);
636        let twos = Twos::decode(buf).expect("failed to decode twos");
637
638        let mut encoded = Vec::new();
639        twos.encode(&mut encoded).unwrap();
640        assert_eq!(encoded.as_slice(), ENCODED_BARE_TWOS);
641    }
642
643    #[test]
644    fn test_ipcm_missing_pcmc_decode() {
645        // The same bare entry, but ipcm requires a pcmC box (ISO/IEC 23003-5).
646        let mut encoded = ENCODED_BARE_TWOS.to_vec();
647        encoded[4..8].copy_from_slice(b"ipcm");
648
649        let buf = &mut std::io::Cursor::new(&encoded);
650        assert!(matches!(
651            Ipcm::decode(buf),
652            Err(Error::MissingBox(PcmC::KIND))
653        ));
654    }
655
656    #[test]
657    fn test_ipcm_missing_pcmc_encode() {
658        let ipcm = Ipcm {
659            audio: Audio {
660                data_reference_index: 1,
661                channel_count: 2,
662                sample_size: 16,
663                sample_rate: 48000.into(),
664            },
665            pcmc: None,
666            chnl: None,
667            btrt: None,
668        };
669
670        let mut buf = Vec::new();
671        assert!(matches!(
672            ipcm.encode(&mut buf),
673            Err(Error::MissingBox(PcmC::KIND))
674        ));
675    }
676}