vapor_parser/analyser/
mod.rs1use std::{io::SeekFrom, time::Duration};
2
3use crate::{
4 error::{self, Result},
5 frames::{
6 decode_header,
7 types::AudioFrame,
8 utils::{
9 decode_xing_header, get_frame_duration, get_frame_duration_from_header,
10 get_frame_size_from_header, get_samples_per_frame_from_header,
11 get_xing_offset_from_header, is_xing_or_info,
12 },
13 },
14 id3v1::ID3V1Tag,
15 id3v2::ID3V2Tag,
16 utils::XING_BUFFER_SIZE,
17};
18use tokio::{
19 fs::File,
20 io::{AsyncReadExt, AsyncSeekExt, BufReader},
21};
22
23#[derive(Copy, Clone, PartialEq, Eq, Debug)]
24pub struct AudioFramesIndexItem {
25 offset: usize,
26 time: usize,
27}
28
29#[derive(Debug)]
30pub struct Analyser {
31 reader: BufReader<File>,
32 reader_offset: u64,
33}
34
35#[derive(Debug)]
36pub struct Analysis {
37 pub audio_frames: Vec<AudioFrame>,
38 pub id3v2: Option<ID3V2Tag>,
39 pub id3v1: Option<ID3V1Tag>,
40}
41
42impl Analyser {
43 pub fn new(reader: BufReader<File>) -> Self {
44 Self {
45 reader,
46 reader_offset: 0,
47 }
48 }
49
50 async fn read(&mut self, buffer: &mut [u8]) -> Result<u64> {
51 let bytes_read = match self.reader.read(buffer).await {
52 Ok(bytes_read) => bytes_read,
53 Err(_) => Err(error::Error::UnexpectedEof)?,
54 };
55
56 self.reader_offset += bytes_read as u64;
57 Ok(bytes_read as u64)
58 }
59
60 async fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
61 let offset = self.reader.seek(pos).await.unwrap();
62 self.reader_offset = offset.try_into().unwrap();
63 Ok(offset)
64 }
65
66 async fn fetch_audio_frames(
67 &mut self,
68 hook: impl Fn(AudioFrame) -> bool + Copy,
69 ) -> Result<Vec<AudioFrame>> {
70 let mut found_first_header = false;
71 let mut found_first_xing = false;
72 let mut buffer = [0; 4];
73 let mut audio_frames: Vec<AudioFrame> = Vec::new();
74
75 self.seek(std::io::SeekFrom::Start(0)).await?;
76
77 loop {
78 let bytes_read = if !found_first_header {
81 buffer[0] = buffer[1];
82 buffer[1] = buffer[2];
83 buffer[2] = buffer[3];
84
85 match self.read(&mut buffer[3..]).await {
86 Ok(bytes_read) => bytes_read,
87 Err(_) => break,
88 }
89 } else {
90 match self.read(&mut buffer).await {
91 Ok(bytes_read) => bytes_read,
92 Err(_) => break,
93 }
94 };
95
96 if bytes_read == 0 {
98 break;
99 }
100
101 let header = match decode_header(&buffer) {
102 Ok(header) => header,
103 Err(_) => continue,
104 };
105
106 let current_offset = self.reader_offset - buffer.len() as u64;
107 let xing_offset = get_xing_offset_from_header(&header);
108 let xing_header = if !found_first_xing {
109 self.seek(std::io::SeekFrom::Current(xing_offset.try_into().unwrap()))
110 .await?;
111
112 let mut xing_buffer = [0u8; XING_BUFFER_SIZE];
113 self.read(&mut xing_buffer).await?;
114
115 if is_xing_or_info(&xing_buffer) {
116 let xing_header = decode_xing_header(&xing_buffer)?;
117 Some(xing_header)
118 } else {
119 None
120 }
121 } else {
122 None
123 };
124
125 let frame_size = get_frame_size_from_header(&header);
128 let bytes_to_next_frame = if !found_first_xing {
129 frame_size - buffer.len() as u64 - xing_offset - XING_BUFFER_SIZE as u64
130 } else {
131 frame_size - buffer.len() as u64
132 };
133
134 found_first_header = true;
135 if xing_header.is_some() {
136 found_first_xing = true;
137 }
138
139 let audio_frame = AudioFrame {
140 header,
141 xing_header,
142 offset: current_offset,
143 data: buffer.to_vec(),
144 };
145
146 audio_frames.push(audio_frame.clone());
147
148 if !hook(audio_frame.clone()) {
149 break;
150 }
151
152 self.seek(std::io::SeekFrom::Current(bytes_to_next_frame as i64))
153 .await
154 .unwrap();
155 }
156
157 Ok(audio_frames)
158 }
159
160 pub async fn get_audio_frames(mut self) -> Result<Vec<AudioFrame>> {
161 let ref mut this = self;
162 let hook = |_: AudioFrame| true;
163 let audio_frames = this.fetch_audio_frames(hook).await?;
164 Ok(audio_frames)
165 }
166
167 pub async fn get_duration(mut self) -> Result<Duration> {
168 let audio_frames = {
169 let hook = |audio_frame: AudioFrame| {
171 if audio_frame.xing_header.is_some() {
172 let xing_header = audio_frame.xing_header.as_ref().unwrap();
173 if xing_header.frames.is_some() {
174 return false;
175 }
176 }
177 true
178 };
179
180 async move {
181 let audio_frames = self.fetch_audio_frames(hook).await?;
182 Ok(audio_frames)
183 }
184 }
185 .await?;
186
187 let mut nanos = 0;
188 for audio_frame in audio_frames.iter() {
189 if audio_frame.xing_header.is_some() {
190 let xing_header = audio_frame.xing_header.as_ref().unwrap();
191 if xing_header.frames.is_some() {
192 let samples_per_frame = get_samples_per_frame_from_header(&audio_frame.header);
193 let frames = xing_header.frames.unwrap();
194 let duration =
195 get_frame_duration(samples_per_frame, audio_frame.header.sample_rate.hz())
196 * frames as u32;
197
198 return Ok(duration);
199 }
200 }
201
202 let frame_duration = get_frame_duration_from_header(&audio_frame.header).as_nanos();
203 nanos += frame_duration;
204 }
205
206 Ok(Duration::from_nanos(nanos as u64))
207 }
208}