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