1#![forbid(unsafe_code)]
4
5use crate::error::Error;
6use crate::types::{FrameHeader, MpegVersion, bitrate_table, sample_rate_table};
7
8const HEADER_LEN: usize = 4;
9const LAYER_III_BITS: u8 = 0b01;
10
11const fn version_bits(version: MpegVersion) -> u8 {
12 match version {
13 MpegVersion::Mpeg25 => 0b00,
14 MpegVersion::Mpeg2 => 0b10,
15 MpegVersion::Mpeg1 => 0b11,
16 }
17}
18
19#[derive(Debug, Clone, Copy)]
27pub struct Muxer {
28 header: FrameHeader,
29 bitrate_index: u8,
30 sample_rate_index: u8,
31}
32
33impl Muxer {
34 #[allow(
37 clippy::cast_possible_truncation,
38 reason = "bitrate/sample-rate tables have 14/3 entries; the index always fits u8"
39 )]
40 pub fn new(header: FrameHeader) -> Result<Self, Error> {
41 let bitrate_index = bitrate_table(header.version)
42 .iter()
43 .position(|&kbps| kbps == header.bitrate_kbps)
44 .map_or_else(
45 || Err(Error::UnsupportedBitrate(header.bitrate_kbps)),
46 |i| Ok(i as u8 + 1), )?;
48 let sample_rate_index = sample_rate_table(header.version)
49 .iter()
50 .position(|&rate| rate == header.sample_rate)
51 .map_or_else(
52 || Err(Error::UnsupportedSampleRate(header.sample_rate)),
53 |i| Ok(i as u8),
54 )?;
55 Ok(Self {
56 header,
57 bitrate_index,
58 sample_rate_index,
59 })
60 }
61
62 pub fn write_frame(
68 &self,
69 frame_body: &[u8],
70 padding: bool,
71 out: &mut Vec<u8>,
72 ) -> Result<(), Error> {
73 let expected = self.header.frame_len(padding) - HEADER_LEN;
74 if frame_body.len() != expected {
75 return Err(Error::FrameBodyLengthMismatch {
76 expected,
77 actual: frame_body.len(),
78 });
79 }
80
81 out.push(0xFF);
82 out.push(0xE0 | (version_bits(self.header.version) << 3) | (LAYER_III_BITS << 1) | 1);
83 out.push(
84 (self.bitrate_index << 4) | (self.sample_rate_index << 2) | (u8::from(padding) << 1),
85 );
86 out.push((self.header.channel_mode.bits() << 6) | 0b0000_0100); out.extend_from_slice(frame_body);
88 Ok(())
89 }
90}
91
92#[cfg(test)]
93#[path = "mux_tests.rs"]
94mod tests;