videoplayer: allow custom scaling
[nihav-player.git] / videoplayer / src / main.rs
CommitLineData
69b93cb5
KS
1extern crate sdl2;
2extern crate nihav_core;
3extern crate nihav_registry;
4extern crate nihav_allstuff;
5
6use std::env;
7use std::fs::File;
8use std::io::Write;
9use std::path::Path;
10use std::time::{Duration, Instant};
11use std::thread;
4e72c04a 12use std::sync::atomic::{AtomicU8, Ordering};
69b93cb5
KS
13
14use sdl2::event::{Event, WindowEvent};
8a51f59b 15use sdl2::keyboard::{Keycode, Mod};
2ef3529c 16use sdl2::mouse::{MouseButton, MouseWheelDirection};
69b93cb5
KS
17use sdl2::render::{Canvas, Texture, TextureCreator};
18use sdl2::pixels::PixelFormatEnum;
19use sdl2::video::{Window, WindowContext};
20
21use nihav_registry::detect;
22use nihav_core::frame::*;
23use nihav_core::io::byteio::{FileReader, ByteReader};
24use nihav_core::reorder::*;
25use nihav_core::codecs::*;
26use nihav_core::demuxers::*;
27use nihav_registry::register::*;
28use nihav_allstuff::*;
29
30mod audiodec;
31use audiodec::*;
32mod videodec;
33use videodec::*;
8686f9f6
KS
34mod osd;
35use osd::*;
69b93cb5 36
4e72c04a 37#[repr(u8)]
bbd69555 38#[derive(Clone,Copy,Debug,PartialEq,Default)]
4e72c04a 39enum DecodingState {
bbd69555 40 #[default]
4e72c04a
KS
41 Normal,
42 Waiting,
43 Flush,
44 Prefetch,
45 Error,
46 End,
47}
48
4e72c04a
KS
49impl From<u8> for DecodingState {
50 fn from(val: u8) -> Self {
51 match val {
52 0 => DecodingState::Normal,
53 1 => DecodingState::Waiting,
54 2 => DecodingState::Flush,
55 3 => DecodingState::Prefetch,
56 4 => DecodingState::End,
57 _ => DecodingState::Error,
58 }
59 }
60}
61
62struct DecoderState {
63 state: AtomicU8
64}
65
66impl DecoderState {
67 const fn new() -> Self {
68 Self {
69 state: AtomicU8::new(DecodingState::Normal as u8)
70 }
71 }
72 fn set_state(&self, state: DecodingState) {
73 self.state.store(state as u8, Ordering::Release);
74 }
75 fn get_state(&self) -> DecodingState {
76 self.state.load(Ordering::Acquire).into()
77 }
78 fn is_flushing(&self) -> bool {
79 matches!(self.get_state(), DecodingState::Flush | DecodingState::Error)
80 }
81}
82
69b93cb5
KS
83#[cfg(feature="debug")]
84macro_rules! debug_log {
85 ($log: expr; $blk: block) => {
86 $log.logfile.write($blk.as_bytes()).unwrap();
87 $log.logfile.write(b"\n").unwrap();
88 };
89}
90#[cfg(not(feature="debug"))]
91macro_rules! debug_log {
92 ($log: expr; $blk: block) => {};
93}
94
549511e9
KS
95enum ScaleSize {
96 Auto,
97 Times(f32),
98 Fixed(usize, usize)
99}
100
101impl FromStr for ScaleSize {
102 type Err = ();
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 if matches!(s, "" | "auto") {
105 Ok(ScaleSize::Auto)
106 } else if s.ends_with('x') || s.ends_with('X') {
107 let factor = s[..s.len() - 1].parse::<f32>().map_err(|_| ())?;
108 if factor > 0.0 {
109 Ok(ScaleSize::Times(factor))
110 } else {
111 Err(())
112 }
113 } else if s.contains('x') | s.contains('X') {
114 let mut dims = if s.contains('x') { s.split('x') } else { s.split('X') };
115 let w = dims.next().unwrap();
116 let h = dims.next().unwrap();
117 let width = w.parse::<usize>().map_err(|_| ())?;
118 let height = h.parse::<usize>().map_err(|_| ())?;
119 if width > 0 && height > 0 {
120 Ok(ScaleSize::Fixed(width, height))
121 } else {
122 Err(())
123 }
124 } else {
125 Err(())
126 }
127 }
128}
129
69b93cb5
KS
130pub enum PktSendEvent {
131 Packet(NAPacket),
37f130a7 132 GetFrames,
69b93cb5
KS
133 Flush,
134 End,
135 ImmediateEnd,
136 HurryUp,
137}
138
37f130a7
KS
139pub enum DecoderType {
140 Audio(Box<dyn NADecoder + Send>),
141 Video(Box<dyn NADecoder + Send>, Box<dyn FrameReorderer + Send>),
142 VideoMT(Box<dyn NADecoderMT + Send>, MTFrameReorderer),
143}
144
69b93cb5
KS
145pub struct DecoderStuff {
146 pub dsupp: Box<NADecoderSupport>,
37f130a7 147 pub dec: DecoderType,
69b93cb5
KS
148}
149
150fn format_time(ms: u64) -> String {
151 let s = ms / 1000;
152 let ds = (ms % 1000) / 100;
153 let (min, s) = (s / 60, s % 60);
154 let (h, min) = (min / 60, min % 60);
155 if h == 0 {
156 if min == 0 {
157 format!("{}.{}", s, ds)
158 } else {
159 format!("{}:{:02}.{}", min, s, ds)
160 }
161 } else {
162 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
163 }
164}
165
166const FRAME_QUEUE_LEN: usize = 25;
167const MAX_VOLUME: usize = 200;
168
169pub type FrameRecord = (NABufferType, u64);
170
171pub struct TimeKeep {
172 ref_time: Instant,
173 ref_ts: u64,
174}
175
176impl TimeKeep {
177 fn new() -> Self {
178 Self {
179 ref_time: Instant::now(),
180 ref_ts: 0,
181 }
182 }
183 pub fn get_cur_time(&self) -> u64 {
184 let add = self.ref_time.elapsed().as_millis() as u64;
185 self.ref_ts + add
186 }
187 fn reset_ts(&mut self) {
188 self.ref_ts = 0;
189 }
190 fn reset_all(&mut self, ts: u64) {
191 self.ref_time = Instant::now();
192 self.ref_ts = ts;
193 }
194 fn set_ts(&mut self) {
195 self.ref_ts = self.get_cur_time();
196 }
197 fn set_time(&mut self) {
198 self.ref_time = Instant::now();
199 }
200}
201
202pub struct DispFrame<'a> {
203 pub ts: u64,
204 pub is_yuv: bool,
205 pub valid: bool,
206 pub rgb_tex: Texture<'a>,
207 pub yuv_tex: Texture<'a>,
208}
209
210pub struct DispQueue<'a> {
211 pub pool: Vec<DispFrame<'a>>,
212 pub first_ts: u64,
213 pub last_ts: u64,
214 pub start: usize,
215 pub end: usize,
216 pub len: usize,
217 pub width: usize,
218 pub height: usize,
219}
220
221impl<'a> DispQueue<'a> {
222 fn new(texture_creator: &'a TextureCreator<WindowContext>, width: usize, height: usize, len: usize) -> Self {
223 let mut pool = Vec::with_capacity(len);
224 for _ in 0..len + 1 {
749a451e
KS
225 let rgb_tex = texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, width as u32, height as u32).expect("failed to create RGB texture");
226 let yuv_tex = texture_creator.create_texture_streaming(PixelFormatEnum::IYUV, ((width + 1) & !1) as u32, ((height + 1) & !1) as u32).expect("failed to create YUV texture");
69b93cb5
KS
227 pool.push(DispFrame{ ts: 0, is_yuv: false, valid: false, rgb_tex, yuv_tex });
228 }
229 pool[len].is_yuv = false;
230 pool[len].rgb_tex.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
231 for el in buffer.iter_mut() { *el = 0; }
749a451e 232 }).expect("RGB texture could not be locked");
69b93cb5
KS
233
234 Self { pool, first_ts: 0, last_ts: 0, start: 0, end: 0, len, width, height }
235 }
236
237 fn flush(&mut self) {
238 self.start = 0;
239 self.end = 0;
240 self.first_ts = 0;
241 self.last_ts = 0;
242 for frm in self.pool.iter_mut() {
243 frm.valid = false;
244 }
245 }
246
8686f9f6 247 fn get_last_texture(&mut self, osd: &OSD) -> &Texture<'a> {
69b93cb5 248 if self.pool[self.len].is_yuv {
8686f9f6
KS
249 if osd.is_active() {
250 self.pool[self.len].yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
251 osd.draw_yuv(buffer, pitch);
749a451e 252 }).expect("YUV texture locking failure");
8686f9f6 253 }
69b93cb5
KS
254 &self.pool[self.len].yuv_tex
255 } else {
8686f9f6
KS
256 if osd.is_active() {
257 self.pool[self.len].rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
258 osd.draw_rgb(buffer, pitch);
749a451e 259 }).expect("RGB texture locking failure");
8686f9f6 260 }
69b93cb5
KS
261 &self.pool[self.len].rgb_tex
262 }
263 }
264 pub fn is_empty(&self) -> bool { self.start == self.end }
265 pub fn is_full(&self) -> bool { self.len == 0 || self.start == (self.end + 1) % self.len }
266 pub fn move_end(&mut self) {
267 self.end += 1;
268 if self.end >= self.len {
269 self.end -= self.len;
270 }
271 }
272 pub fn move_start(&mut self) {
273 self.pool.swap(self.start, self.len);
274 self.start += 1;
275 if self.start >= self.len {
276 self.start -= self.len;
277 }
278 if !self.is_empty() {
279 self.first_ts = self.pool[self.start].ts;
280 }
281 }
282}
283
8686f9f6 284fn try_display(disp_queue: &mut DispQueue, canvas: &mut Canvas<Window>, osd: &mut OSD, ctime: &TimeKeep) -> Option<u64> {
69b93cb5
KS
285 while !disp_queue.is_empty() {
286 let disp_time = disp_queue.first_ts;
287 let ctime = ctime.get_cur_time();
288 if disp_time > ctime + 10 {
289 return Some(disp_time - ctime);
290 } else if disp_time + 10 < ctime {
291 disp_queue.move_start();
292 } else {
8686f9f6
KS
293 if osd.is_active() {
294 osd.prepare(ctime);
295 }
296 let frm = &mut disp_queue.pool[disp_queue.start];
297 let texture = if frm.is_yuv {
298 if osd.is_active() {
299 frm.yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
300 osd.draw_yuv(buffer, pitch);
749a451e 301 }).expect("YUV texture locking failure");
8686f9f6
KS
302 }
303 &frm.yuv_tex
304 } else {
305 if osd.is_active() {
306 frm.rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
307 osd.draw_rgb(buffer, pitch);
749a451e 308 }).expect("RGB texture locking failure");
8686f9f6
KS
309 }
310 &frm.rgb_tex
311 };
69b93cb5 312 canvas.clear();
749a451e 313 canvas.copy(texture, None, None).expect("canvas blit failure");
69b93cb5
KS
314 canvas.present();
315
316 disp_queue.move_start();
317 if !disp_queue.is_empty() {
318 return Some((disp_queue.first_ts - ctime).saturating_sub(2));
319 } else {
320 return None;
321 }
322 }
323 }
324 None
325}
326
327struct Player {
328 sdl_context: sdl2::Sdl,
329 vsystem: sdl2::VideoSubsystem,
330 asystem: sdl2::AudioSubsystem,
331
332 acontrol: AudioControl,
333 vcontrol: VideoControl,
334
335 play_video: bool,
336 play_audio: bool,
337 has_video: bool,
338 has_audio: bool,
339 video_str: u32,
340 audio_str: u32,
549511e9 341 sc_size: ScaleSize,
69b93cb5 342
37f130a7
KS
343 vthreads: usize,
344 use_mt: bool,
345
69b93cb5
KS
346 paused: bool,
347 mute: bool,
348 volume: usize,
349 end: bool,
350
351 tkeep: TimeKeep,
352
353 debug: bool,
8686f9f6 354 osd: OSD,
69b93cb5
KS
355
356 #[cfg(feature="debug")]
357 logfile: File,
358}
359
360impl Player {
361 fn new() -> Self {
749a451e
KS
362 let sdl_context = sdl2::init().expect("SDL2 init failure");
363 let vsystem = sdl_context.video().expect("video subsystem init failure");
364 let asystem = sdl_context.audio().expect("audio subsystem init failure");
69b93cb5 365 vsystem.disable_screen_saver();
e2ca0dbe 366 let acontrol = AudioControl::new(None, None, false, &asystem);
69b93cb5
KS
367 let vcontrol = VideoControl::new(None, 0, 0, 0, 0);
368 Self {
369 sdl_context, asystem, vsystem,
370
371 acontrol, vcontrol,
372
373 play_video: true,
374 play_audio: true,
375 has_video: false,
376 has_audio: false,
377 video_str: 0,
378 audio_str: 0,
549511e9 379 sc_size: ScaleSize::Auto,
69b93cb5 380
37f130a7
KS
381 vthreads: 3,
382 use_mt: true,
383
69b93cb5
KS
384 paused: false,
385 mute: false,
386 volume: 100,
387 end: false,
388
389 tkeep: TimeKeep::new(),
390
391 debug: false,
8686f9f6 392 osd: OSD::new(),
69b93cb5
KS
393
394 #[cfg(feature="debug")]
749a451e 395 logfile: File::create("debug.log").expect("'debug.log' should be available for writing"),
69b93cb5
KS
396 }
397 }
4e72c04a 398 fn seek(&mut self, off: u64, fwd: bool, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<(), ()> {
69b93cb5
KS
399 let cur_time = self.tkeep.get_cur_time();
400 let seektime = if fwd { cur_time + off * 1000 } else {
401 cur_time.saturating_sub(off * 1000) };
402 debug_log!(self; {format!(" seek to {}", seektime)});
403
404 let ret = dmx.seek(NATimePoint::Milliseconds(seektime));
405 if ret.is_err() {
406 println!(" seek error");
4e72c04a 407 return Ok(()); //TODO: not ignore some of seek errors?
69b93cb5
KS
408 }
409
410 self.acontrol.flush();
411 self.vcontrol.flush();
412 disp_queue.flush();
413
414 self.tkeep.reset_ts();
4e72c04a 415 self.prefill(dmx, disp_queue)?;
69b93cb5
KS
416 if !disp_queue.is_empty() {
417 self.tkeep.reset_all(disp_queue.first_ts);
418 } else {
419 let mut iterations = 0;
420 let mut time = self.acontrol.get_time();
421 while time.is_none() {
422 iterations += 1;
423 std::thread::yield_now();
424 if iterations > 1000000 { println!(" still no time set?!"); break; }
425 time = self.acontrol.get_time();
426 }
427 if let Some(time) = time {
428 self.tkeep.reset_all(time);
429 }
430 }
431 if !self.paused {
432 self.acontrol.resume();
433 }
4e72c04a 434 Ok(())
69b93cb5 435 }
4e72c04a 436 fn prefill(&mut self, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<(), ()> {
69b93cb5
KS
437 debug_log!(self; {" prefilling"});
438 while self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN {
439 let mut try_send = self.acontrol.get_queue_size() < FRAME_QUEUE_LEN && (!self.has_video || (!self.vcontrol.is_filled(FRAME_QUEUE_LEN) && !disp_queue.is_full()));
440
441 if !self.vcontrol.try_send_queued() && self.vcontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
442 try_send = false;
443 }
444 if !self.acontrol.try_send_queued() && self.acontrol.get_queue_size() > FRAME_QUEUE_LEN / 2 {
445 try_send = false;
446 }
447 if try_send {
448 match dmx.get_frame() {
449 Err(DemuxerError::EOF) => break,
450 Err(_) => break,
451 Ok(pkt) => {
452 let streamno = pkt.get_stream().get_id();
453 if self.has_video && streamno == self.video_str {
454 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
455 } else if self.has_audio && streamno == self.audio_str {
456 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
457 }
458 }
459 };
460 }
461 self.vcontrol.fill(disp_queue);
462
463 if !try_send {
464 break;
465 }
466 }
467 if self.has_video {
468 while self.vcontrol.get_queue_size() > 0 && !disp_queue.is_full() {
469 self.vcontrol.try_send_queued();
470 self.vcontrol.fill(disp_queue);
471 std::thread::sleep(Duration::from_millis(10));
472 }
4e72c04a 473 self.vcontrol.wait_for_frames()?;
69b93cb5
KS
474 self.vcontrol.fill(disp_queue);
475 }
476 debug_log!(self; {format!(" prefilling done, frames {}-{} audio {}", disp_queue.start, disp_queue.end, self.acontrol.get_fill())});
4e72c04a 477 Ok(())
69b93cb5 478 }
ff58185a
KS
479 fn toggle_pause(&mut self) {
480 self.paused = !self.paused;
481 if self.paused {
482 self.vsystem.enable_screen_saver();
483 self.tkeep.set_ts();
484 } else {
485 self.vsystem.disable_screen_saver();
486 self.tkeep.set_time();
487 }
488 if self.paused {
489 self.acontrol.pause();
490 } else {
491 self.acontrol.resume();
492 }
493 }
4e72c04a 494 fn handle_events(&mut self, event_pump: &mut sdl2::EventPump, canvas: &mut Canvas<Window>, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<bool, ()> {
69b93cb5
KS
495 for event in event_pump.poll_iter() {
496 if let Event::Quit {..} = event {
497 self.end = true;
498 println!();
4e72c04a 499 return Ok(true);
69b93cb5
KS
500 }
501 if let Event::Window {win_event: WindowEvent::Exposed, ..} = event {
502 canvas.clear();
749a451e 503 canvas.copy(disp_queue.get_last_texture(&self.osd), None, None).expect("blitting failure");
69b93cb5
KS
504 canvas.present();
505 }
86da7709
KS
506 if let Event::MouseButtonDown {mouse_btn, ..} = event {
507 match mouse_btn {
508 MouseButton::Right => self.toggle_pause(),
509 MouseButton::Middle => self.osd.toggle(),
510 _ => {},
511 };
ff58185a 512 }
2ef3529c
KS
513 if let Event::MouseWheel {direction: MouseWheelDirection::Normal, x: 0, y, ..} = event {
514 self.seek(10, y > 0, dmx, disp_queue)?;
515 }
8a51f59b 516 if let Event::KeyDown {keycode: Some(keycode), keymod, ..} = event {
69b93cb5 517 match keycode {
8a51f59b
KS
518 Keycode::Escape => {
519 self.end = true;
520 println!();
521 return Ok(true);
522 },
ab76e2d6 523 Keycode::Q if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) => {
69b93cb5
KS
524 self.end = true;
525 println!();
4e72c04a 526 return Ok(true);
69b93cb5 527 },
7133dd98 528 Keycode::Return | Keycode::KpEnter => return Ok(true),
4e72c04a
KS
529 Keycode::Right => { self.seek(10, true, dmx, disp_queue)?; },
530 Keycode::Left => { self.seek(10, false, dmx, disp_queue)?; },
531 Keycode::Up => { self.seek(60, true, dmx, disp_queue)?; },
532 Keycode::Down => { self.seek(60, false, dmx, disp_queue)?; },
533 Keycode::PageUp => { self.seek(600, true, dmx, disp_queue)?; },
534 Keycode::PageDown => { self.seek(600, false, dmx, disp_queue)?; },
ff58185a 535 Keycode::Space => { self.toggle_pause(); },
69b93cb5
KS
536 Keycode::Plus | Keycode::KpPlus => {
537 self.volume = (self.volume + 10).min(MAX_VOLUME);
538 if !self.mute {
539 self.acontrol.set_volume(self.volume);
540 }
541 },
542 Keycode::Minus | Keycode::KpMinus => {
543 self.volume = self.volume.saturating_sub(10);
544 if !self.mute {
545 self.acontrol.set_volume(self.volume);
546 }
547 },
548 Keycode::D => {
549 self.debug = !self.debug;
550 },
551 Keycode::M => {
552 self.mute = !self.mute;
553 if self.mute {
554 self.acontrol.set_volume(0);
555 } else {
556 self.acontrol.set_volume(self.volume);
557 }
558 },
559 Keycode::H => {
560 self.vcontrol.try_send_video(PktSendEvent::HurryUp);
561 },
8686f9f6 562 Keycode::O => {
5a268da1
KS
563 if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) {
564 self.osd.toggle_perm();
565 } else {
566 self.osd.toggle();
567 }
8686f9f6 568 },
69b93cb5
KS
569 _ => {},
570 };
571 if !self.paused {
572 print!("{:60}\r", ' ');
573 std::io::stdout().flush().unwrap();
574 }
575 }
576 }
4e72c04a 577 Ok(false)
69b93cb5 578 }
6cdca687 579 fn play(&mut self, mut window: Window, name: &str, start_time: NATimePoint) -> Window {
69b93cb5
KS
580 debug_log!(self; {format!("Playing {}", name)});
581
582 // prepare data source
583 let path = Path::new(name);
749a451e 584 let mut file = if let Ok(handle) = File::open(path) {
0d3a25c7
KS
585 if let Ok(meta) = handle.metadata() {
586 if meta.is_dir() {
587 return window;
588 }
589 }
749a451e
KS
590 handle
591 } else {
592 println!("failed to open {}", name);
6cdca687 593 return window;
749a451e 594 };
69b93cb5
KS
595 let mut fr = FileReader::new_read(&mut file);
596 let mut br = ByteReader::new(&mut fr);
597 let res = detect::detect_format(name, &mut br);
598 if res.is_none() {
599 println!("cannot detect format for {}", name);
6cdca687 600 return window;
69b93cb5
KS
601 }
602 let (dmx_name, _score) = res.unwrap();
603 debug_log!(self; {format!(" found demuxer {} with score {:?}", dmx_name, _score)});
604 println!("trying demuxer {} on {}", dmx_name, name);
605
606 let mut dmx_reg = RegisteredDemuxers::new();
607 nihav_register_all_demuxers(&mut dmx_reg);
608 let mut dec_reg = RegisteredDecoders::new();
609 nihav_register_all_decoders(&mut dec_reg);
37f130a7
KS
610 let mut mtdec_reg = RegisteredMTDecoders::new();
611 if self.use_mt {
612 nihav_register_all_mt_decoders(&mut mtdec_reg);
613 }
69b93cb5
KS
614
615 let ret = dmx_reg.find_demuxer(dmx_name);
616 if ret.is_none() {
617 println!("error finding {} demuxer", dmx_name);
6cdca687 618 return window;
69b93cb5 619 }
bbd69555 620 let dmx_fact = ret.unwrap();
749a451e 621 br.seek(SeekFrom::Start(0)).expect("should be able to seek to the start");
69b93cb5
KS
622 let ret = create_demuxer(dmx_fact, &mut br);
623 if ret.is_err() {
624 println!("error creating demuxer");
6cdca687 625 return window;
69b93cb5
KS
626 }
627 let mut dmx = ret.unwrap();
628 if start_time != NATimePoint::None {
629 debug_log!(self; {format!(" start seek to {}", start_time)});
630 if dmx.seek(start_time).is_err() {
631 println!("initial seek failed");
632 }
633 }
634
635 let mut width = 640;
636 let mut height = 480;
637 let mut tb_num = 0;
638 let mut tb_den = 0;
639 let mut ainfo: Option<NAAudioInfo> = None;
e2ca0dbe 640 let mut sbr_hack = false;
69b93cb5
KS
641
642 let mut video_dec: Option<DecoderStuff> = None;
643 let mut audio_dec: Option<DecoderStuff> = None;
644
645 let duration = dmx.get_duration();
646 if duration != 0 {
647 println!(" total duration {}", format_time(duration));
648 }
649 self.has_video = false;
650 self.has_audio = false;
2425989e
KS
651 self.osd.reset();
652 self.osd.set_duration(duration);
69b93cb5
KS
653 for i in 0..dmx.get_num_streams() {
654 let s = dmx.get_stream(i).unwrap();
655 let info = s.get_info();
656 let decfunc = dec_reg.find_decoder(info.get_name());
37f130a7 657 let decfunc_mt = mtdec_reg.find_decoder(info.get_name());
69b93cb5
KS
658 println!("stream {} - {} {}", i, s, info.get_name());
659 debug_log!(self; {format!(" stream {} - {} {}", i, s, info.get_name())});
660 let str_id = s.get_id();
661 if info.is_video() {
662 if video_dec.is_none() && self.play_video {
37f130a7
KS
663 if let Some(decfunc) = decfunc_mt {
664 let mut dec = (decfunc)();
665 let mut dsupp = Box::new(NADecoderSupport::new());
666 let props = info.get_properties().get_video_info().unwrap();
667 if props.get_width() != 0 {
668 width = props.get_width();
669 height = props.get_height();
670 }
671 if dec.init(&mut dsupp, info.clone(), self.vthreads).is_ok() {
672 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::VideoMT(dec, MTFrameReorderer::new()) });
673 self.video_str = str_id;
674 let (tbn, tbd) = s.get_timebase();
675 tb_num = tbn;
676 tb_den = tbd;
677 self.has_video = true;
678 continue;
679 } else {
680 println!("failed to create multi-threaded decoder, falling back");
681 }
682 }
69b93cb5
KS
683 if let Some(decfunc) = decfunc {
684 let mut dec = (decfunc)();
685 let mut dsupp = Box::new(NADecoderSupport::new());
686 let props = info.get_properties().get_video_info().unwrap();
687 if props.get_width() != 0 {
688 width = props.get_width();
689 height = props.get_height();
690 }
691 let desc = get_codec_description(info.get_name());
692 let (reorder_depth, reord) = if desc.is_none() || (desc.unwrap().caps & CODEC_CAP_COMPLEX_REORDER) == 0 {
693 let reord: Box<dyn FrameReorderer + Send> = Box::new(IPBReorderer::new());
694 (3, reord)
695 } else {
696 let reord: Box<dyn FrameReorderer + Send> = Box::new(ComplexReorderer::new());
697 (16, reord)
698 };
699 dsupp.pool_u8 = NAVideoBufferPool::new(reorder_depth);
700 dsupp.pool_u16 = NAVideoBufferPool::new(reorder_depth);
701 dsupp.pool_u32 = NAVideoBufferPool::new(reorder_depth);
749a451e
KS
702 if dec.init(&mut dsupp, info).is_err() {
703 println!("failed to initialise video decoder");
6cdca687 704 return window;
749a451e 705 }
37f130a7 706 video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Video(dec, reord) });
69b93cb5
KS
707 self.video_str = str_id;
708 let (tbn, tbd) = s.get_timebase();
709 tb_num = tbn;
710 tb_den = tbd;
711 self.has_video = true;
712 } else {
713 println!("no video decoder for {} found!", info.get_name());
714 }
715 }
716 } else if info.is_audio() {
717 if audio_dec.is_none() && self.play_audio {
718 if let Some(decfunc) = decfunc {
719 let mut dec = (decfunc)();
720 let mut dsupp = Box::new(NADecoderSupport::new());
721 ainfo = info.get_properties().get_audio_info();
e2ca0dbe
KS
722 if let (true, Some(ref ai)) = (info.get_name() == "aac", ainfo) {
723 if ai.sample_rate < 32000 {
724 sbr_hack = true;
725 }
726 }
749a451e
KS
727 if dec.init(&mut dsupp, info).is_err() {
728 println!("failed to initialise audio decoder");
6cdca687 729 return window;
749a451e 730 }
37f130a7 731 audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) });
69b93cb5
KS
732 self.audio_str = str_id;
733 self.has_audio = true;
734 } else {
735 println!("no audio decoder for {} found!", info.get_name());
736 }
737 }
738 } else {
739 println!("decoder {} not found", info.get_name());
740 }
741 }
742 if !self.has_video && !self.has_audio {
743 println!("No playable streams found.");
6cdca687 744 return window;
69b93cb5
KS
745 }
746
549511e9
KS
747 match self.sc_size {
748 ScaleSize::Auto => {
749 while (width <= 384) && (height <= 288) {
750 width <<= 1;
751 height <<= 1;
752 }
753 },
754 ScaleSize::Times(factor) => {
755 let nw = ((width as f32) * factor).ceil() as usize;
756 let nh = ((height as f32) * factor).ceil() as usize;
757 if nw > 0 && nh > 0 {
758 width = nw;
759 height = nh;
760 }
761 },
762 ScaleSize::Fixed(w, h) => {
763 width = w;
764 height = h;
765 },
766 };
69b93cb5
KS
767
768 // prepare playback structure
769 let mut new_vcontrol = VideoControl::new(video_dec, width, height, tb_num, tb_den);
770 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
771
e2ca0dbe 772 let mut new_acontrol = AudioControl::new(audio_dec, ainfo, sbr_hack, &self.asystem);
69b93cb5
KS
773 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
774
775 if self.mute {
776 self.acontrol.set_volume(0);
777 } else {
778 self.acontrol.set_volume(self.volume);
779 }
780
781 let fname = path.file_name();
782 let wname = if let Some(fname) = fname {
75da2ee6
KS
783 // workaround for libSDL2 workaround for non-UTF8 windowing systems
784 // see https://github.com/libsdl-org/SDL/pull/4290 for detais
c66ce565 785 let nname = fname.to_str().expect("should be able to set window title").replace('\u{2013}', "-").replace('\u{2014}', "-");
75da2ee6 786 "NihAV player - ".to_owned() + &nname
69b93cb5
KS
787 } else {
788 "NihAV player".to_owned()
789 };
6cdca687 790 window.set_title(&wname).expect("set window title");
034bfb62
KS
791 if window.size() != (width as u32, height as u32) {
792 window.set_size(width as u32, height as u32).expect("resize window");
793 }
6cdca687 794 window.show();
749a451e 795 let mut canvas = window.into_canvas().build().expect("should be able to build canvas");
69b93cb5
KS
796 let texture_creator = canvas.texture_creator();
797 let mut disp_q = DispQueue::new(&texture_creator, width, height, if self.has_video { FRAME_QUEUE_LEN } else { 0 });
798 if !self.has_video {
799 canvas.clear();
749a451e 800 canvas.copy(disp_q.get_last_texture(&self.osd), None, None).expect("blit failure");
69b93cb5
KS
801 canvas.present();
802 }
803
804 self.has_audio = self.acontrol.has_audio();
805 if !self.has_video && !self.has_audio {
806 println!("No playable streams.");
6cdca687 807 return canvas.into_window();
69b93cb5
KS
808 }
809
810 // play
4e72c04a
KS
811 if self.prefill(&mut dmx, &mut disp_q).is_err() {
812 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
813 new_vcontrol.finish();
814 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
815 new_acontrol.finish();
6cdca687 816 return canvas.into_window();
4e72c04a 817 }
0b064d41 818 self.tkeep.reset_all(if !disp_q.is_empty() { disp_q.first_ts } else { 0 });
69b93cb5
KS
819 if !self.paused {
820 self.acontrol.resume();
821 }
749a451e 822 let mut event_pump = self.sdl_context.event_pump().expect("should be able to create event pump");
69b93cb5
KS
823 let mut last_disp = Instant::now();
824 let mut has_data = true;
825 'main: loop {
4e72c04a
KS
826 let ret = self.handle_events(&mut event_pump, &mut canvas, &mut dmx, &mut disp_q);
827 if matches!(ret, Ok(true) | Err(_)) {
69b93cb5
KS
828 println!();
829 break 'main;
830 }
831 if !self.paused {
832 let mut try_send = self.acontrol.get_queue_size() < FRAME_QUEUE_LEN && self.vcontrol.get_queue_size() < FRAME_QUEUE_LEN;
833 if !self.vcontrol.try_send_queued() && self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
834 try_send = false;
835 }
836 if !self.acontrol.try_send_queued() {
837 try_send = false;
838 }
839 while has_data && try_send {
840 match dmx.get_frame() {
841 Err(DemuxerError::EOF) => {
842 self.vcontrol.try_send_video(PktSendEvent::End);
843 self.acontrol.try_send_audio(PktSendEvent::End);
844 has_data = false;
845 },
6a5b7102
KS
846 Err(err) => {
847 println!("demuxer error {:?}", err);
848 if err == DemuxerError::IOError {
849 self.vcontrol.try_send_video(PktSendEvent::End);
850 self.acontrol.try_send_audio(PktSendEvent::End);
851 has_data = false;
852 }
853 },
69b93cb5
KS
854 Ok(pkt) => {
855 let streamno = pkt.get_stream().get_id();
856 if self.has_video && streamno == self.video_str {
857 debug_log!(self; {" sending video packet"});
858 self.vcontrol.try_send_video(PktSendEvent::Packet(pkt));
859 if self.vcontrol.is_filled(FRAME_QUEUE_LEN) {
860 try_send = false;
861 }
862 } else if self.has_audio && streamno == self.audio_str {
863 debug_log!(self; {" sending audio packet"});
864 self.acontrol.try_send_audio(PktSendEvent::Packet(pkt));
865 if self.acontrol.get_queue_size() >= FRAME_QUEUE_LEN {
866 try_send = false;
867 }
868 }
869 }
870 };
871 }
872 self.vcontrol.fill(&mut disp_q);
873 let mut sleep_time = 25;
874 debug_log!(self; {format!(" time {}", self.tkeep.get_cur_time())});
875 if self.has_video {
876 debug_log!(self; {format!(" disp queue {}-{}, {}-{} vqueue fill {}", disp_q.first_ts, disp_q.last_ts, disp_q.start, disp_q.end, self.vcontrol.get_queue_size())});
8686f9f6 877 let ret = try_display(&mut disp_q, &mut canvas, &mut self.osd, &self.tkeep);
69b93cb5
KS
878 if let Some(next_time) = ret {
879 sleep_time = sleep_time.min(next_time);
880 }
881 }
882 if self.has_audio {
883 let time_left = self.acontrol.get_time_left();
884 debug_log!(self; {format!(" audio left {}", time_left)});
885 sleep_time = sleep_time.min(time_left);
886 }
887 debug_log!(self; {format!(" sleep {}ms", sleep_time)});
888 if last_disp.elapsed().as_millis() >= 10 {
889 let c_time = self.tkeep.get_cur_time();
890
891 if !self.debug {
892 print!(" {} {}% \r", format_time(c_time), self.acontrol.get_volume());
893 } else {
894 print!(" {} {} {}% {:3} {:6}\r", format_time(c_time), if self.vcontrol.is_yuv() { 'Y' } else { 'R' }, self.acontrol.get_volume(), (disp_q.end + disp_q.len - disp_q.start) % disp_q.len, self.acontrol.get_fill());
895 }
896 std::io::stdout().flush().unwrap();
897 last_disp = Instant::now();
898 }
899 let mut end = true;
900 if self.has_video && !self.vcontrol.is_video_end() {
901 end = false;
902 }
903 if self.has_audio && !self.acontrol.is_audio_end() {
904 end = false;
905 }
906 if end {
907 break;
908 }
909 thread::sleep(Duration::from_millis(sleep_time));
910 } else {
911 thread::sleep(Duration::from_millis(20));
912 }
913 }
914 println!();
915 std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
916 new_vcontrol.finish();
917 std::mem::swap(&mut self.acontrol, &mut new_acontrol);
918 new_acontrol.finish();
6cdca687 919 canvas.into_window()
69b93cb5
KS
920 }
921}
922
923fn main() {
924 let args: Vec<String> = env::args().collect();
925
926 if args.len() == 1 {
927 println!("usage: nihav-player file1 file2 ...");
928 return;
929 }
930
931 let mut player = Player::new();
6cdca687
KS
932 let mut builder = player.vsystem.window("NihAV Player", 640, 480);
933 let mut window = builder.position_centered().hidden().build().expect("should be able to centre window");
69b93cb5
KS
934
935 let mut aiter = args.iter().skip(1);
936 let mut seek_time = NATimePoint::None;
937 while let Some(arg) = aiter.next() {
938 match arg.as_str() {
939 "-an" => { player.play_audio = false; },
940 "-ae" => { player.play_audio = true; },
941 "-vn" => { player.play_video = false; },
942 "-ve" => { player.play_video = true; },
b452b665 943 "-seek" | "-start" => {
69b93cb5
KS
944 if let Some(arg) = aiter.next() {
945 if let Ok(time) = arg.parse::<NATimePoint>() {
946 seek_time = time;
947 } else {
948 println!("wrong seek time");
949 seek_time = NATimePoint::None;
950 }
951 }
952 },
953 "-vol" => {
954 if let Some(arg) = aiter.next() {
955 if let Ok(vol) = arg.parse::<usize>() {
956 player.volume = vol.min(MAX_VOLUME);
957 } else {
958 println!("wrong volume");
959 }
960 }
961 },
962 "-debug" => {
963 player.debug = true;
964 },
965 "-nodebug" => {
966 player.debug = false;
967 },
37f130a7
KS
968 "-mt" => {
969 player.use_mt = true;
970 },
971 "-nomt" => {
972 player.use_mt = false;
973 },
974 "-threads" => {
975 if let Some(arg) = aiter.next() {
976 if let Ok(val) = arg.parse::<usize>() {
977 player.vthreads = val.max(1);
978 } else {
979 println!("wrong number of threads");
980 }
981 }
982 },
549511e9
KS
983 "-scale" => {
984 if let Some(arg) = aiter.next() {
985 if let Ok(ssize) = arg.parse::<ScaleSize>() {
986 player.sc_size = ssize;
987 } else {
988 println!("invalid scale size");
989 }
990 }
991 },
69b93cb5 992 _ => {
6cdca687 993 window = player.play(window, arg, seek_time);
69b93cb5
KS
994 if player.end { break; }
995 seek_time = NATimePoint::None;
996 },
997 };
998 }
999}