X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=videoplayer%2Fsrc%2Fmain.rs;h=cabb925fb5e0ecd1dbcd6d7ecfcf8a7cc72b8d58;hb=35d0d63a755ace4c79769aeb04f94469f8b47709;hp=674a243ae49ab9d55e9a3198deb63781e6deb01d;hpb=86da77091a6bb3eca31d5b8066cd95e981e4f8f7;p=nihav-player.git diff --git a/videoplayer/src/main.rs b/videoplayer/src/main.rs index 674a243..cabb925 100644 --- a/videoplayer/src/main.rs +++ b/videoplayer/src/main.rs @@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicU8, Ordering}; use sdl2::event::{Event, WindowEvent}; use sdl2::keyboard::{Keycode, Mod}; -use sdl2::mouse::MouseButton; +use sdl2::mouse::{MouseButton, MouseWheelDirection}; use sdl2::render::{Canvas, Texture, TextureCreator}; use sdl2::pixels::PixelFormatEnum; use sdl2::video::{Window, WindowContext}; @@ -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; @@ -35,8 +38,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 +49,6 @@ enum DecodingState { End, } -impl Default for DecodingState { - fn default() -> Self { DecodingState::Normal } -} - impl From for DecodingState { fn from(val: u8) -> Self { match val { @@ -95,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 { + if matches!(s, "" | "auto") { + Ok(ScaleSize::Auto) + } else if s.ends_with('x') || s.ends_with('X') { + let factor = s[..s.len() - 1].parse::().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::().map_err(|_| ())?; + let height = h.parse::().map_err(|_| ())?; + if width > 0 && height > 0 { + Ok(ScaleSize::Fixed(width, height)) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + pub enum PktSendEvent { Packet(NAPacket), GetFrames, @@ -108,6 +143,8 @@ pub enum DecoderType { Audio(Box), Video(Box, Box), VideoMT(Box, MTFrameReorderer), + #[cfg(feature="hwaccel")] + VideoHW(Box), } pub struct DecoderStuff { @@ -296,8 +333,6 @@ struct Player { sdl_context: sdl2::Sdl, vsystem: sdl2::VideoSubsystem, asystem: sdl2::AudioSubsystem, - xpos: Option, - ypos: Option, acontrol: AudioControl, vcontrol: VideoControl, @@ -308,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, @@ -332,12 +370,10 @@ impl Player { 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, @@ -347,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, @@ -480,6 +519,9 @@ impl Player { _ => {}, }; } + 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), keymod, ..} = event { match keycode { Keycode::Escape => { @@ -487,12 +529,12 @@ impl Player { println!(); return Ok(true); }, - Keycode::Q if matches!(keymod, Mod::RSHIFTMOD | Mod::LSHIFTMOD) => { + Keycode::Q if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) => { self.end = true; println!(); return Ok(true); }, - Keycode::Return => 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)?; }, @@ -527,7 +569,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,24 +585,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 dmx_fact; 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)}); @@ -574,14 +624,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(); + 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 { @@ -596,6 +646,7 @@ impl Player { let mut tb_num = 0; let mut tb_den = 0; let mut ainfo: Option = None; + let mut sbr_hack = false; let mut video_dec: Option = None; let mut audio_dec: Option = None; @@ -618,6 +669,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 +732,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; @@ -677,9 +750,14 @@ impl Player { let mut dec = (decfunc)(); let mut dsupp = Box::new(NADecoderSupport::new()); ainfo = info.get_properties().get_audio_info(); + 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; + return window; } audio_dec = Some(DecoderStuff{ dsupp, dec: DecoderType::Audio(dec) }); self.audio_str = str_id; @@ -694,19 +772,35 @@ 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); 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 { @@ -717,16 +811,18 @@ impl Player { let fname = path.file_name(); let wname = if let Some(fname) = fname { - "NihAV player - ".to_owned() + fname.to_str().expect("should be able to set window title") + // 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().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 }); @@ -739,7 +835,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 @@ -748,7 +844,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 { @@ -846,14 +942,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() } } @@ -866,6 +960,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; @@ -875,7 +971,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::() { seek_time = time; @@ -906,6 +1002,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::() { @@ -915,8 +1019,17 @@ fn main() { } } }, + "-scale" => { + if let Some(arg) = aiter.next() { + if let Ok(ssize) = arg.parse::() { + 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; },