]> git.nihav.org Git - nihav-player.git/blobdiff - videoplayer/src/main.rs
videoplayer: introduce refresh command
[nihav-player.git] / videoplayer / src / main.rs
index 89deb7c910e748d6542ed7470fd3374e9ed5e4c3..0ee3c1472033a2000b0f7dcf7d2630dfecb56f9b 100644 (file)
@@ -27,6 +27,9 @@ use nihav_core::demuxers::*;
 use nihav_registry::register::*;
 use nihav_allstuff::*;
 
+#[cfg(feature="hwaccel")]
+use hwdec_vaapi::*;
+
 mod audiodec;
 use audiodec::*;
 mod videodec;
@@ -92,6 +95,41 @@ macro_rules! debug_log {
     ($log: expr; $blk: block) => {};
 }
 
+enum ScaleSize {
+    Auto,
+    Times(f32),
+    Fixed(usize, usize)
+}
+
+impl FromStr for ScaleSize {
+    type Err = ();
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if matches!(s, "" | "auto") {
+            Ok(ScaleSize::Auto)
+        } else if s.ends_with('x') || s.ends_with('X') {
+            let factor = s[..s.len() - 1].parse::<f32>().map_err(|_| ())?;
+            if factor > 0.0 {
+                Ok(ScaleSize::Times(factor))
+            } else {
+                Err(())
+            }
+        } else if s.contains('x') | s.contains('X') {
+            let mut dims = if s.contains('x') { s.split('x') } else { s.split('X') };
+            let w = dims.next().unwrap();
+            let h = dims.next().unwrap();
+            let width  = w.parse::<usize>().map_err(|_| ())?;
+            let height = h.parse::<usize>().map_err(|_| ())?;
+            if width > 0 && height > 0 {
+                Ok(ScaleSize::Fixed(width, height))
+            } else {
+                Err(())
+            }
+        } else {
+            Err(())
+        }
+    }
+}
+
 pub enum PktSendEvent {
     Packet(NAPacket),
     GetFrames,
@@ -105,6 +143,8 @@ pub enum DecoderType {
     Audio(Box<dyn NADecoder + Send>),
     Video(Box<dyn NADecoder + Send>, Box<dyn FrameReorderer + Send>),
     VideoMT(Box<dyn NADecoderMT + Send>, MTFrameReorderer),
+    #[cfg(feature="hwaccel")]
+    VideoHW(Box<dyn HWDecoder + Send>),
 }
 
 pub struct DecoderStuff {
@@ -293,8 +333,6 @@ struct Player {
     sdl_context:    sdl2::Sdl,
     vsystem:        sdl2::VideoSubsystem,
     asystem:        sdl2::AudioSubsystem,
-    xpos:           Option<i32>,
-    ypos:           Option<i32>,
 
     acontrol:       AudioControl,
     vcontrol:       VideoControl,
@@ -305,9 +343,12 @@ struct Player {
     has_audio:      bool,
     video_str:      u32,
     audio_str:      u32,
+    sc_size:        ScaleSize,
 
     vthreads:       usize,
     use_mt:         bool,
+    #[cfg(feature="hwaccel")]
+    use_hwaccel:    bool,
 
     paused:         bool,
     mute:           bool,
@@ -333,8 +374,6 @@ impl Player {
         let vcontrol = VideoControl::new(None, 0, 0, 0, 0);
         Self {
             sdl_context, asystem, vsystem,
-            xpos:           None,
-            ypos:           None,
 
             acontrol, vcontrol,
 
@@ -344,9 +383,12 @@ impl Player {
             has_audio:      false,
             video_str:      0,
             audio_str:      0,
+            sc_size:        ScaleSize::Auto,
 
             vthreads:       3,
             use_mt:         true,
+            #[cfg(feature="hwaccel")]
+            use_hwaccel:    true,
 
             paused:         false,
             mute:           false,
@@ -492,7 +534,8 @@ impl Player {
                         println!();
                         return Ok(true);
                     },
-                    Keycode::Return => return Ok(true),
+                    Keycode::Return | Keycode::KpEnter => return Ok(true),
+                    Keycode::R          => { self.seek(0, true,  dmx, disp_queue)?; },
                     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)?; },
@@ -527,7 +570,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();
+                        }
                     },
                     _ => {},
                 };
@@ -539,23 +586,28 @@ 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 = if let Ok(handle) = File::open(path) {
+                if let Ok(meta) = handle.metadata() {
+                    if meta.is_dir() {
+                        return window;
+                    }
+                }
                 handle
             } else {
                 println!("failed to open {}", name);
-                return;
+                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)});
@@ -573,14 +625,14 @@ impl Player {
         let ret = dmx_reg.find_demuxer(dmx_name);
         if ret.is_none() {
             println!("error finding {} demuxer", dmx_name);
-            return;
+            return window;
         }
         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 {
@@ -618,6 +670,28 @@ impl Player {
             let str_id = s.get_id();
             if info.is_video() {
                 if video_dec.is_none() && self.play_video {
+                    #[cfg(feature="hwaccel")]
+                    if info.get_name() == "h264" && self.use_hwaccel {
+                        let mut dec = new_h264_hwdec();
+                        let dsupp = Box::new(NADecoderSupport::new());
+                        let props = info.get_properties().get_video_info().unwrap();
+                        if props.get_width() != 0 {
+                            width  = props.get_width();
+                            height = props.get_height();
+                        }
+                        if dec.init(info.clone()).is_err() {
+                            println!("failed to initialise hwaccel video decoder");
+                        } else {
+                            video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::VideoHW(dec) });
+                            self.video_str = str_id;
+                            let (tbn, tbd) = s.get_timebase();
+                            tb_num = tbn;
+                            tb_den = tbd;
+                            self.has_video = true;
+                            println!(" using hardware-accelerated decoding");
+                            continue;
+                        }
+                    }
                     if let Some(decfunc) = decfunc_mt {
                         let mut dec = (decfunc)();
                         let mut dsupp = Box::new(NADecoderSupport::new());
@@ -659,7 +733,7 @@ impl Player {
                         dsupp.pool_u32 = NAVideoBufferPool::new(reorder_depth);
                         if dec.init(&mut dsupp, info).is_err() {
                             println!("failed to initialise video decoder");
-                            return;
+                            return window;
                         }
                         video_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Video(dec, reord) });
                         self.video_str = str_id;
@@ -684,7 +758,7 @@ impl Player {
                         }
                         if dec.init(&mut dsupp, info).is_err() {
                             println!("failed to initialise audio decoder");
-                            return;
+                            return window;
                         }
                         audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) });
                         self.audio_str = str_id;
@@ -699,13 +773,29 @@ impl Player {
         }
         if !self.has_video && !self.has_audio {
             println!("No playable streams found.");
-            return;
+            return window;
         }
 
-        while (width <= 384) && (height <= 288) {
-            width <<= 1;
-            height <<= 1;
-        }
+        match self.sc_size {
+            ScaleSize::Auto => {
+                while (width <= 384) && (height <= 288) {
+                    width <<= 1;
+                    height <<= 1;
+                }
+            },
+            ScaleSize::Times(factor) => {
+                let nw = ((width  as f32) * factor).ceil() as usize;
+                let nh = ((height as f32) * factor).ceil() as usize;
+                if nw > 0 && nh > 0 {
+                    width  = nw;
+                    height = nh;
+                }
+            },
+            ScaleSize::Fixed(w, h) => {
+                width  = w;
+                height = h;
+            },
+        };
 
         // prepare playback structure
         let mut new_vcontrol = VideoControl::new(video_dec, width, height, tb_num, tb_den);
@@ -724,17 +814,16 @@ impl Player {
         let wname = if let Some(fname) = fname {
                 // 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}', "-");
+                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().expect("should be able to set window position")
-            } else {
-                builder.position_centered().build().expect("should be able to centre window")
-            };
+        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 });
@@ -747,7 +836,7 @@ impl Player {
         self.has_audio = self.acontrol.has_audio();
         if !self.has_video && !self.has_audio {
             println!("No playable streams.");
-            return;
+            return canvas.into_window();
         }
 
         // play
@@ -756,7 +845,7 @@ 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 {
@@ -854,14 +943,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()
     }
 }
 
@@ -874,6 +961,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;
@@ -914,6 +1003,14 @@ fn main() {
             "-nomt" => {
                 player.use_mt = false;
             },
+            #[cfg(feature="hwaccel")]
+            "-hwaccel" => {
+                player.use_hwaccel = true;
+            },
+            #[cfg(feature="hwaccel")]
+            "-nohwaccel" => {
+                player.use_hwaccel = false;
+            },
             "-threads" => {
                 if let Some(arg) = aiter.next() {
                     if let Ok(val) = arg.parse::<usize>() {
@@ -923,8 +1020,17 @@ fn main() {
                     }
                 }
             },
+            "-scale" => {
+                if let Some(arg) = aiter.next() {
+                    if let Ok(ssize) = arg.parse::<ScaleSize>() {
+                        player.sc_size = ssize;
+                    } else {
+                        println!("invalid scale size");
+                    }
+                }
+            },
             _ => {
-                player.play(arg, seek_time);
+                window = player.play(window, arg, seek_time);
                 if player.end { break; }
                 seek_time = NATimePoint::None;
             },