retina/codec/
simple_audio.rs

1// Copyright (C) 2021 Scott Lamb <slamb@slamb.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Fixed-size audio sample codecs as defined in
5//! [RFC 3551 section 4.5](https://datatracker.ietf.org/doc/html/rfc3551#section-4.5).
6
7use std::num::NonZeroU32;
8
9use super::{AudioParameters, CodecItem};
10
11#[derive(Debug)]
12pub(crate) struct Depacketizer {
13    parameters: AudioParameters,
14    pending: Option<super::AudioFrame>,
15    bits_per_sample: u32,
16}
17
18impl Depacketizer {
19    /// Creates a new Depacketizer.
20    pub(super) fn new(clock_rate: u32, bits_per_sample: u32) -> Self {
21        Self {
22            parameters: AudioParameters {
23                rfc6381_codec: None,
24                frame_length: None, // variable
25                clock_rate,
26                extra_data: Vec::new(),
27                codec: super::AudioParametersCodec::Other,
28            },
29            bits_per_sample,
30            pending: None,
31        }
32    }
33
34    pub(super) fn parameters(&self) -> Option<super::ParametersRef<'_>> {
35        Some(super::ParametersRef::Audio(&self.parameters))
36    }
37
38    fn frame_length(&self, payload_len: usize) -> Option<NonZeroU32> {
39        // This calculation could be strength-reduced but it's just once per frame anyway.
40        // Let's do it in a straightforward way.
41        assert!(payload_len < usize::from(u16::MAX));
42        let bits = (payload_len) as u32 * 8;
43        match bits.is_multiple_of(self.bits_per_sample) {
44            true => None,
45            false => NonZeroU32::new(bits / self.bits_per_sample),
46        }
47    }
48
49    pub(super) fn push(&mut self, pkt: crate::rtp::ReceivedPacket) -> Result<(), String> {
50        assert!(self.pending.is_none());
51        let payload = pkt.payload();
52        let frame_length = self.frame_length(payload.len()).ok_or_else(|| {
53            format!(
54                "invalid length {} for payload of {}-bit audio samples",
55                payload.len(),
56                self.bits_per_sample
57            )
58        })?;
59        self.pending = Some(super::AudioFrame {
60            loss: pkt.loss(),
61            ctx: *pkt.ctx(),
62            stream_id: pkt.stream_id(),
63            timestamp: pkt.timestamp(),
64            frame_length,
65            data: pkt.into_payload_bytes(),
66        });
67        Ok(())
68    }
69
70    pub(super) fn pull(&mut self) -> Option<super::CodecItem> {
71        self.pending.take().map(CodecItem::AudioFrame)
72    }
73}