1use std::fs::File;
2use std::io::Read;
3use std::path::Path;
4use std::time::Duration;
5
6use consts::{BITRATES, SAMPLING_FREQ};
7use enums::{ChannelType, Copyright, CRC, Emphasis, Error, Genre, Layer, Status, Version};
8use types::{AudioTag, Frame, MP3Metadata, OptionalAudioTags};
9use utils::{compute_duration, create_utf8_str, get_line, get_samp_line, get_text_field, get_text_fields};
10use utils::{get_url_field, get_url_fields};
11
12fn get_id3(i: &mut u32, buf: &[u8], meta: &mut MP3Metadata) -> Result<(), Error> {
13 let mut x = *i as usize;
14 if buf.len() > 32 && x + 32 < buf.len() && buf[x] == b'A' && buf[x + 1] == b'P' && buf[x + 2] == b'E' && buf[x + 3] == b'T' &&
17 buf[x + 4] == b'A' && buf[x + 5] == b'G' && buf[x + 6] == b'E' && buf[x + 7] == b'X' {
18 *i += 31; Ok(())
20 } else if buf.len() > 127 && x + 127 < buf.len() && buf[x] == b'T' && buf[x + 1] == b'A' && buf[x + 2] == b'G' {
22 if meta.tag.is_some() {
23 return Err(Error::DuplicatedIDV3);
24 }
25 if let Some(last) = meta.frames.last_mut() {
26 if *i <= last.size {
27 return Ok(());
28 }
29 last.size = *i - last.size - 1;
30 }
31 *i += 126;
32 meta.tag = Some(AudioTag {
34 title: create_utf8_str(&buf[x + 3..][..30]),
35 artist: create_utf8_str(&buf[x + 33..][..30]),
36 album: create_utf8_str(&buf[x + 63..][..30]),
37 year: create_utf8_str(&buf[x + 93..][..4]).parse::<u16>().unwrap_or(0),
38 comment: create_utf8_str(&buf[x + 97..][..if buf[x + 97 + 28] != 0 { 30 } else { 28 }]),
39 genre: Genre::from(buf[x + 127]),
40 });
41 Ok(())
42 } else if buf.len() > x + 13 && buf[x] == b'I' && buf[x + 1] == b'D' && buf[x + 2] == b'3' {
44 let maj_version = buf[x + 3];
45 let min_version = buf[x + 4];
46
47 if maj_version > 4 {
48 return Ok(());
49 }
50
51 let tag_size = ((buf[x + 9] as usize) & 0xFF) |
52 (((buf[x + 8] as usize) & 0xFF) << 7) |
53 (((buf[x + 7] as usize) & 0xFF) << 14) |
54 ((((buf[x + 6] as usize) & 0xFF) << 21) + 10);
55 let use_sync = buf[x + 5] & 0x80 != 0;
56 let has_extended_header = buf[x + 5] & 0x40 != 0;
57
58 x += 10;
59
60 if has_extended_header {
61 if x + 4 >= buf.len() {
62 *i = x as u32;
63 return Ok(())
64 }
65 let header_size = ((buf[x] as u32) << 21) |
66 ((buf[x + 1] as u32) << 14) |
67 ((buf[x + 2] as u32) << 7) |
68 buf[x + 3] as u32;
69 if header_size < 4 {
70 return Ok(())
71 }
72 x += header_size as usize - 4;
73 }
74
75 *i = x as u32 + tag_size as u32;
76 if x + tag_size >= buf.len() {
77 return Ok(())
78 }
79
80 let mut v = Vec::new();
83 let (buf, length) = if use_sync {
84 let mut new_pos = 0;
85 let mut skip = false;
86 v.reserve(tag_size);
87
88 for i in 0..tag_size {
89 if skip {
90 skip = false;
91 continue;
92 }
93 if i + 1 >= buf.len() {
94 return Ok(());
95 }
96 if i + 1 < tag_size && buf[i] == 0xFF && buf[i + 1] == 0 {
97 v[new_pos] = 0xFF;
98 new_pos += 1;
99 skip = true;
100 continue;
101 }
102 if new_pos >= v.len() {
103 return Ok(());
104 }
105 v[new_pos] = buf[i];
106 new_pos += 1;
107 }
108 (v.as_slice(), new_pos)
109 } else {
110 (buf, tag_size)
111 };
112
113 let mut pos = x;
114 let id3_frame_size = if maj_version < 3 { 6 } else { 10 };
115 let mut op = OptionalAudioTags::default();
116 let mut changes = false;
117 loop {
118 if pos + id3_frame_size > x + length {
119 break;
120 }
121
122 if buf[pos] < b'A' || buf[pos] > b'Z' {
124 break;
125 }
126
127 let (frame_name, frame_size) = if maj_version < 3 {
129 (create_utf8_str(&buf[pos..][..3]), (buf[pos + 5] as u32 & 0xFF) |
130 ((buf[pos + 4] as u32 & 0xFF) << 8) |
131 ((buf[pos + 3] as u32 & 0xFF) << 16))
132 } else if maj_version < 4 {
133 (create_utf8_str(&buf[pos..][..4]), (buf[pos + 7] as u32 & 0xFF) |
134 ((buf[pos + 6] as u32 & 0xFF) << 8) |
135 ((buf[pos + 5] as u32 & 0xFF) << 16) |
136 ((buf[pos + 4] as u32 & 0xFF) << 24))
137 } else {
138 (create_utf8_str(&buf[pos..][..4]), (buf[pos + 7] as u32 & 0xFF) |
139 ((buf[pos + 6] as u32 & 0xFF) << 7) |
140 ((buf[pos + 5] as u32 & 0xFF) << 14) |
141 ((buf[pos + 4] as u32 & 0xFF) << 21))
142 };
143
144 pos += id3_frame_size;
145 if pos + frame_size as usize > x + length {
146 break;
147 }
148
149 match &frame_name[..] {
151 "TALB" => get_text_field(buf, pos, frame_size, &mut changes,
155 &mut op.album_movie_show),
156 "TBPM" => get_text_field(buf, pos, frame_size, &mut changes,
157 &mut op.bpm),
158 "TCOM" => get_text_fields(buf, pos, frame_size, &mut changes,
159 &mut op.composers),
160 "TCON" => {
161 let mut s = None;
162 get_text_field(buf, pos, frame_size, &mut changes, &mut s);
163 if let Some(s) = s {
164 if !s.is_empty() {
165 if s.starts_with('(') && s.ends_with(')') {
166 let v = s.split(')').collect::<Vec<&str>>().into_iter().filter_map(|a| {
167 match a.replace('(', "").parse::<u8>() {
168 Ok(num) => Some(Genre::from(num)),
169 _ => None,
170 }
171 }).collect::<Vec<Genre>>();
172 if !v.is_empty() {
173 for entry in v {
174 op.content_type.push(entry);
175 }
176 } else {
177 op.content_type.push(Genre::from(s.as_str()));
178 }
179 } else {
180 op.content_type.push(Genre::from(s.as_str()));
181 }
182 }
183 }
184
185 }
186 "TCOP" => get_text_field(buf, pos, frame_size, &mut changes,
187 &mut op.copyright),
188 "TDAT" => get_text_field(buf, pos, frame_size, &mut changes,
189 &mut op.date),
190 "TDLY" => get_text_field(buf, pos, frame_size, &mut changes,
191 &mut op.playlist_delay),
192 "TENC" => get_text_field(buf, pos, frame_size, &mut changes,
193 &mut op.encoded_by),
194 "TEXT" => get_text_fields(buf, pos, frame_size, &mut changes,
195 &mut op.text_writers),
196 "TFLT" => get_text_field(buf, pos, frame_size, &mut changes,
197 &mut op.file_type),
198 "TIME" => get_text_field(buf, pos, frame_size, &mut changes,
199 &mut op.time),
200 "TIT" | "TIT2" => get_text_field(buf, pos, frame_size,
201 &mut changes, &mut op.title),
202 "TIT1" => get_text_field(buf, pos, frame_size, &mut changes,
203 &mut op.content_group_description),
204 "TIT3" => get_text_field(buf, pos, frame_size, &mut changes,
205 &mut op.subtitle_refinement_description),
206 "TKEY" => get_text_field(buf, pos, frame_size, &mut changes,
207 &mut op.initial_key),
208 "TLAN" => get_text_field(buf, pos, frame_size, &mut changes,
209 &mut op.language),
210 "TLEN" => get_text_field(buf, pos, frame_size, &mut changes,
211 &mut op.length),
212 "TMED" => get_text_field(buf, pos, frame_size, &mut changes,
213 &mut op.media_type),
214 "TOAL" => get_text_field(buf, pos, frame_size, &mut changes,
215 &mut op.original_album_move_show_title),
216 "TOFN" => get_text_field(buf, pos, frame_size, &mut changes,
217 &mut op.original_filename),
218 "TOLY" => get_text_fields(buf, pos, frame_size, &mut changes,
219 &mut op.original_text_writers),
220 "TOPE" => get_text_fields(buf, pos, frame_size, &mut changes,
221 &mut op.original_artists),
222 "TORY" => get_text_field(buf, pos, frame_size, &mut changes,
223 &mut op.original_release_year),
224 "TOWN" => get_text_field(buf, pos, frame_size, &mut changes,
225 &mut op.file_owner),
226 "TPE1" => get_text_fields(buf, pos, frame_size, &mut changes,
227 &mut op.performers),
228 "TPE2" => get_text_field(buf, pos, frame_size, &mut changes,
229 &mut op.band),
230 "TPE3" => get_text_field(buf, pos, frame_size, &mut changes,
231 &mut op.conductor),
232 "TPE4" => get_text_field(buf, pos, frame_size, &mut changes,
233 &mut op.interpreted),
234 "TPOS" => get_text_field(buf, pos, frame_size, &mut changes,
235 &mut op.part_of_a_set),
236 "TPUB" => get_text_field(buf, pos, frame_size, &mut changes,
237 &mut op.publisher),
238 "TRCK" => get_text_field(buf, pos, frame_size, &mut changes,
239 &mut op.track_number),
240 "TRDA" => get_text_field(buf, pos, frame_size, &mut changes,
241 &mut op.recording_dates),
242 "TRSN" => get_text_field(buf, pos, frame_size, &mut changes,
243 &mut op.internet_radio_station_name),
244 "TRSO" => get_text_field(buf, pos, frame_size, &mut changes,
245 &mut op.internet_radio_station_owner),
246 "TSIZ" => get_text_field(buf, pos, frame_size, &mut changes,
247 &mut op.size),
248 "TSRC" => get_text_field(buf, pos, frame_size, &mut changes,
249 &mut op.international_standard_recording_code),
250 "TSSE" => get_text_field(buf, pos, frame_size, &mut changes,
251 &mut op.soft_hard_setting),
252 "TYER" => get_text_field(buf, pos, frame_size, &mut changes,
253 &mut op.year),
254 "IPLS" => get_text_field(buf, pos, frame_size, &mut changes,
255 &mut op.involved_people),
256 "WCOM" => get_url_fields(buf, pos, frame_size, &mut changes,
260 &mut op.commercial_info_url),
261 "WCOP" => get_url_field(buf, pos, frame_size, &mut changes,
262 &mut op.copyright_info_url),
263 "WOAF" => get_url_field(buf, pos, frame_size, &mut changes,
264 &mut op.official_webpage),
265 "WOAR" => get_url_fields(buf, pos, frame_size, &mut changes,
266 &mut op.official_artist_webpage),
267 "WOAS" => get_url_field(buf, pos, frame_size, &mut changes,
268 &mut op.official_audio_source_webpage),
269 "WORS" => get_url_field(buf, pos, frame_size, &mut changes,
270 &mut op.official_internet_radio_webpage),
271 "WPAY" => get_url_field(buf, pos, frame_size, &mut changes,
272 &mut op.payment_url),
273 "WPUB" => get_url_field(buf, pos, frame_size, &mut changes,
274 &mut op.publishers_official_webpage),
275 _ => {
276 }
278 };
279
280 pos += frame_size as usize;
281 }
282 if changes {
283 op.position = meta.frames.len() as u32;
284 op.minor_version = min_version;
285 op.major_version = maj_version;
286 meta.optional_info.push(op);
287 }
288 Ok(())
289 } else {
290 Ok(())
291 }
292}
293
294fn read_header(buf: &[u8], i: &mut u32, meta: &mut MP3Metadata) -> Result<bool, Error> {
295 let header = (buf[*i as usize] as u32) << 24 | (buf[*i as usize + 1] as u32) << 16 |
296 (buf[*i as usize + 2] as u32) << 8 | buf[*i as usize + 3] as u32;
297 if header & 0xffe00000 == 0xffe00000 && header & (3 << 17) != 0 &&
298 header & (0xf << 12) != (0xf << 12) && header & (3 << 10) != (3 << 10) {
299 let mut frame: Frame = Default::default();
300
301 frame.version = Version::from((header >> 19) & 3);
302 frame.layer = Layer::from((header >> 17) & 3);
303 frame.crc = CRC::from((header >> 16) & 1);
304
305 frame.bitrate = BITRATES[get_line(frame.version, frame.layer)]
306 [((header >> 12) & 0xF) as usize];
307 frame.sampling_freq = SAMPLING_FREQ[get_samp_line(frame.version)]
308 [((header >> 10) & 0x3) as usize];
309 frame.padding = (header >> 9) & 1 == 1;
310 frame.private_bit = (header >> 8) & 1 == 1;
311
312 frame.chan_type = ChannelType::from((header >> 6) & 3);
313 let (intensity, ms_stereo) = match (header >> 4) & 3 {
314 0x1 => (true, false),
315 0x2 => (false, true),
316 0x3 => (true, true),
317 _ => (false, false),
318 };
319 frame.intensity_stereo = intensity;
320 frame.ms_stereo = ms_stereo;
321 frame.copyright = Copyright::from((header >> 3) & 1);
322 frame.status = Status::from((header >> 2) & 1);
323 frame.emphasis = Emphasis::from(header & 0x03);
324 frame.duration = compute_duration(frame.version,
325 frame.layer,
326 frame.sampling_freq);
327 frame.position = meta.duration;
328 frame.offset = *i;
329
330 if let Some(dur) = frame.duration {
331 meta.duration += dur;
332 }
333 let samples_per_frame = match frame.layer {
353 Layer::Layer3 => {
354 if frame.version == Version::MPEG1 {
355 1152
356 } else {
357 576
358 }
359 }
360 Layer::Layer2 => 1152,
361 Layer::Layer1 => 384,
362 _ => unreachable!(),
363 };
364 frame.size = (samples_per_frame as u64 / 8 * frame.bitrate as u64 * 1000 /
365 frame.sampling_freq as u64) as u32;
366 if frame.size < 1 {
367 return Ok(false);
368 }
369 if frame.padding {
370 frame.size += 1;
371 }
372 *i += frame.size;
373 meta.frames.push(frame);
374 Ok(true)
375 } else {
376 Ok(false)
377 }
378}
379
380pub fn read_from_file<P>(file: P) -> Result<MP3Metadata, Error>
381where P: AsRef<Path> {
382 if let Ok(mut fd) = File::open(file) {
383 let mut buf = Vec::new();
384
385 match fd.read_to_end(&mut buf) {
386 Ok(_) => read_from_slice(&buf),
387 Err(_) => Err(Error::FileError),
388 }
389 } else {
390 Err(Error::FileError)
391 }
392}
393
394pub fn read_from_slice(buf: &[u8]) -> Result<MP3Metadata, Error> {
395 let mut meta = MP3Metadata {
396 frames: Vec::new(),
397 duration: Duration::new(0, 0),
398 tag: None,
399 optional_info: Vec::new(),
400 };
401 let mut i = 0u32;
402
403 'a: while i < buf.len() as u32 {
404 loop {
405 get_id3(&mut i, buf, &mut meta)?;
406 if i + 3 >= buf.len() as u32 {
407 break 'a;
408 }
409 match read_header(buf, &mut i, &mut meta) {
410 Ok(true) => continue 'a,
411 Err(e) => return Err(e),
412 _ => {}
413 }
414 let old_i = i;
415 get_id3(&mut i, buf, &mut meta)?;
416 if i == old_i {
417 i += 1;
418 }
419 if i >= buf.len() as u32 {
420 break 'a;
421 }
422 }
423 }
424 if meta.tag.is_none() {
425 if let Some(last) = meta.frames.last_mut() {
426 if i <= last.size {
427 return Err(Error::InvalidData);
428 }
429 }
430 }
431 if meta.frames.is_empty() {
432 Err(Error::NotMP3)
433 } else {
434 Ok(meta)
435 }
436}
437
438#[test]
439fn not_mp3() {
440 let ret = read_from_file("src/lib.rs");
441
442 match ret {
443 Ok(_) => panic!("Wasn't supposed to be ok!"),
444 Err(e) => assert_eq!(e, Error::NotMP3),
445 }
446}
447
448#[test]
449fn double_id() {
450 let ret = read_from_file("assets/double_id.mp3");
451
452 match ret {
453 Ok(_) => panic!("Wasn't supposed to be ok!"),
454 Err(e) => assert_eq!(e, Error::DuplicatedIDV3),
455 }
456}