1use symphonia_core::support_format;
9
10use symphonia_core::checksum::Crc16AnsiLe;
11use symphonia_core::codecs::CodecParameters;
12use symphonia_core::errors::{seek_error, Result, SeekErrorKind};
13use symphonia_core::formats::prelude::*;
14use symphonia_core::io::*;
15use symphonia_core::meta::{Metadata, MetadataLog};
16use symphonia_core::probe::{Descriptor, Instantiate, QueryDescriptor};
17
18use crate::common::{FrameHeader, MpegLayer};
19use crate::header::{self, MAX_MPEG_FRAME_SIZE, MPEG_HEADER_LEN};
20
21use std::io::{Seek, SeekFrom};
22
23use log::{debug, info, warn};
24
25pub struct MpaReader {
29 reader: MediaSourceStream,
30 tracks: Vec<Track>,
31 cues: Vec<Cue>,
32 metadata: MetadataLog,
33 options: FormatOptions,
34 first_packet_pos: u64,
35 next_packet_ts: u64,
36}
37
38impl QueryDescriptor for MpaReader {
39 fn query() -> &'static [Descriptor] {
40 &[
41 support_format!(
43 "mp1",
44 "MPEG Audio Layer 1 Native",
45 &["mp1"],
46 &["audio/mpeg", "audio/mp1"],
47 &[
48 &[0xff, 0xfe], &[0xff, 0xff], &[0xff, 0xf6], &[0xff, 0xf7], &[0xff, 0xe6], &[0xff, 0xe7], ]
55 ),
56 support_format!(
58 "mp2",
59 "MPEG Audio Layer 2 Native",
60 &["mp2"],
61 &["audio/mpeg", "audio/mp2"],
62 &[
63 &[0xff, 0xfc], &[0xff, 0xfd], &[0xff, 0xf4], &[0xff, 0xf5], &[0xff, 0xe4], &[0xff, 0xe5], ]
70 ),
71 support_format!(
73 "mp3",
74 "MPEG Audio Layer 3 Native",
75 &["mp3"],
76 &["audio/mpeg", "audio/mp3"],
77 &[
78 &[0xff, 0xfa], &[0xff, 0xfb], &[0xff, 0xf2], &[0xff, 0xf3], &[0xff, 0xe2], &[0xff, 0xe3], ]
85 ),
86 ]
87 }
88
89 fn score(_context: &[u8]) -> u8 {
90 255
91 }
92}
93
94impl FormatReader for MpaReader {
95 fn try_new(mut source: MediaSourceStream, options: &FormatOptions) -> Result<Self> {
96 let (header, packet) = read_mpeg_frame_strict(&mut source)?;
98
99 let mut params = CodecParameters::new();
101
102 params
103 .for_codec(header.codec())
104 .with_sample_rate(header.sample_rate)
105 .with_time_base(TimeBase::new(1, header.sample_rate))
106 .with_channels(header.channel_mode.channels());
107
108 if let Some(info_tag) = try_read_info_tag(&packet, &header) {
110 let (delay, padding) = if let Some(lame_tag) = info_tag.lame {
112 params.with_delay(lame_tag.enc_delay).with_padding(lame_tag.enc_padding);
113
114 (lame_tag.enc_delay, lame_tag.enc_padding)
115 }
116 else {
117 (0, 0)
118 };
119
120 if let Some(num_mpeg_frames) = info_tag.num_frames {
122 info!("using xing header for duration");
123
124 let num_frames = u64::from(num_mpeg_frames) * header.duration();
125
126 if options.enable_gapless {
128 params.with_n_frames(num_frames - u64::from(delay) - u64::from(padding));
129 }
130 else {
131 params.with_n_frames(num_frames);
132 }
133 }
134 }
135 else if let Some(vbri_tag) = try_read_vbri_tag(&packet, &header) {
136 info!("using vbri header for duration");
137
138 let num_frames = u64::from(vbri_tag.num_mpeg_frames) * header.duration();
139
140 params.with_n_frames(num_frames);
142 }
143 else {
144 source.seek_buffered_rev(MPEG_HEADER_LEN + header.frame_size);
147
148 if source.is_seekable() {
150 info!("estimating duration from bitrate, may be inaccurate for vbr files");
151
152 if let Some(n_mpeg_frames) = estimate_num_mpeg_frames(&mut source) {
153 params.with_n_frames(n_mpeg_frames * header.duration());
154 }
155 }
156 }
157
158 let first_packet_pos = source.pos();
159
160 Ok(MpaReader {
161 reader: source,
162 tracks: vec![Track::new(0, params)],
163 cues: Vec::new(),
164 metadata: Default::default(),
165 options: *options,
166 first_packet_pos,
167 next_packet_ts: 0,
168 })
169 }
170
171 fn next_packet(&mut self) -> Result<Packet> {
172 let (header, packet) = loop {
173 let (header, packet) = read_mpeg_frame(&mut self.reader)?;
175
176 if is_maybe_info_tag(&packet, &header) {
178 if try_read_info_tag(&packet, &header).is_some() {
179 warn!("found an unexpected xing tag, discarding");
181 continue;
182 }
183 }
184 else if is_maybe_vbri_tag(&packet, &header)
185 && try_read_vbri_tag(&packet, &header).is_some()
186 {
187 warn!("found an unexpected vbri tag, discarding");
189 continue;
190 }
191
192 break (header, packet);
193 };
194
195 let ts = self.next_packet_ts;
197 let duration = header.duration();
198
199 self.next_packet_ts += duration;
200
201 let mut packet = Packet::new_from_boxed_slice(0, ts, duration, packet.into_boxed_slice());
202
203 if self.options.enable_gapless {
204 symphonia_core::formats::util::trim_packet(
205 &mut packet,
206 self.tracks[0].codec_params.delay.unwrap_or(0),
207 self.tracks[0].codec_params.n_frames,
208 );
209 }
210
211 Ok(packet)
212 }
213
214 fn metadata(&mut self) -> Metadata<'_> {
215 self.metadata.metadata()
216 }
217
218 fn cues(&self) -> &[Cue] {
219 &self.cues
220 }
221
222 fn tracks(&self) -> &[Track] {
223 &self.tracks
224 }
225
226 fn seek(&mut self, mode: SeekMode, to: SeekTo) -> Result<SeekedTo> {
227 const MAX_REF_FRAMES: usize = 4;
228 const REF_FRAMES_MASK: usize = MAX_REF_FRAMES - 1;
229
230 let desired_ts = match to {
232 SeekTo::TimeStamp { ts, .. } => ts,
234 SeekTo::Time { time, .. } => {
236 if let Some(sample_rate) = self.tracks[0].codec_params.sample_rate {
239 TimeBase::new(1, sample_rate).calc_timestamp(time)
240 }
241 else {
242 return seek_error(SeekErrorKind::Unseekable);
243 }
244 }
245 };
246
247 let delay = if self.options.enable_gapless {
249 u64::from(self.tracks[0].codec_params.delay.unwrap_or(0))
250 }
251 else {
252 0
253 };
254
255 let required_ts = desired_ts + delay;
257
258 let is_seekable = self.reader.is_seekable();
261
262 if !is_seekable && required_ts < self.next_packet_ts {
263 return seek_error(SeekErrorKind::ForwardOnly);
264 }
265
266 debug!("seeking to ts={} (+{} delay = {})", desired_ts, delay, required_ts);
267
268 match mode {
277 SeekMode::Coarse if is_seekable => self.preseek_coarse(required_ts, delay)?,
278 SeekMode::Accurate => self.preseek_accurate(required_ts)?,
279 _ => (),
280 };
281
282 let mut frames: [FramePos; MAX_REF_FRAMES] = Default::default();
289 let mut n_parsed = 0;
290
291 loop {
292 let header = header::parse_frame_header(header::sync_frame(&mut self.reader)?)?;
294
295 let pos = self.reader.pos() - std::mem::size_of::<u32>() as u64;
297
298 let duration = header.duration();
300
301 frames[n_parsed & REF_FRAMES_MASK] = FramePos { pos, ts: self.next_packet_ts };
303 n_parsed += 1;
304
305 if self.next_packet_ts + duration > required_ts {
308 let main_data_begin = read_main_data_begin(&mut self.reader, &header)? as u64;
313
314 debug!(
315 "found frame with ts={} ({}) @ pos={} with main_data_begin={}",
316 self.next_packet_ts.saturating_sub(delay),
317 self.next_packet_ts,
318 pos,
319 main_data_begin
320 );
321
322 let mut n_ref_frames = 0;
326 let mut ref_frame = &frames[(n_parsed - 1) & REF_FRAMES_MASK];
327
328 if main_data_begin > 0 {
329 let max_ref_frames = std::cmp::min(n_parsed, frames.len());
332
333 while n_ref_frames < max_ref_frames {
334 ref_frame = &frames[(n_parsed - n_ref_frames - 1) & REF_FRAMES_MASK];
335
336 if pos - ref_frame.pos >= main_data_begin {
337 break;
338 }
339
340 n_ref_frames += 1;
341 }
342
343 debug!(
344 "will seek -{} frame(s) to ts={} ({}) @ pos={} (-{} bytes)",
345 n_ref_frames,
346 ref_frame.ts.saturating_sub(delay),
347 ref_frame.ts,
348 ref_frame.pos,
349 pos - ref_frame.pos
350 );
351 }
352
353 self.next_packet_ts = ref_frame.ts;
355 self.reader.seek_buffered(ref_frame.pos);
356
357 break;
358 }
359
360 self.reader.ignore_bytes(header.frame_size as u64)?;
362
363 self.next_packet_ts += duration;
365 }
366
367 let actual_ts = self.next_packet_ts.saturating_sub(delay);
368
369 debug!(
370 "seeked to ts={} ({}) (delta={})",
371 actual_ts,
372 self.next_packet_ts,
373 self.next_packet_ts as i64 - required_ts as i64,
374 );
375
376 Ok(SeekedTo { track_id: 0, required_ts: required_ts - delay, actual_ts })
377 }
378
379 fn into_inner(self: Box<Self>) -> MediaSourceStream {
380 self.reader
381 }
382}
383
384impl MpaReader {
385 fn preseek_coarse(&mut self, required_ts: u64, delay: u64) -> Result<()> {
388 let padding = if self.options.enable_gapless {
390 u64::from(self.tracks[0].codec_params.padding.unwrap_or(0))
391 }
392 else {
393 0
394 };
395
396 let total_byte_len = match self.reader.byte_len() {
398 Some(byte_len) => byte_len,
399 None => return seek_error(SeekErrorKind::Unseekable),
400 };
401
402 let duration = match self.tracks[0].codec_params.n_frames {
405 Some(num_frames) => num_frames + delay + padding,
406 None => return seek_error(SeekErrorKind::Unseekable),
407 };
408
409 let audio_byte_len = total_byte_len - self.first_packet_pos;
411
412 let packet_pos =
415 ((u128::from(required_ts) * u128::from(audio_byte_len)) / u128::from(duration)) as u64;
416
417 let seek_pos = packet_pos.saturating_sub(MAX_MPEG_FRAME_SIZE) + self.first_packet_pos;
420
421 self.reader.seek(SeekFrom::Start(seek_pos))?;
423
424 let (header, _) = read_mpeg_frame_strict(&mut self.reader)?;
426
427 let seeked_pos = self.reader.pos();
429
430 let ts = ((u128::from(seeked_pos - self.first_packet_pos) * u128::from(duration))
431 / u128::from(audio_byte_len)) as u64;
432
433 let packet_dur = header.duration();
436
437 self.next_packet_ts = (ts / packet_dur) * packet_dur;
438
439 Ok(())
440 }
441
442 fn preseek_accurate(&mut self, required_ts: u64) -> Result<()> {
445 if required_ts < self.next_packet_ts {
446 let seeked_pos = self.reader.seek(SeekFrom::Start(self.first_packet_pos))?;
447
448 if seeked_pos != self.first_packet_pos {
451 return seek_error(SeekErrorKind::Unseekable);
452 }
453
454 self.next_packet_ts = 0;
456 }
457
458 Ok(())
459 }
460}
461
462fn read_mpeg_frame(reader: &mut MediaSourceStream) -> Result<(FrameHeader, Vec<u8>)> {
464 let (header, header_word) = loop {
465 let sync = header::sync_frame(reader)?;
467
468 if let Ok(header) = header::parse_frame_header(sync) {
470 break (header, sync);
471 }
472
473 warn!("invalid mpeg audio header");
474 };
475
476 let mut packet = vec![0u8; MPEG_HEADER_LEN + header.frame_size];
478 packet[0..MPEG_HEADER_LEN].copy_from_slice(&header_word.to_be_bytes());
479
480 reader.read_buf_exact(&mut packet[MPEG_HEADER_LEN..])?;
482
483 Ok((header, packet))
485}
486
487fn read_mpeg_frame_strict(reader: &mut MediaSourceStream) -> Result<(FrameHeader, Vec<u8>)> {
489 loop {
490 let (header, packet) = read_mpeg_frame(reader)?;
492
493 let pos = reader.pos();
495
496 if let Ok(sync) = header::read_frame_header_word_no_sync(reader) {
499 if !header::is_frame_header_word_synced(sync) || !is_frame_header_similar(&header, sync)
503 {
504 warn!("skipping junk at {} bytes", pos - packet.len() as u64);
505
506 reader.seek_buffered_rev(packet.len() + MPEG_HEADER_LEN - 1);
509 continue;
510 }
511 }
512
513 reader.seek_buffered(pos);
515
516 break Ok((header, packet));
517 }
518}
519
520fn is_frame_header_similar(header: &FrameHeader, sync: u32) -> bool {
522 if let Ok(candidate) = header::parse_frame_header(sync) {
523 if header.version == candidate.version
524 && header.layer == candidate.layer
525 && header.sample_rate == candidate.sample_rate
526 && header.n_channels() == candidate.n_channels()
527 {
528 return true;
529 }
530 }
531
532 false
533}
534
535#[derive(Default)]
536struct FramePos {
537 ts: u64,
538 pos: u64,
539}
540
541fn read_main_data_begin<B: ReadBytes>(reader: &mut B, header: &FrameHeader) -> Result<u16> {
543 if header.has_crc {
545 let _crc = reader.read_be_u16()?;
546 }
547
548 let main_data_begin = if header.is_mpeg1() {
550 reader.read_be_u16()? >> 7
551 }
552 else {
554 u16::from(reader.read_u8()?)
555 };
556
557 Ok(main_data_begin)
558}
559
560fn estimate_num_mpeg_frames(reader: &mut MediaSourceStream) -> Option<u64> {
562 const MAX_FRAMES: u32 = 16;
563 const MAX_LEN: usize = 16 * 1024;
564
565 macro_rules! break_on_err {
567 ($expr:expr) => {
568 match $expr {
569 Ok(a) => a,
570 _ => break None,
571 }
572 };
573 }
574
575 let start_pos = reader.pos();
576
577 let mut total_frame_len = 0;
578 let mut total_frames = 0;
579
580 let total_len = match reader.byte_len() {
581 Some(len) => len - start_pos,
582 _ => return None,
583 };
584
585 let num_mpeg_frames = loop {
586 let header_val = break_on_err!(reader.read_be_u32());
588
589 let header = break_on_err!(header::parse_frame_header(header_val));
591
592 total_frame_len += MPEG_HEADER_LEN + header.frame_size;
594 total_frames += 1;
595
596 break_on_err!(reader.ignore_bytes(header.frame_size as u64));
598
599 if total_frames > MAX_FRAMES || total_frame_len > MAX_LEN {
602 let avg_mpeg_frame_len = total_frame_len as f64 / total_frames as f64;
603 break Some((total_len as f64 / avg_mpeg_frame_len) as u64);
604 }
605 };
606
607 reader.seek_buffered_rev((reader.pos() - start_pos) as usize);
609
610 num_mpeg_frames
611}
612
613const XING_TAG_ID: [u8; 4] = *b"Xing";
614const INFO_TAG_ID: [u8; 4] = *b"Info";
615
616#[allow(dead_code)]
618struct LameTag {
619 encoder: String,
620 replaygain_peak: Option<f32>,
621 replaygain_radio: Option<f32>,
622 replaygain_audiophile: Option<f32>,
623 enc_delay: u32,
624 enc_padding: u32,
625}
626
627#[allow(dead_code)]
629struct XingInfoTag {
630 num_frames: Option<u32>,
631 num_bytes: Option<u32>,
632 toc: Option<[u8; 100]>,
633 quality: Option<u32>,
634 is_cbr: bool,
635 lame: Option<LameTag>,
636}
637
638fn try_read_info_tag(buf: &[u8], header: &FrameHeader) -> Option<XingInfoTag> {
640 try_read_info_tag_inner(buf, header).ok().flatten()
643}
644
645fn try_read_info_tag_inner(buf: &[u8], header: &FrameHeader) -> Result<Option<XingInfoTag>> {
646 if !is_maybe_info_tag(buf, header) {
648 return Ok(None);
649 }
650
651 let offset = header.side_info_len();
654
655 let mut crc16 = Crc16AnsiLe::new(0);
657 crc16.process_buf_bytes(&buf[..offset + MPEG_HEADER_LEN]);
658
659 let mut reader = MonitorStream::new(BufReader::new(&buf[offset + MPEG_HEADER_LEN..]), crc16);
661
662 let id = reader.read_quad_bytes()?;
664
665 if id != XING_TAG_ID && id != INFO_TAG_ID {
666 return Ok(None);
667 }
668
669 let is_cbr = id == INFO_TAG_ID;
671
672 let flags = reader.read_be_u32()?;
674
675 let num_frames = if flags & 0x1 != 0 { Some(reader.read_be_u32()?) } else { None };
676
677 let num_bytes = if flags & 0x2 != 0 { Some(reader.read_be_u32()?) } else { None };
678
679 let toc = if flags & 0x4 != 0 {
680 let mut toc = [0; 100];
681 reader.read_buf_exact(&mut toc)?;
682 Some(toc)
683 }
684 else {
685 None
686 };
687
688 let quality = if flags & 0x8 != 0 { Some(reader.read_be_u32()?) } else { None };
689
690 const LAME_EXT_LEN: u64 = 36;
692 const MIN_LAME_EXT_LEN: u64 = 24;
694
695 let lame = if reader.inner().bytes_available() >= MIN_LAME_EXT_LEN {
698 let mut encoder = [0; 9];
700 reader.read_buf_exact(&mut encoder)?;
701
702 let _revision = reader.read_u8()?;
704
705 let _lowpass = reader.read_u8()?;
707
708 let replaygain_peak = match reader.read_be_u32()? {
710 0 => None,
711 peak => Some(32767.0 * (peak as f32 / 2.0f32.powi(23))),
712 };
713
714 let replaygain_radio = parse_lame_tag_replaygain(reader.read_be_u16()?, 1);
716
717 let replaygain_audiophile = parse_lame_tag_replaygain(reader.read_be_u16()?, 2);
719
720 let _encoding_flags = reader.read_u8()?;
722
723 let _abr = reader.read_u8()?;
725
726 let (enc_delay, enc_padding) = {
727 let trim = reader.read_be_u24()?;
728
729 if encoder[..4] == *b"LAME" || encoder[..4] == *b"Lavf" || encoder[..4] == *b"Lavc" {
730 let delay = 528 + 1 + (trim >> 12);
731 let padding = trim & ((1 << 12) - 1);
732
733 (delay, padding.saturating_sub(528 + 1))
734 }
735 else {
736 (0, 0)
737 }
738 };
739
740 let crc = if reader.inner().bytes_available() >= LAME_EXT_LEN - MIN_LAME_EXT_LEN {
743 let _misc = reader.read_u8()?;
745
746 let _mp3_gain = reader.read_u8()?;
748
749 let _surround_info = reader.read_be_u16()?;
751
752 let _music_len = reader.read_be_u32()?;
754
755 let _music_crc = reader.read_be_u16()?;
757
758 if header.has_crc || encoder[..4] == *b"LAME" {
761 Some(reader.inner_mut().read_be_u16()?)
763 }
764 else {
765 None
767 }
768 }
769 else {
770 info!("xing tag lame extension is truncated");
772 None
773 };
774
775 let is_tag_ok = crc.map_or(true, |crc| crc == reader.monitor().crc());
777
778 if is_tag_ok {
779 Some(LameTag {
781 encoder: String::from_utf8_lossy(&encoder).into(),
782 replaygain_peak,
783 replaygain_radio,
784 replaygain_audiophile,
785 enc_delay,
786 enc_padding,
787 })
788 }
789 else {
790 warn!("xing tag lame extension crc mismatch");
792 None
793 }
794 }
795 else {
796 info!("xing tag too small for lame extension");
798 None
799 };
800
801 Ok(Some(XingInfoTag { num_frames, num_bytes, toc, quality, is_cbr, lame }))
802}
803
804fn parse_lame_tag_replaygain(value: u16, expected_name: u8) -> Option<f32> {
805 let name = ((value & 0xe000) >> 13) as u8;
807
808 if name == expected_name {
809 let gain = (value & 0x01ff) as f32 / 10.0;
810 Some(if value & 0x200 != 0 { -gain } else { gain })
811 }
812 else {
813 None
814 }
815}
816
817fn is_maybe_info_tag(buf: &[u8], header: &FrameHeader) -> bool {
820 const MIN_XING_TAG_LEN: usize = 8;
821
822 if header.layer != MpegLayer::Layer3 {
824 return false;
825 }
826
827 let offset = header.side_info_len() + MPEG_HEADER_LEN;
830
831 if buf.len() < offset + MIN_XING_TAG_LEN {
833 return false;
834 }
835
836 let id = &buf[offset..offset + 4];
838
839 if id != XING_TAG_ID && id != INFO_TAG_ID {
840 return false;
841 }
842
843 !buf[MPEG_HEADER_LEN..offset].iter().any(|&b| b != 0)
845}
846
847const VBRI_TAG_ID: [u8; 4] = *b"VBRI";
848
849#[allow(dead_code)]
851struct VbriTag {
852 num_bytes: u32,
853 num_mpeg_frames: u32,
854}
855
856fn try_read_vbri_tag(buf: &[u8], header: &FrameHeader) -> Option<VbriTag> {
858 try_read_vbri_tag_inner(buf, header).ok().flatten()
861}
862
863fn try_read_vbri_tag_inner(buf: &[u8], header: &FrameHeader) -> Result<Option<VbriTag>> {
864 if !is_maybe_vbri_tag(buf, header) {
866 return Ok(None);
867 }
868
869 let mut reader = BufReader::new(buf);
870
871 reader.ignore_bytes(MPEG_HEADER_LEN as u64 + 32)?;
873
874 let id = reader.read_quad_bytes()?;
876
877 if id != VBRI_TAG_ID {
878 return Ok(None);
879 }
880
881 let version = reader.read_be_u16()?;
883
884 if version != 1 {
885 return Ok(None);
886 }
887
888 let _delay = reader.read_be_u16()?;
890 let _quality = reader.read_be_u16()?;
891
892 let num_bytes = reader.read_be_u32()?;
893 let num_mpeg_frames = reader.read_be_u32()?;
894
895 Ok(Some(VbriTag { num_bytes, num_mpeg_frames }))
896}
897
898fn is_maybe_vbri_tag(buf: &[u8], header: &FrameHeader) -> bool {
901 const MIN_VBRI_TAG_LEN: usize = 26;
902 const VBRI_TAG_OFFSET: usize = 36;
903
904 if header.layer != MpegLayer::Layer3 {
906 return false;
907 }
908
909 if buf.len() < VBRI_TAG_OFFSET + MIN_VBRI_TAG_LEN {
911 return false;
912 }
913
914 let id = &buf[VBRI_TAG_OFFSET..VBRI_TAG_OFFSET + 4];
916
917 if id != VBRI_TAG_ID {
918 return false;
919 }
920
921 !buf[MPEG_HEADER_LEN..VBRI_TAG_OFFSET].iter().any(|&b| b != 0)
923}