1use super::header::{ADTSHeader, HEADER_MASK};
2use super::AacFile;
3use crate::config::{ParseOptions, ParsingMode};
4use crate::error::Result;
5use crate::id3::v2::header::Id3v2Header;
6use crate::id3::v2::read::parse_id3v2;
7use crate::id3::{find_id3v1, ID3FindResults};
8use crate::macros::{decode_err, err, parse_mode_choice};
9use crate::mpeg::header::{cmp_header, search_for_frame_sync, HeaderCmpResult};
10
11use std::io::{Read, Seek, SeekFrom};
12
13use byteorder::ReadBytesExt;
14
15#[allow(clippy::unnecessary_wraps)]
16pub(super) fn read_from<R>(reader: &mut R, parse_options: ParseOptions) -> Result<AacFile>
17where
18 R: Read + Seek,
19{
20 let parse_mode = parse_options.parsing_mode;
21
22 let mut file = AacFile::default();
23
24 let mut first_frame_header = None;
25 let mut first_frame_end = 0;
26
27 while reader.read_u8()? == 0 {}
29
30 reader.seek(SeekFrom::Current(-1))?;
31
32 let pos = reader.stream_position()?;
33 let mut stream_len = reader.seek(SeekFrom::End(0))?;
34
35 reader.seek(SeekFrom::Start(pos))?;
36
37 let mut header = [0; 4];
38
39 while let Ok(()) = reader.read_exact(&mut header) {
40 match header {
41 [b'I', b'D', b'3', ..] => {
43 reader.seek(SeekFrom::Current(-4))?;
45
46 let header = Id3v2Header::parse(reader)?;
47 let skip_footer = header.flags.footer;
48
49 let Some(new_stream_len) = stream_len.checked_sub(u64::from(header.size)) else {
50 err!(SizeMismatch);
51 };
52
53 stream_len = new_stream_len;
54
55 if parse_options.read_tags {
56 let id3v2 = parse_id3v2(reader, header, parse_options)?;
57 if let Some(existing_tag) = &mut file.id3v2_tag {
58 log::warn!("Duplicate ID3v2 tag found, appending frames to previous tag");
59
60 for frame in id3v2.frames {
63 existing_tag.insert(frame);
64 }
65 continue;
66 }
67 file.id3v2_tag = Some(id3v2);
68 }
69
70 if skip_footer {
72 log::debug!("Skipping ID3v2 footer");
73
74 let Some(new_stream_len) = stream_len.checked_sub(10) else {
75 err!(SizeMismatch);
76 };
77
78 stream_len = new_stream_len;
79 reader.seek(SeekFrom::Current(10))?;
80 }
81
82 continue;
83 },
84 _ => {
86 log::debug!("Searching for first ADTS frame");
87
88 #[allow(clippy::neg_multiply)]
91 reader.seek(SeekFrom::Current(-1 * header.len() as i64))?;
92
93 if let Some((first_frame_header_, first_frame_end_)) =
94 find_next_frame(reader, parse_mode)?
95 {
96 log::debug!("Found first ADTS frame");
97
98 first_frame_header = Some(first_frame_header_);
99 first_frame_end = first_frame_end_;
100 break;
101 }
102 },
103 }
104 }
105
106 #[allow(unused_variables)]
107 let ID3FindResults(header, id3v1) = find_id3v1(reader, parse_options.read_tags)?;
108
109 if header.is_some() {
110 let Some(new_stream_len) = stream_len.checked_sub(128) else {
111 err!(SizeMismatch);
112 };
113
114 stream_len = new_stream_len;
115 file.id3v1_tag = id3v1;
116 }
117
118 if parse_options.read_properties {
119 let Some(mut first_frame_header) = first_frame_header else {
120 decode_err!(@BAIL Mpeg, "File contains an invalid frame");
122 };
123
124 if first_frame_header.sample_rate == 0 {
125 parse_mode_choice!(
126 parse_mode,
127 STRICT: decode_err!(@BAIL Mpeg, "Sample rate is 0"),
128 );
129 }
130
131 if first_frame_header.bitrate == 0 {
132 parse_mode_choice!(parse_mode, STRICT: decode_err!(@BAIL Mpeg, "Bitrate is 0"),);
133 }
134
135 reader.seek(SeekFrom::Start(first_frame_end))?;
137
138 let mut frame_count = 1;
139
140 while let Some((header, _)) = find_next_frame(reader, parse_mode)? {
141 first_frame_header.bitrate += header.bitrate;
142 frame_count += 1u32;
143 }
144
145 first_frame_header.bitrate /= frame_count;
146
147 super::properties::read_properties(&mut file.properties, first_frame_header, stream_len);
148 }
149
150 Ok(file)
151}
152
153fn find_next_frame<R>(
155 reader: &mut R,
156 parsing_mode: ParsingMode,
157) -> Result<Option<(ADTSHeader, u64)>>
158where
159 R: Read + Seek,
160{
161 let mut pos = reader.stream_position()?;
162
163 while let Ok(Some(first_adts_frame_start_relative)) = search_for_frame_sync(reader) {
164 let first_adts_frame_start_absolute = pos + first_adts_frame_start_relative;
165
166 reader.seek(SeekFrom::Start(first_adts_frame_start_absolute))?;
168
169 if let Some(first_header) = ADTSHeader::read(reader, parsing_mode)? {
170 let header_len = if first_header.has_crc { 9 } else { 7 };
171
172 match cmp_header(
173 reader,
174 header_len,
175 u32::from(first_header.len),
176 u32::from_be_bytes(first_header.bytes[..4].try_into().unwrap()),
177 HEADER_MASK,
178 ) {
179 HeaderCmpResult::Equal => {
180 return Ok(Some((
181 first_header,
182 first_adts_frame_start_absolute + u64::from(header_len),
183 )))
184 },
185 HeaderCmpResult::Undetermined => return Ok(None),
186 HeaderCmpResult::NotEqual => {},
187 }
188 }
189
190 pos = reader.stream_position()?;
191 }
192
193 Ok(None)
194}