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