]> git.nihav.org Git - nihav-player.git/blobdiff - videoplayer/src/main.rs
videoplayer: allow diplaying time indefinitely long
[nihav-player.git] / videoplayer / src / main.rs
index fa2c4d5bb143bdd9d1b788c33a3f0e0ade6dcdcf..56c311237ff6cf06aeb0ec6171bb4907d9f5b81a 100644 (file)
@@ -12,8 +12,8 @@ use std::thread;
 use std::sync::atomic::{AtomicU8, Ordering};
 
 use sdl2::event::{Event, WindowEvent};
-use sdl2::keyboard::Keycode;
-use sdl2::mouse::MouseButton;
+use sdl2::keyboard::{Keycode, Mod};
+use sdl2::mouse::{MouseButton, MouseWheelDirection};
 use sdl2::render::{Canvas, Texture, TextureCreator};
 use sdl2::pixels::PixelFormatEnum;
 use sdl2::video::{Window, WindowContext};
@@ -35,8 +35,9 @@ mod osd;
 use osd::*;
 
 #[repr(u8)]
-#[derive(Clone,Copy,Debug,PartialEq)]
+#[derive(Clone,Copy,Debug,PartialEq,Default)]
 enum DecodingState {
+    #[default]
     Normal,
     Waiting,
     Flush,
@@ -45,10 +46,6 @@ enum DecodingState {
     End,
 }
 
-impl Default for DecodingState {
-    fn default() -> Self { DecodingState::Normal }
-}
-
 impl From<u8> for DecodingState {
     fn from(val: u8) -> Self {
         match val {
@@ -190,14 +187,14 @@ impl<'a> DispQueue<'a> {
     fn new(texture_creator: &'a TextureCreator<WindowContext>, width: usize, height: usize, len: usize) -> Self {
         let mut pool = Vec::with_capacity(len);
         for _ in 0..len + 1 {
-            let rgb_tex = texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, width as u32, height as u32).unwrap();
-            let yuv_tex = texture_creator.create_texture_streaming(PixelFormatEnum::IYUV, ((width + 1) & !1) as u32, ((height + 1) & !1) as u32).unwrap();
+            let rgb_tex = texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, width as u32, height as u32).expect("failed to create RGB texture");
+            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");
             pool.push(DispFrame{ ts: 0, is_yuv: false, valid: false, rgb_tex, yuv_tex });
         }
         pool[len].is_yuv = false;
         pool[len].rgb_tex.with_lock(None, |buffer: &mut [u8], _pitch: usize| {
                 for el in buffer.iter_mut() { *el = 0; }
-            }).unwrap();
+            }).expect("RGB texture could not be locked");
 
         Self { pool, first_ts: 0, last_ts: 0, start: 0, end: 0, len, width, height }
     }
@@ -217,14 +214,14 @@ impl<'a> DispQueue<'a> {
             if osd.is_active() {
                 self.pool[self.len].yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                         osd.draw_yuv(buffer, pitch);
-                    }).unwrap();
+                    }).expect("YUV texture locking failure");
             }
             &self.pool[self.len].yuv_tex
         } else {
             if osd.is_active() {
                 self.pool[self.len].rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                         osd.draw_rgb(buffer, pitch);
-                    }).unwrap();
+                    }).expect("RGB texture locking failure");
             }
             &self.pool[self.len].rgb_tex
         }
@@ -266,19 +263,19 @@ fn try_display(disp_queue: &mut DispQueue, canvas: &mut Canvas<Window>, osd: &mu
                     if osd.is_active() {
                         frm.yuv_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                                 osd.draw_yuv(buffer, pitch);
-                            }).unwrap();
+                            }).expect("YUV texture locking failure");
                     }
                     &frm.yuv_tex
                 } else {
                     if osd.is_active() {
                         frm.rgb_tex.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                                 osd.draw_rgb(buffer, pitch);
-                            }).unwrap();
+                            }).expect("RGB texture locking failure");
                     }
                     &frm.rgb_tex
                 };
             canvas.clear();
-            canvas.copy(texture, None, None).unwrap();
+            canvas.copy(texture, None, None).expect("canvas blit failure");
             canvas.present();
 
             disp_queue.move_start();
@@ -296,8 +293,6 @@ struct Player {
     sdl_context:    sdl2::Sdl,
     vsystem:        sdl2::VideoSubsystem,
     asystem:        sdl2::AudioSubsystem,
-    xpos:           Option<i32>,
-    ypos:           Option<i32>,
 
     acontrol:       AudioControl,
     vcontrol:       VideoControl,
@@ -328,16 +323,14 @@ struct Player {
 
 impl Player {
     fn new() -> Self {
-        let sdl_context = sdl2::init().unwrap();
-        let vsystem = sdl_context.video().unwrap();
-        let asystem = sdl_context.audio().unwrap();
+        let sdl_context = sdl2::init().expect("SDL2 init failure");
+        let vsystem = sdl_context.video().expect("video subsystem init failure");
+        let asystem = sdl_context.audio().expect("audio subsystem init failure");
         vsystem.disable_screen_saver();
-        let acontrol = AudioControl::new(None, None, &asystem);
+        let acontrol = AudioControl::new(None, None, false, &asystem);
         let vcontrol = VideoControl::new(None, 0, 0, 0, 0);
         Self {
             sdl_context, asystem, vsystem,
-            xpos:           None,
-            ypos:           None,
 
             acontrol, vcontrol,
 
@@ -362,7 +355,7 @@ impl Player {
             osd:            OSD::new(),
 
             #[cfg(feature="debug")]
-            logfile:        File::create("debug.log").unwrap(),
+            logfile:        File::create("debug.log").expect("'debug.log' should be available for writing"),
         }
     }
     fn seek(&mut self, off: u64, fwd: bool, dmx: &mut Demuxer, disp_queue: &mut DispQueue) -> Result<(), ()> {
@@ -470,20 +463,32 @@ impl Player {
             }
             if let Event::Window {win_event: WindowEvent::Exposed, ..} = event {
                 canvas.clear();
-                canvas.copy(disp_queue.get_last_texture(&self.osd), None, None).unwrap();
+                canvas.copy(disp_queue.get_last_texture(&self.osd), None, None).expect("blitting failure");
                 canvas.present();
             }
-            if let Event::MouseButtonDown {mouse_btn: MouseButton::Right, ..} = event {
-                self.toggle_pause();
+            if let Event::MouseButtonDown {mouse_btn, ..} = event {
+                match mouse_btn {
+                    MouseButton::Right => self.toggle_pause(),
+                    MouseButton::Middle => self.osd.toggle(),
+                    _ => {},
+                };
+            }
+            if let Event::MouseWheel {direction: MouseWheelDirection::Normal, x: 0, y, ..} = event {
+                self.seek(10, y > 0, dmx, disp_queue)?;
             }
-            if let Event::KeyDown {keycode: Some(keycode), ..} = event {
+            if let Event::KeyDown {keycode: Some(keycode), keymod, ..} = event {
                 match keycode {
-                    Keycode::Escape | Keycode::Q => {
+                    Keycode::Escape => {
                         self.end = true;
                         println!();
                         return Ok(true);
                     },
-                    Keycode::Return => return Ok(true),
+                    Keycode::Q if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) => {
+                        self.end = true;
+                        println!();
+                        return Ok(true);
+                    },
+                    Keycode::Return | Keycode::KpEnter => return Ok(true),
                     Keycode::Right      => { self.seek(10, true,  dmx, disp_queue)?; },
                     Keycode::Left       => { self.seek(10, false, dmx, disp_queue)?; },
                     Keycode::Up         => { self.seek(60, true,  dmx, disp_queue)?; },
@@ -518,7 +523,11 @@ impl Player {
                         self.vcontrol.try_send_video(PktSendEvent::HurryUp);
                     },
                     Keycode::O => {
-                        self.osd.toggle();
+                        if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) {
+                            self.osd.toggle_perm();
+                        } else {
+                            self.osd.toggle();
+                        }
                     },
                     _ => {},
                 };
@@ -530,19 +539,23 @@ impl Player {
         }
         Ok(false)
     }
-    fn play(&mut self, name: &str, start_time: NATimePoint) {
+    fn play(&mut self, mut window: Window, name: &str, start_time: NATimePoint) -> Window {
         debug_log!(self; {format!("Playing {}", name)});
 
         // prepare data source
         let path = Path::new(name);
-        let mut file = File::open(path).unwrap();
-        let dmx_fact;
+        let mut file = if let Ok(handle) = File::open(path) {
+                handle
+            } else {
+                println!("failed to open {}", name);
+                return window;
+            };
         let mut fr = FileReader::new_read(&mut file);
         let mut br = ByteReader::new(&mut fr);
         let res = detect::detect_format(name, &mut br);
         if res.is_none() {
             println!("cannot detect format for {}", name);
-            return;
+            return window;
         }
         let (dmx_name, _score) = res.unwrap();
         debug_log!(self; {format!(" found demuxer {} with score {:?}", dmx_name, _score)});
@@ -560,14 +573,14 @@ impl Player {
         let ret = dmx_reg.find_demuxer(dmx_name);
         if ret.is_none() {
             println!("error finding {} demuxer", dmx_name);
-            return;
+            return window;
         }
-        dmx_fact = ret.unwrap();
-        br.seek(SeekFrom::Start(0)).unwrap();
+        let dmx_fact = ret.unwrap();
+        br.seek(SeekFrom::Start(0)).expect("should be able to seek to the start");
         let ret = create_demuxer(dmx_fact, &mut br);
         if ret.is_err() {
             println!("error creating demuxer");
-            return;
+            return window;
         }
         let mut dmx = ret.unwrap();
         if start_time != NATimePoint::None {
@@ -582,6 +595,7 @@ impl Player {
         let mut tb_num = 0;
         let mut tb_den = 0;
         let mut ainfo: Option<NAAudioInfo> = None;
+        let mut sbr_hack = false;
 
         let mut video_dec: Option<DecoderStuff> = None;
         let mut audio_dec: Option<DecoderStuff> = None;
@@ -643,7 +657,10 @@ impl Player {
                         dsupp.pool_u8 = NAVideoBufferPool::new(reorder_depth);
                         dsupp.pool_u16 = NAVideoBufferPool::new(reorder_depth);
                         dsupp.pool_u32 = NAVideoBufferPool::new(reorder_depth);
-                        dec.init(&mut dsupp, info).unwrap();
+                        if dec.init(&mut dsupp, info).is_err() {
+                            println!("failed to initialise video decoder");
+                            return window;
+                        }
                         video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Video(dec, reord) });
                         self.video_str = str_id;
                         let (tbn, tbd) = s.get_timebase();
@@ -660,7 +677,15 @@ impl Player {
                         let mut dec = (decfunc)();
                         let mut dsupp = Box::new(NADecoderSupport::new());
                         ainfo = info.get_properties().get_audio_info();
-                        dec.init(&mut dsupp, info).unwrap();
+                        if let (true, Some(ref ai)) = (info.get_name() == "aac", ainfo) {
+                            if ai.sample_rate < 32000 {
+                                sbr_hack = true;
+                            }
+                        }
+                        if dec.init(&mut dsupp, info).is_err() {
+                            println!("failed to initialise audio decoder");
+                            return window;
+                        }
                         audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) });
                         self.audio_str = str_id;
                         self.has_audio = true;
@@ -674,7 +699,7 @@ impl Player {
         }
         if !self.has_video && !self.has_audio {
             println!("No playable streams found.");
-            return;
+            return window;
         }
 
         while (width <= 384) && (height <= 288) {
@@ -686,7 +711,7 @@ impl Player {
         let mut new_vcontrol = VideoControl::new(video_dec, width, height, tb_num, tb_den);
         std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
 
-        let mut new_acontrol = AudioControl::new(audio_dec, ainfo, &self.asystem);
+        let mut new_acontrol = AudioControl::new(audio_dec, ainfo, sbr_hack, &self.asystem);
         std::mem::swap(&mut self.acontrol, &mut new_acontrol);
 
         if self.mute {
@@ -697,29 +722,31 @@ impl Player {
 
         let fname = path.file_name();
         let wname = if let Some(fname) = fname {
-                "NihAV player - ".to_owned() + fname.to_str().unwrap()
+                // workaround for libSDL2 workaround for non-UTF8 windowing systems
+                // see https://github.com/libsdl-org/SDL/pull/4290 for detais
+                let nname = fname.to_str().expect("should be able to set window title").replace('\u{2013}', "-").replace('\u{2014}', "-");
+                "NihAV player - ".to_owned() + &nname
             } else {
                 "NihAV player".to_owned()
             };
-        let mut builder = self.vsystem.window(&wname, width as u32, height as u32);
-        let window = if let (Some(xpos), Some(ypos)) = (self.xpos, self.ypos) {
-                builder.position(xpos, ypos).build().unwrap()
-            } else {
-                builder.position_centered().build().unwrap()
-            };
-        let mut canvas = window.into_canvas().build().unwrap();
+        window.set_title(&wname).expect("set window title");
+        if window.size() != (width as u32, height as u32) {
+            window.set_size(width as u32, height as u32).expect("resize window");
+        }
+        window.show();
+        let mut canvas = window.into_canvas().build().expect("should be able to build canvas");
         let texture_creator = canvas.texture_creator();
         let mut disp_q = DispQueue::new(&texture_creator, width, height, if self.has_video { FRAME_QUEUE_LEN } else { 0 });
         if !self.has_video {
             canvas.clear();
-            canvas.copy(disp_q.get_last_texture(&self.osd), None, None).unwrap();
+            canvas.copy(disp_q.get_last_texture(&self.osd), None, None).expect("blit failure");
             canvas.present();
         }
 
         self.has_audio = self.acontrol.has_audio();
         if !self.has_video && !self.has_audio {
             println!("No playable streams.");
-            return;
+            return canvas.into_window();
         }
 
         // play
@@ -728,13 +755,13 @@ impl Player {
             new_vcontrol.finish();
             std::mem::swap(&mut self.acontrol, &mut new_acontrol);
             new_acontrol.finish();
-            return;
+            return canvas.into_window();
         }
         self.tkeep.reset_all(if !disp_q.is_empty() { disp_q.first_ts } else { 0 });
         if !self.paused {
             self.acontrol.resume();
         }
-        let mut event_pump = self.sdl_context.event_pump().unwrap();
+        let mut event_pump = self.sdl_context.event_pump().expect("should be able to create event pump");
         let mut last_disp = Instant::now();
         let mut has_data = true;
         'main: loop {
@@ -826,14 +853,12 @@ impl Player {
                 thread::sleep(Duration::from_millis(20));
             }
         }
-        let (xpos, ypos) = canvas.into_window().position();
-        self.xpos = Some(xpos);
-        self.ypos = Some(ypos);
         println!();
         std::mem::swap(&mut self.vcontrol, &mut new_vcontrol);
         new_vcontrol.finish();
         std::mem::swap(&mut self.acontrol, &mut new_acontrol);
         new_acontrol.finish();
+        canvas.into_window()
     }
 }
 
@@ -846,6 +871,8 @@ fn main() {
     }
 
     let mut player = Player::new();
+    let mut builder = player.vsystem.window("NihAV Player", 640, 480);
+    let mut window = builder.position_centered().hidden().build().expect("should be able to centre window");
 
     let mut aiter = args.iter().skip(1);
     let mut seek_time = NATimePoint::None;
@@ -855,7 +882,7 @@ fn main() {
             "-ae" => { player.play_audio = true; },
             "-vn" => { player.play_video = false; },
             "-ve" => { player.play_video = true; },
-            "-seek" => {
+            "-seek" | "-start" => {
                 if let Some(arg) = aiter.next() {
                     if let Ok(time) = arg.parse::<NATimePoint>() {
                         seek_time = time;
@@ -896,7 +923,7 @@ fn main() {
                 }
             },
             _ => {
-                player.play(arg, seek_time);
+                window = player.play(window, arg, seek_time);
                 if player.end { break; }
                 seek_time = NATimePoint::None;
             },