1use std::fs::File;
2use std::io::prelude::*;
3use std::io::BufReader;
4use std::path::Path;
5use std::time::Duration;
6
7mod constants;
8mod context;
9mod error;
10#[cfg(test)]
11mod test;
12
13use crate::constants::*;
14use crate::context::Context;
15
16pub use crate::error::{ErrorKind, MP3DurationError};
17
18fn get_bitrate<T: Read>(
19 context: &Context<T>,
20 version: Version,
21 layer: Layer,
22 encoded_bitrate: u8,
23) -> Result<u32, MP3DurationError> {
24 if encoded_bitrate >= 15 {
25 return Err(context.error(ErrorKind::InvalidBitrate {
26 bitrate: encoded_bitrate,
27 }));
28 }
29 if layer == Layer::NotDefined {
30 return Err(context.error(ErrorKind::ForbiddenLayer));
31 }
32 Ok(1000 * BIT_RATES[version as usize][layer as usize][encoded_bitrate as usize])
33}
34
35fn get_sampling_rate<T: Read>(
36 context: &Context<T>,
37 version: Version,
38 encoded_sampling_rate: u8,
39) -> Result<u32, MP3DurationError> {
40 if encoded_sampling_rate >= 3 {
41 return Err(context.error(ErrorKind::InvalidSamplingRate {
42 sampling_rate: encoded_sampling_rate,
43 }));
44 }
45 Ok(SAMPLING_RATES[version as usize][encoded_sampling_rate as usize])
46}
47
48fn get_samples_per_frame<T: Read>(
49 context: &Context<T>,
50 version: Version,
51 layer: Layer,
52) -> Result<u32, MP3DurationError> {
53 if layer == Layer::NotDefined {
54 return Err(context.error(ErrorKind::ForbiddenLayer));
55 }
56 Ok(SAMPLES_PER_FRAME[version as usize][layer as usize])
57}
58
59fn get_side_information_size(version: Version, mode: Mode) -> usize {
60 SIDE_INFORMATION_SIZES[version as usize][mode as usize] as usize
61}
62
63pub fn from_read<T>(reader: &mut T) -> Result<Duration, MP3DurationError>
80where
81 T: Read,
82{
83 let mut header_buffer = [0; 4];
84
85 let mut context = Context::new(reader);
86
87 loop {
88 header_buffer[0] = 0;
90 while header_buffer[0] == 0 {
91 match context.read_exact(&mut header_buffer[0..1]) {
92 Ok(_) => (),
93 Err(_) if context.reached_eof() => break,
94 Err(e) => return Err(e),
95 };
96 }
97
98 match context.read_exact(&mut header_buffer[1..]) {
99 Ok(_) => (),
100 Err(_) if context.reached_eof() => break,
101 Err(e) => return Err(e),
102 };
103
104 let header = (header_buffer[0] as u32) << 24
106 | (header_buffer[1] as u32) << 16
107 | (header_buffer[2] as u32) << 8
108 | header_buffer[3] as u32;
109 let is_mp3 = header >> 21 == 0x7FF;
110 if is_mp3 {
111 let version = match (header >> 19) & 0b11 {
112 0 => Version::Mpeg25,
113 1 => return Err(context.error(ErrorKind::ForbiddenVersion)),
114 2 => Version::Mpeg2,
115 3 => Version::Mpeg1,
116 _ => unreachable!(),
117 };
118
119 let layer = match (header >> 17) & 0b11 {
120 0 => Layer::NotDefined,
121 1 => Layer::Layer3,
122 2 => Layer::Layer2,
123 3 => Layer::Layer1,
124 _ => unreachable!(),
125 };
126
127 let encoded_bitrate = (header >> 12) & 0b1111;
128 let encoded_sampling_rate = (header >> 10) & 0b11;
129 let padding = if 0 != ((header >> 9) & 1) { 1 } else { 0 };
130
131 let mode = match (header >> 6) & 0b11 {
132 0 => Mode::Stereo,
133 1 => Mode::JointStereo,
134 2 => Mode::DualChannel,
135 3 => Mode::Mono,
136 _ => unreachable!(),
137 };
138
139 let sampling_rate = get_sampling_rate(&context, version, encoded_sampling_rate as u8)?;
140 let num_samples = get_samples_per_frame(&context, version, layer)?;
141
142 let xing_offset = get_side_information_size(version, mode);
143 let mut xing_buffer = [0; 12];
144
145 context.skip(xing_offset)?;
146 context.read_exact(&mut xing_buffer)?;
147
148 let is_xing = xing_buffer[0] == 'X' as u8
149 && xing_buffer[1] == 'i' as u8
150 && xing_buffer[2] == 'n' as u8
151 && xing_buffer[3] == 'g' as u8;
152 let is_info = xing_buffer[0] == 'I' as u8
153 && xing_buffer[1] == 'n' as u8
154 && xing_buffer[2] == 'f' as u8
155 && xing_buffer[3] == 'o' as u8;
156 if is_xing || is_info {
157 let has_frames = 0 != (xing_buffer[7] & 1);
158 if has_frames {
159 let num_frames = (xing_buffer[8] as u32) << 24
160 | (xing_buffer[9] as u32) << 16
161 | (xing_buffer[10] as u32) << 8
162 | xing_buffer[11] as u32;
163 let rate = sampling_rate as u64;
164 let billion = 1_000_000_000;
165 let frames_x_samples = num_frames as u64 * num_samples as u64;
166 let seconds = frames_x_samples / rate;
167 let nanoseconds = (billion * frames_x_samples) / rate - billion * seconds;
168 return Ok(Duration::new(seconds, nanoseconds as u32));
169 }
170 }
171
172 let bitrate = get_bitrate(&context, version, layer, encoded_bitrate as u8)?;
173 let frame_length = (num_samples / 8 * bitrate / sampling_rate + padding) as usize;
174
175 let bytes_to_next_frame = frame_length
176 .checked_sub(header_buffer.len() + xing_offset + xing_buffer.len())
177 .ok_or(context.error(ErrorKind::MPEGFrameTooShort))?;
178
179 context.skip(bytes_to_next_frame)?;
180
181 let frame_duration = (num_samples as u64 * 1_000_000_000) / (sampling_rate as u64);
182 context.duration += Duration::new(0, frame_duration as u32);
183
184 continue;
185 }
186
187 let is_id3v2 = header_buffer[0] == 'I' as u8
189 && header_buffer[1] == 'D' as u8
190 && header_buffer[2] == '3' as u8;
191 if is_id3v2 {
192 let mut id3v2 = [0; 6]; context.read_exact(&mut id3v2)?;
194 let flags = id3v2[1];
195 let footer_size: usize = if 0 != (flags & 0b0001_0000) { 10 } else { 0 };
196 let tag_size: usize = ((id3v2[5] as u32)
197 | ((id3v2[4] as u32) << 7)
198 | ((id3v2[3] as u32) << 14)
199 | ((id3v2[2] as u32) << 21)) as usize;
200 context.skip(tag_size + footer_size)?;
201 continue;
202 }
203
204 let is_id3v1 = header_buffer[0] == 'T' as u8
206 && header_buffer[1] == 'A' as u8
207 && header_buffer[2] == 'G' as u8;
208 if is_id3v1 {
209 context.skip(128 - header_buffer.len())?;
210 continue;
211 }
212
213 let maybe_is_ape_v2 = header_buffer[0] == 'A' as u8
215 && header_buffer[1] == 'P' as u8
216 && header_buffer[2] == 'E' as u8
217 && header_buffer[3] == 'T' as u8;
218 if maybe_is_ape_v2 {
219 let mut ape_header = [0; 12];
220 context.read_exact(&mut ape_header)?;
221 let is_really_ape_v2 = ape_header[0] == 'A' as u8
222 && ape_header[1] == 'G' as u8
223 && ape_header[2] == 'E' as u8
224 && ape_header[3] == 'X' as u8;
225 if is_really_ape_v2 {
226 let tag_size: usize = ((ape_header[8] as u32)
227 | ((ape_header[9] as u32) << 8)
228 | ((ape_header[10] as u32) << 16)
229 | ((ape_header[11] as u32) << 24))
230 as usize;
231 context.skip(tag_size + 16)?;
232 continue;
233 }
234 }
235
236 return Err(context.error(ErrorKind::UnexpectedFrame { header }));
237 }
238
239 Ok(context.duration)
240}
241
242pub fn from_file(file: &File) -> Result<Duration, MP3DurationError> {
257 let mut reader = BufReader::new(file);
258 from_read(&mut reader)
259}
260
261pub fn from_path<P>(path: P) -> Result<Duration, MP3DurationError>
274where
275 P: AsRef<Path>,
276{
277 File::open(path)
278 .map_err(|e| MP3DurationError {
279 kind: e.into(),
280 offset: 0,
281 at_duration: Duration::from_secs(0),
282 })
283 .and_then(|file| from_file(&file))
284}