raw_player/
audio_player.rs1use std::collections::VecDeque;
2use std::sync::Mutex;
3
4use crate::audio_format::AudioFormat;
5use crate::audio_stream::AudioStream;
6use crate::error::{Error, Result};
7use crate::ffi;
8
9struct AudioChunk {
10 pts_us: i64,
11 sample_rate: i32,
12 channels: i32,
13 format: AudioFormat,
14 data: Vec<u8>,
15}
16
17#[derive(Debug, Clone)]
18pub struct AudioPlayerStats {
19 pub audio_queue_size: usize,
20 pub audio_buffer_ms: f64,
21 pub chunks_played: i64,
22 pub audio_clock_us: i64,
23 pub sample_rate: i32,
24 pub channels: i32,
25 pub is_float: bool,
26 pub total_samples_enqueued: i64,
27 pub total_samples_played: i64,
28 pub elapsed_time_ms: f64,
29 pub audio_bitrate_kbps: f64,
30}
31
32struct AudioPlayerInner {
33 stream: Option<AudioStream>,
34 audio_queue: VecDeque<AudioChunk>,
35 sample_rate: i32,
36 channels: i32,
37 is_float: bool,
38 samples_written: i64,
39 first_pts_us: i64,
40 audio_started: bool,
41 playing: bool,
42 has_played: bool,
43 volume: f32,
44 chunks_played: i64,
45 total_samples_enqueued: i64,
46 play_start_time_ns: u64,
47}
48
49pub struct AudioPlayer {
50 inner: Mutex<AudioPlayerInner>,
51}
52
53impl AudioPlayer {
54 pub fn new() -> Self {
55 Self {
56 inner: Mutex::new(AudioPlayerInner {
57 stream: None,
58 audio_queue: VecDeque::new(),
59 sample_rate: 0,
60 channels: 0,
61 is_float: false,
62 samples_written: 0,
63 first_pts_us: 0,
64 audio_started: false,
65 playing: false,
66 has_played: false,
67 volume: 1.0,
68 chunks_played: 0,
69 total_samples_enqueued: 0,
70 play_start_time_ns: 0,
71 }),
72 }
73 }
74
75 pub fn enqueue_audio(
77 &self,
78 data: &[u8],
79 pts_us: i64,
80 sample_rate: i32,
81 channels: i32,
82 format: AudioFormat,
83 ) -> Result<()> {
84 if data.is_empty() {
85 return Err(Error::invalid_argument("data is empty"));
86 }
87 if sample_rate <= 0 {
88 return Err(Error::invalid_argument("sample_rate must be positive"));
89 }
90 if channels <= 0 {
91 return Err(Error::invalid_argument("channels must be positive"));
92 }
93
94 let sample_size = format.sample_size();
95 let frame_size = channels as usize * sample_size;
96 if !data.len().is_multiple_of(frame_size) {
97 return Err(Error::invalid_argument(
98 "data size is not aligned to frame size",
99 ));
100 }
101
102 let num_samples = data.len() as i64 / (channels as i64 * sample_size as i64);
103
104 let mut inner = self.inner.lock().unwrap();
105
106 if inner.has_played && !inner.playing {
107 return Err(Error::NotPlaying);
108 }
109
110 inner.total_samples_enqueued += num_samples;
111 inner.audio_queue.push_back(AudioChunk {
112 pts_us,
113 sample_rate,
114 channels,
115 format,
116 data: data.to_vec(),
117 });
118
119 Ok(())
120 }
121
122 pub fn play(&self) -> Result<()> {
124 let mut inner = self.inner.lock().unwrap();
125 if !inner.playing {
126 inner.playing = true;
127 inner.has_played = true;
128 if inner.play_start_time_ns == 0 {
129 inner.play_start_time_ns = unsafe { ffi::SDL_GetTicksNS() };
130 }
131 Self::process_audio_queue(&mut inner)?;
132 if let Some(ref mut stream) = inner.stream {
133 stream.resume()?;
134 }
135 }
136 Ok(())
137 }
138
139 pub fn pause(&self) -> Result<()> {
141 let mut inner = self.inner.lock().unwrap();
142 if inner.playing {
143 inner.playing = false;
144 if let Some(ref mut stream) = inner.stream {
145 stream.pause()?;
146 }
147 }
148 Ok(())
149 }
150
151 pub fn stop(&self) -> Result<()> {
153 let mut inner = self.inner.lock().unwrap();
154 inner.playing = false;
155 inner.has_played = false;
156 inner.audio_queue.clear();
157 if let Some(ref mut stream) = inner.stream {
158 stream.pause()?;
159 stream.clear()?;
160 }
161 inner.samples_written = 0;
162 inner.audio_started = false;
163 inner.total_samples_enqueued = 0;
164 inner.play_start_time_ns = 0;
165 inner.chunks_played = 0;
166 Ok(())
167 }
168
169 pub fn is_playing(&self) -> bool {
170 self.inner.lock().unwrap().playing
171 }
172
173 pub fn volume(&self) -> f32 {
174 self.inner.lock().unwrap().volume
175 }
176
177 pub fn set_volume(&self, volume: f32) -> Result<()> {
178 let mut inner = self.inner.lock().unwrap();
179 let clamped = volume.clamp(0.0, 1.0);
180 inner.volume = clamped;
181 if let Some(ref mut stream) = inner.stream {
182 stream.set_gain(clamped)?;
183 }
184 Ok(())
185 }
186
187 pub fn audio_clock_us(&self) -> i64 {
189 let inner = self.inner.lock().unwrap();
190 Self::get_audio_clock_us(&inner)
191 }
192
193 pub fn is_started(&self) -> bool {
195 self.inner.lock().unwrap().audio_started
196 }
197
198 pub fn process(&self) -> Result<()> {
200 let mut inner = self.inner.lock().unwrap();
201 if inner.playing {
202 Self::process_audio_queue(&mut inner)?;
203 }
204 Ok(())
205 }
206
207 pub fn audio_queue_ms(&self) -> f64 {
209 let inner = self.inner.lock().unwrap();
210 if inner.sample_rate > 0 {
211 let sample_size: i64 = if inner.is_float { 4 } else { 2 };
212 let bytes_per_frame = inner.channels as i64 * sample_size;
213 let queued_bytes: i64 = inner.audio_queue.iter().map(|c| c.data.len() as i64).sum();
214 let stream_queued = inner
215 .stream
216 .as_ref()
217 .map(|s| s.queued_bytes() as i64)
218 .unwrap_or(0);
219 let total = queued_bytes + stream_queued;
220 if bytes_per_frame > 0 {
221 let samples = total / bytes_per_frame;
222 samples as f64 * 1000.0 / inner.sample_rate as f64
223 } else {
224 0.0
225 }
226 } else {
227 0.0
228 }
229 }
230
231 pub fn stats(&self) -> AudioPlayerStats {
232 let inner = self.inner.lock().unwrap();
233
234 let clock_us = Self::get_audio_clock_us(&inner);
235
236 let buffer_ms = if inner.sample_rate > 0 {
237 let sample_size = if inner.is_float { 4 } else { 2 };
238 let bytes_per_frame = inner.channels as i64 * sample_size;
239 let total_queued_bytes: i64 =
240 inner.audio_queue.iter().map(|c| c.data.len() as i64).sum();
241 let stream_queued = inner
242 .stream
243 .as_ref()
244 .map(|s| s.queued_bytes() as i64)
245 .unwrap_or(0);
246 let total_bytes = total_queued_bytes + stream_queued;
247 if bytes_per_frame > 0 {
248 let samples = total_bytes / bytes_per_frame;
249 samples as f64 * 1000.0 / inner.sample_rate as f64
250 } else {
251 0.0
252 }
253 } else {
254 0.0
255 };
256
257 let elapsed_ms = if inner.has_played {
258 let now = unsafe { ffi::SDL_GetTicksNS() };
259 (now - inner.play_start_time_ns) as f64 / 1_000_000.0
260 } else {
261 0.0
262 };
263
264 let sample_size = if inner.is_float { 4 } else { 2 };
265 let bitrate_kbps = if inner.sample_rate > 0 {
266 inner.sample_rate as f64 * inner.channels as f64 * sample_size as f64 * 8.0 / 1000.0
267 } else {
268 0.0
269 };
270
271 let queued_samples = inner
272 .stream
273 .as_ref()
274 .map(|s| {
275 let bytes = s.queued_bytes() as i64;
276 let bytes_per_frame = inner.channels as i64 * sample_size;
277 if bytes_per_frame > 0 {
278 bytes / bytes_per_frame
279 } else {
280 0
281 }
282 })
283 .unwrap_or(0);
284 let played_samples = (inner.samples_written - queued_samples).max(0);
285
286 AudioPlayerStats {
287 audio_queue_size: inner.audio_queue.len(),
288 audio_buffer_ms: buffer_ms,
289 chunks_played: inner.chunks_played,
290 audio_clock_us: clock_us,
291 sample_rate: inner.sample_rate,
292 channels: inner.channels,
293 is_float: inner.is_float,
294 total_samples_enqueued: inner.total_samples_enqueued,
295 total_samples_played: played_samples,
296 elapsed_time_ms: elapsed_ms,
297 audio_bitrate_kbps: bitrate_kbps,
298 }
299 }
300
301 fn get_audio_clock_us(inner: &AudioPlayerInner) -> i64 {
302 if !inner.audio_started || inner.stream.is_none() {
303 return 0;
304 }
305
306 let sample_size: i64 = if inner.is_float { 4 } else { 2 };
307 let bytes_per_frame = inner.channels as i64 * sample_size;
308
309 let queued_bytes = inner
310 .stream
311 .as_ref()
312 .map(|s| s.queued_bytes() as i64)
313 .unwrap_or(0);
314
315 let queued_samples = if bytes_per_frame > 0 {
316 queued_bytes / bytes_per_frame
317 } else {
318 0
319 };
320
321 let played_samples = (inner.samples_written - queued_samples).max(0);
322
323 if inner.sample_rate > 0 {
324 let played_us = played_samples * 1_000_000 / inner.sample_rate as i64;
325 inner.first_pts_us + played_us
326 } else {
327 0
328 }
329 }
330
331 fn process_audio_queue(inner: &mut AudioPlayerInner) -> Result<()> {
332 while let Some(chunk) = inner.audio_queue.pop_front() {
333 let format_changed = inner.stream.is_some()
334 && (chunk.sample_rate != inner.sample_rate
335 || chunk.channels != inner.channels
336 || chunk.format.is_float() != inner.is_float);
337
338 if inner.stream.is_none() || format_changed {
339 inner.stream = None;
340 let mut stream =
341 match AudioStream::open(chunk.sample_rate, chunk.channels, chunk.format) {
342 Ok(s) => s,
343 Err(e) => {
344 inner.audio_queue.push_front(chunk);
345 return Err(e);
346 }
347 };
348 if let Err(e) = stream.set_gain(inner.volume) {
349 inner.audio_queue.push_front(chunk);
350 return Err(e);
351 }
352 if inner.playing
353 && let Err(e) = stream.resume()
354 {
355 inner.audio_queue.push_front(chunk);
356 return Err(e);
357 }
358 inner.stream = Some(stream);
359 inner.sample_rate = chunk.sample_rate;
360 inner.channels = chunk.channels;
361 inner.is_float = chunk.format.is_float();
362 inner.samples_written = 0;
363 inner.first_pts_us = chunk.pts_us;
365 inner.audio_started = true;
366 }
367
368 if !inner.audio_started {
369 inner.first_pts_us = chunk.pts_us;
370 inner.audio_started = true;
371 }
372
373 let sample_size = if inner.is_float { 4 } else { 2 };
374 let bytes_per_frame = inner.channels as i64 * sample_size;
375 let num_samples = if bytes_per_frame > 0 {
376 chunk.data.len() as i64 / bytes_per_frame
377 } else {
378 0
379 };
380
381 if let Some(ref mut stream) = inner.stream
382 && let Err(e) = stream.put_data(&chunk.data)
383 {
384 inner.audio_queue.push_front(chunk);
385 return Err(e);
386 }
387
388 inner.samples_written += num_samples;
389 inner.chunks_played += 1;
390 }
391 Ok(())
392 }
393}
394
395impl Default for AudioPlayer {
396 fn default() -> Self {
397 Self::new()
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use std::collections::VecDeque;
404
405 #[test]
408 fn requeue_after_pop_restores_queue_order() {
409 let mut q = VecDeque::from([1u32, 2, 3]);
410 let c = q.pop_front().unwrap();
411 assert_eq!(c, 1);
412 q.push_front(c);
413 assert_eq!(q.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
414 }
415}