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