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