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