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