sndplayer: free audio device after playback is over
[nihav-player.git] / sndplay / src / main.rs
CommitLineData
b043bd0a
KS
1extern crate libc;
2extern crate sdl2_sys;
3extern crate nihav_core;
4extern crate nihav_registry;
5extern crate nihav_allstuff;
6
7use std::fs::File;
8use std::io::prelude::*;
9use std::io::{BufReader, SeekFrom};
10use std::sync::mpsc;
11use std::time::Duration;
12use std::thread;
13use sdl2_sys::{SDL_AudioSpec, Uint32};
14use nihav_core::io::byteio::{FileReader, ByteReader};
15use nihav_core::frame::*;
16use nihav_core::codecs::*;
17use nihav_core::demuxers::*;
18use nihav_core::soundcvt::*;
19use nihav_registry::detect;
20use nihav_allstuff::*;
21
22mod command;
23use command::*;
24
25struct Player {
26 ended: bool,
27 dmx_reg: RegisteredDemuxers,
28 dec_reg: RegisteredDecoders,
29 paused: bool,
30 mute: bool,
31 volume: u8,
32 debug: bool,
33 buf: Vec<i16>,
34}
35
36struct AudioDevice {
37 device_id: sdl2_sys::SDL_AudioDeviceID
38}
39
40impl AudioDevice {
41 fn open(arate: u32, channels: u8) -> Option<(Self, SDL_AudioSpec)> {
42 let desired_spec = SDL_AudioSpec {
43 freq: arate as i32,
44 format: sdl2_sys::AUDIO_S16 as u16,
45 channels,
46 silence: 0,
47 samples: 0,
48 padding: 0,
49 size: 0,
50 callback: None,
51 userdata: std::ptr::null_mut(),
52 };
53 let mut dspec = desired_spec;
54 let device_id = unsafe { sdl2_sys::SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_spec, &mut dspec, 0) };
55 if device_id != 0 {
56 Some((AudioDevice { device_id }, dspec))
57 } else {
58 None
59 }
60 }
61 fn pause(&self) {
62 unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 1); }
63 }
64 fn resume(&self) {
65 unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 0); }
66 }
67 fn clear(&self) {
68 unsafe { sdl2_sys::SDL_ClearQueuedAudio(self.device_id); }
69 }
70 fn queue(&self, buf: &[i16]) {
71 unsafe {
72 let len = buf.len() * 2;
73 let buf_ptr = buf.as_ptr();
74 sdl2_sys::SDL_QueueAudio(self.device_id, buf_ptr as *const core::ffi::c_void, len as Uint32);
75 }
76 }
77 fn queue_bytes(&self, buf: &[u8]) {
78 unsafe {
79 let len = buf.len();
80 let buf_ptr = buf.as_ptr();
81 sdl2_sys::SDL_QueueAudio(self.device_id, buf_ptr as *const core::ffi::c_void, len as Uint32);
82 }
83 }
84 fn size(&self) -> u32 {
85 unsafe { sdl2_sys::SDL_GetQueuedAudioSize(self.device_id) }
86 }
87}
88
65606213
KS
89impl Drop for AudioDevice {
90 fn drop(&mut self) {
91 unsafe { sdl2_sys::SDL_CloseAudioDevice(self.device_id); }
92 }
93}
94
b043bd0a
KS
95struct Decoder<'a> {
96 demuxer: Demuxer<'a>,
97 decoder: Box<dyn NADecoder>,
98 dsupp: Box<NADecoderSupport>,
99 buf: &'a mut Vec<i16>,
100 stream_no: usize,
101 dst_info: NAAudioInfo,
102 dst_chmap: NAChannelMap,
103 samplepos: u64,
104 arate: u32,
105 volume: u8,
106 mute: bool,
107}
108
109fn output_vol_i16(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[i16], mute: bool, volume: u8) {
110 if !mute {
111 tmp.truncate(0);
112 tmp.reserve(src.len());
113 let vol = i32::from(volume);
114 for &sample in src.iter() {
115 let nsamp = vol * i32::from(sample) / 100;
116 tmp.push(nsamp.min(32767).max(-32768) as i16);
117 }
118 } else {
119 tmp.truncate(0);
120 tmp.resize(src.len(), 0);
121 }
122 device.queue(&tmp);
123}
124
125fn output_vol_u8(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[u8], mute: bool, volume: u8) {
126 if !mute {
127 tmp.truncate(0);
128 tmp.reserve(src.len());
129 let vol = i32::from(volume);
130 for sample in src.chunks_exact(2) {
131 let sample = (u16::from(sample[0]) + u16::from(sample[1]) * 256) as i16;
132 let nsamp = vol * i32::from(sample) / 100;
133 tmp.push(nsamp.min(32767).max(-32768) as i16);
134 }
135 } else {
136 tmp.truncate(0);
137 tmp.resize(src.len() / 2, 0);
138 }
139 device.queue(&tmp);
140}
141
142impl<'a> Decoder<'a> {
143 fn refill(&mut self, device: &AudioDevice) -> bool {
144 loop {
145 match self.demuxer.get_frame() {
146 Ok(pkt) => {
147 if pkt.get_stream().get_num() == self.stream_no {
148 match self.decoder.decode(&mut self.dsupp, &pkt) {
149 Ok(frm) => {
150 let buf = frm.get_buffer();
151 if let Some(pts) = frm.ts.get_pts() {
152 self.samplepos = NATimeInfo::ts_to_time(pts, u64::from(self.arate), frm.ts.tb_num, frm.ts.tb_den);
153 }
154 let out_buf = convert_audio_frame(&buf, &self.dst_info, &self.dst_chmap).unwrap();
155 match out_buf {
156 NABufferType::AudioI16(abuf) => {
157 if !self.mute && self.volume == 100 {
158 device.queue(&abuf.get_data());
159 } else {
160 output_vol_i16(device, self.buf, &abuf.get_data(), self.mute, self.volume);
161 }
162 self.samplepos += abuf.get_length() as u64;
163 },
164 NABufferType::AudioPacked(abuf) => {
165 if !self.mute && self.volume == 100 {
166 device.queue_bytes(&abuf.get_data());
167 } else {
168 output_vol_u8(device, self.buf, &abuf.get_data(), self.mute, self.volume);
169 }
170 self.samplepos += abuf.get_length() as u64;
171 },
172 _ => println!("unknown buffer type"),
173 };
174 return false;
175 },
176 Err(err) => {
177 println!(" error decoding {:?}", err);
178 return true;
179 },
180 };
181 }
182 },
183 Err(DemuxerError::EOF) => return true,
184 Err(err) => {
185 println!("demuxing error {:?}", err);
186 return true;
187 },
188 };
189 }
190 }
191 fn seek(&mut self, time: u64) -> bool {
192 let ret = self.demuxer.seek(NATimePoint::Milliseconds(time));
193if ret.is_err() { println!(" seek error\n"); }
194 ret.is_ok()
195 }
196}
197
198fn format_time(ms: u64) -> String {
199 let s = ms / 1000;
200 let ds = (ms % 1000) / 100;
201 let (min, s) = (s / 60, s % 60);
be48734e
KS
202 let (h, min) = (min / 60, min % 60);
203 if h == 0 {
204 if min == 0 {
205 format!("{}.{}", s, ds)
206 } else {
207 format!("{}:{:02}.{}", min, s, ds)
208 }
b043bd0a 209 } else {
be48734e 210 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
b043bd0a
KS
211 }
212}
213
214impl Player {
215 fn new() -> Self {
216 let mut dmx_reg = RegisteredDemuxers::new();
217 nihav_register_all_demuxers(&mut dmx_reg);
218 let mut dec_reg = RegisteredDecoders::new();
219 nihav_register_all_decoders(&mut dec_reg);
220
221 unsafe {
222 if sdl2_sys::SDL_Init(sdl2_sys::SDL_INIT_AUDIO) != 0 {
223 panic!("cannot init SDL");
224 }
225 }
226
227 Self {
228 ended: false,
229 paused: false,
230 dmx_reg, dec_reg,
231 volume: 100,
232 mute: false,
233 debug: false,
234 buf: Vec::new(),
235 }
236 }
36ac48e5 237 fn play_file(&mut self, name: &str, cmd_receiver: &mpsc::Receiver<Command>, start_time: NATimePoint) {
b043bd0a
KS
238 let ret = File::open(name);
239 if ret.is_err() {
240 println!("error opening {}", name);
241 return;
242 }
243 let mut file = ret.unwrap();
244
245 let mut fr = FileReader::new_read(&mut file);
246 let mut br = ByteReader::new(&mut fr);
247 let res = detect::detect_format(name, &mut br);
248 if res.is_none() {
249 println!("cannot detect format for {}", name);
250 return;
251 }
252 let (dmx_name, _) = res.unwrap();
253 drop(br);
254 drop(fr);
255 let dmx_fact = self.dmx_reg.find_demuxer(dmx_name);
256 if dmx_fact.is_none() {
257 println!("no demuxer for format {}", dmx_name);
258 return;
259 }
260 let dmx_fact = dmx_fact.unwrap();
261
262 file.seek(SeekFrom::Start(0)).unwrap();
263 let mut file = BufReader::new(file);
264 let mut fr = FileReader::new_read(&mut file);
265 let mut br = ByteReader::new(&mut fr);
266 let res = create_demuxer(dmx_fact, &mut br);
267 if res.is_err() {
268 println!("cannot create demuxer");
269 return;
270 }
271 let dmx = res.unwrap();
272
273 let mut ainfo = None;
274 let mut dec: Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)> = None;
275 let mut stream_no = 0;
276 let mut duration = dmx.get_duration();
277 for i in 0..dmx.get_num_streams() {
278 let s = dmx.get_stream(i).unwrap();
279 let info = s.get_info();
280 if info.is_audio() {
281 let decfunc = self.dec_reg.find_decoder(info.get_name());
282 if decfunc.is_none() {
283 println!("no decoder for {}", info.get_name());
284 continue;
285 }
286 let mut decoder = (decfunc.unwrap())();
287 let mut dsupp = Box::new(NADecoderSupport::new());
288 if decoder.init(&mut dsupp, info.clone()).is_err() {
289 println!("cannot init decoder for stream {}", i);
290 continue;
291 }
292 dec = Some((dsupp, decoder));
293 ainfo = Some(info);
294 stream_no = i;
295 if s.duration > 0 {
296 duration = NATimeInfo::ts_to_time(s.duration, 1000, s.tb_num, s.tb_den);
297 }
298 break;
299 }
300 }
301 if dec.is_none() {
302 println!("no audio decoder found");
303 return;
304 }
305 let (dsupp, decoder) = dec.unwrap();
306
307 let ainfo = ainfo.unwrap().get_properties().get_audio_info().unwrap();
308 let arate = if ainfo.sample_rate > 0 { ainfo.sample_rate } else { 44100 };
309 let ch = ainfo.channels;
310
311 println!("Playing {} [{}Hz {}ch]", name, arate, ch);
312 let ret = AudioDevice::open(arate, ch.max(2));
313 if ret.is_none() {
314 println!("cannot open output");
315 return;
316 }
317 let (device, dspec) = ret.unwrap();
318 let block_limit = dmx.get_stream(stream_no).unwrap().tb_num * arate / dmx.get_stream(stream_no).unwrap().tb_den * u32::from(dspec.channels);
319
320 let dst_info = NAAudioInfo { sample_rate: dspec.freq as u32, channels: dspec.channels, format: SND_S16_FORMAT, block_len: 1 };
321 let dst_chmap = if dst_info.channels == 2 {
322 NAChannelMap::from_str("L,R").unwrap()
323 } else {
324 NAChannelMap::from_str("C").unwrap()
325 };
326 let mut decoder = Decoder {
327 demuxer: dmx,
328 decoder, dsupp,
329 stream_no,
330 dst_info, dst_chmap,
331 samplepos: 0,
332 arate,
333 volume: self.volume,
334 mute: self.mute,
335 buf: &mut self.buf,
336 };
337 let mut refill_limit = arate * u32::from(dspec.channels);
338 let underfill_limit = (arate * u32::from(dspec.channels) / 4).max(block_limit);
339
36ac48e5
KS
340 if start_time != NATimePoint::None {
341 let _ret = decoder.demuxer.seek(start_time);
342 }
343
b043bd0a
KS
344 let mut eof = decoder.refill(&device);
345 while !eof && device.size() < refill_limit {
346 eof = decoder.refill(&device);
347 }
348
349 let duration_str = if duration != 0 { format_time(duration) } else { "???".to_owned() };
350 if !self.paused {
351 device.resume();
352 }
0a727e05 353 let mut no_display = false;
b043bd0a
KS
354 'main: loop {
355 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
356 let full_ms = cur_time * 1000 / u64::from(arate);
357 let timestr = format_time(full_ms);
358 let disp_vol = if self.mute { 0 } else { self.volume };
0a727e05
KS
359 if !no_display {
360 if !self.debug {
361 print!("> {} / {} {}% \r", timestr, duration_str, disp_vol);
362 } else {
363 print!("> {} / {} |{}| {}% \r", timestr, duration_str, device.size(), disp_vol);
364 }
b043bd0a
KS
365 }
366 std::io::stdout().flush().unwrap();
367 if device.size() < underfill_limit && !self.paused && refill_limit < (1 << 20) {
368 if full_ms > 5000 {
369 println!("underrun!");
370 }
371 refill_limit += refill_limit >> 1;
372 }
373 if device.size() < refill_limit / 2 {
374 while !eof && device.size() < refill_limit {
375 eof = decoder.refill(&device);
376 }
377 }
378 if eof && device.size() == 0 {
379 break 'main;
380 }
381 while let Ok(cmd) = cmd_receiver.try_recv() {
382 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
383 match cmd {
384 Command::Forward(val) => {
385 device.pause();
386 device.clear();
387 let seekoff = match val {
388 1 => 10,
389 2 => 60,
390 _ => 10 * 60,
391 };
392 let seek_time = cur_time * 1000 / u64::from(arate) + seekoff * 1000;
393 let _ret = decoder.seek(seek_time);
394 while !eof && device.size() < refill_limit {
395 eof = decoder.refill(&device);
396 }
397 if eof {
398 break 'main;
399 }
400 if !self.paused {
401 device.resume();
402 }
403 },
404 Command::Back(val) => {
405 device.pause();
406 device.clear();
407 let seekoff = match val {
408 1 => 10,
409 2 => 60,
410 _ => 10 * 60,
411 };
412 let seek_time = (cur_time * 1000 / u64::from(arate)).saturating_sub(seekoff * 1000);
413 let _ret = decoder.seek(seek_time);
414 while !eof && device.size() < refill_limit {
415 eof = decoder.refill(&device);
416 }
417 if eof {
418 break 'main;
419 }
420 if !self.paused {
421 device.resume();
422 }
423 },
0a727e05
KS
424 Command::Seek(seek_time) => {
425 device.pause();
426 device.clear();
427 let _ret = decoder.seek(seek_time);
428 while !eof && device.size() < refill_limit {
429 eof = decoder.refill(&device);
430 }
431 if eof {
432 break 'main;
433 }
434 if !self.paused {
435 device.resume();
436 }
437 },
b043bd0a
KS
438 Command::Quit => {
439 device.pause();
440 self.ended = true;
441 break 'main;
442 },
443 Command::Next => {
444 device.pause();
445 break 'main;
446 },
447 Command::Repeat => {
448 device.pause();
449 device.clear();
450 let _ret = decoder.seek(0);
451 while !eof && device.size() < refill_limit {
452 eof = decoder.refill(&device);
453 }
454 if eof {
455 break 'main;
456 }
457 if !self.paused {
458 device.resume();
459 }
460 },
461 Command::Pause => {
462 self.paused = !self.paused;
463 if self.paused {
464 device.pause();
465 } else {
466 device.resume();
467 }
468 },
469 Command::VolumeUp => {
470 self.volume = (self.volume + 10).min(200);
471 decoder.volume = self.volume;
472 },
473 Command::VolumeDown => {
474 self.volume = self.volume.saturating_sub(10);
475 decoder.volume = self.volume;
476 },
477 Command::Mute => {
478 self.mute = !self.mute;
479 decoder.mute = self.mute;
480 },
481 Command::Debug => {
482 self.debug = !self.debug;
483 },
98431ed5
KS
484 Command::PauseDisplay => {
485 no_display = true;
486 if !self.paused {
487 device.pause();
488 }
489 },
490 Command::ResumeDisplay => {
491 no_display = false;
492 if !self.paused {
493 device.resume();
494 }
495 },
b043bd0a 496 };
0a727e05
KS
497 if !no_display {
498 print!("\r{:60}\r", ' ');
499 }
b043bd0a
KS
500 }
501 thread::sleep(Duration::from_millis(200));
502 }
503
504 println!();
505 }
506}
507
508fn main() {
509 let args: Vec<String> = std::env::args().collect();
510
511 let cmd_state = CmdLineState::new();
512
513 let (cmd_reader_thread, cmd_receiver) = start_reader();
514 let mut player = Player::new();
515
516 if args.len() == 1 {
517 println!("usage: nihav-sndplay file1 file2 ...");
518 return;
519 }
520
521 if args[1] == "--help" {
522 println!("usage: nihav-sndplay file1 file2 ...");
523 println!("commands:");
524 println!(" escape / q - quit");
525 println!(" space - pause / resume playback");
526 println!(" enter / end - play next file");
527 println!(" home - play current track from the beginning");
528 println!(" left / right - seek 10 seconds forward / back");
529 println!(" up / down - seek 1 minute forward / back");
530 println!(" pgup / pgdn - seek 10 minutes forward / back");
531 println!(" + / - - increase / decrease volume");
532 println!(" m - mute output");
533 return;
534 }
535
36ac48e5
KS
536 let mut aiter = args[1..].iter();
537 let mut start_time = NATimePoint::None;
538 while let Some(arg) = aiter.next() {
539 match arg.as_str() {
540 "-start" => {
541 if let Some(arg) = aiter.next() {
542 if let Ok(val) = arg.parse() {
543 start_time = val;
544 } else {
545 println!("invalid time");
546 }
547 } else {
548 println!("argument is required");
549 }
550 },
551 _ => {
552 player.play_file(arg, &cmd_receiver, start_time);
553 if player.ended {
554 break;
555 }
556 start_time = NATimePoint::None;
557 },
558 };
b043bd0a
KS
559 }
560 cmd_state.restore();
561
562 drop(cmd_reader_thread);
563 unsafe { sdl2_sys::SDL_Quit(); }
564}