diff --git a/src/imgview.rs b/src/imgview.rs index c52abee..d84fc03 100644 --- a/src/imgview.rs +++ b/src/imgview.rs @@ -1,4 +1,5 @@ use crate::widget::{Widget, WidgetCore}; +use crate::coordinates::Coordinates; use crate::fail::HResult; use std::path::{Path, PathBuf}; @@ -18,7 +19,19 @@ pub struct ImgView { impl ImgView { pub fn new_from_file(core: WidgetCore, file: &Path) -> HResult { - let (xsize, ysize) = core.coordinates.size_u(); + let mut view = ImgView { + core: core, + buffer: vec![], + file: file.to_path_buf() + }; + + view.encode_file()?; + Ok(view) + } + + pub fn encode_file(&mut self) -> HResult<()> { + let (xsize, ysize) = self.core.coordinates.size_u(); + let file = &self.file; let output = std::process::Command::new("preview-gen") .arg(format!("{}", (xsize))) @@ -35,11 +48,9 @@ impl ImgView { .map(|l| l.to_string()) .collect(); - Ok(ImgView { - core: core, - buffer: output, - file: file.to_path_buf() - }) + self.buffer = output; + + Ok(()) } pub fn set_image_data(&mut self, img_data: Vec) { @@ -61,6 +72,15 @@ impl Widget for ImgView { Ok(&mut self.core) } + fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { + if &self.core.coordinates == coordinates { return Ok(()) } + + self.core.coordinates = coordinates.clone(); + self.encode_file()?; + + Ok(()) + } + fn refresh(&mut self) -> HResult<()> { Ok(()) diff --git a/src/mediaview.rs b/src/mediaview.rs index 4600dd4..e868ee8 100644 --- a/src/mediaview.rs +++ b/src/mediaview.rs @@ -2,6 +2,7 @@ use lazy_static; use termion::event::Key; use crate::widget::{Widget, WidgetCore}; +use crate::coordinates::Coordinates; use crate::async_value::Stale; use crate::fail::{HResult, HError, ErrorLog}; use crate::imgview::ImgView; @@ -95,7 +96,7 @@ impl MediaView { let mut previewer = std::process::Command::new("preview-gen") .arg(format!("{}", (xsize))) // Leave space for position/seek bar - .arg(format!("{}", (ysize-3))) + .arg(format!("{}", (ysize-1))) .arg(format!("{}", ctype.to_str())) .arg(format!("{}", auto)) .arg(format!("{}", mute)) @@ -190,19 +191,23 @@ impl MediaView { pub fn start_video(&mut self) -> HResult<()> { let runner = self.preview_runner.take(); - let stale = self.stale.clone(); - let autoplay = self.autoplay(); - let mute = self.mute(); - let position = self.position.clone(); - let duration = self.duration.clone(); if runner.is_some() { - self.clear().log(); + let stale = self.stale.clone(); + let autoplay = self.autoplay(); + let mute = self.mute(); + let position = self.position.clone(); + let duration = self.duration.clone(); + let clear = self.get_clearlist()?; + std::thread::spawn(move || -> HResult<()> { + // Sleep a bit to avoid overloading the system when scrolling let sleeptime = std::time::Duration::from_millis(50); std::thread::sleep(sleeptime); if !stale.is_stale()? { + print!("{}", clear); + runner.map(|runner| runner(autoplay, mute, position, @@ -295,7 +300,7 @@ impl MediaView { let auto = AUTOPLAY.read()?.clone(); let pos = self.position.lock()?.clone(); - // This combination means only first frame show, since + // This combination means only first frame is shown, since // self.paused will be false, even with autoplay off if pos == 0 && auto == false && self.paused == false { self.toggle_autoplay(); @@ -396,6 +401,24 @@ impl Widget for MediaView { Ok(&mut self.core) } + fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { + if &self.core.coordinates == coordinates { return Ok(()); } + + self.core.coordinates = coordinates.clone(); + + let mut imgview = self.imgview.lock()?; + imgview.set_coordinates(&coordinates)?; + + let xsize = self.core.coordinates.xsize_u(); + let ysize = self.core.coordinates.ysize_u() - 1; + + let xystring = format!("xy\n{}\n{}\n", xsize, ysize); + + self.controller.send(xystring)?; + + Ok(()) + } + fn refresh(&mut self) -> HResult<()> { self.start_video().log(); Ok(()) diff --git a/src/preview-gen.rs b/src/preview-gen.rs index c8d4936..6f71674 100644 --- a/src/preview-gen.rs +++ b/src/preview-gen.rs @@ -5,8 +5,8 @@ use image::{Pixel, FilterType, DynamicImage, GenericImageView}; use termion::color::{Bg, Fg, Rgb}; #[cfg(feature = "video")] -use termion::{input::TermRead, - event::Key}; +use termion::input::TermRead; + #[cfg(feature = "video")] use gstreamer::{self, prelude::*}; @@ -20,6 +20,8 @@ use failure::format_err; use rayon::prelude::*; use std::io::Write; +#[cfg(feature = "video")] +use std::sync::{Arc, RwLock}; pub type MResult = Result; @@ -77,12 +79,9 @@ fn image_preview(path: &str, ysize: usize) -> MResult<()> { let img = image::open(&path)?; - let renderer = Renderer { - xsize, - ysize - }; + let renderer = Renderer::new(xsize, ysize); - renderer.send_image(img)?; + renderer.send_image(&img)?; Ok(()) } @@ -93,6 +92,7 @@ fn video_preview(path: &String, autoplay: bool, mute: bool) -> MResult<()> { + let (player, appsink) = make_gstreamer()?; let uri = format!("file://{}", &path); @@ -100,10 +100,12 @@ fn video_preview(path: &String, player.set_property("uri", &uri)?; - let renderer = Renderer { - xsize, - ysize - }; + let renderer = Renderer::new(xsize, ysize); + let renderer = Arc::new(RwLock::new(renderer)); + let crenderer = renderer.clone(); + + + let p = player.clone(); @@ -124,26 +126,28 @@ fn video_preview(path: &String, .map(|d| d.seconds().unwrap_or(0)) .unwrap_or(0); - match renderer.send_frame(&*sample, - position, - duration) { - Ok(()) => { - if autoplay == false { - // Just render first frame to get a static image - match p.set_state(gstreamer::State::Paused) - .into_result() { - Ok(_) => gstreamer::FlowReturn::Eos, - Err(_) => gstreamer::FlowReturn::Error - } - } else { - gstreamer::FlowReturn::Ok + if let Ok(mut renderer) = crenderer.write() { + match renderer.send_frame(&*sample, + position, + duration) { + Ok(()) => { + if autoplay == false { + // Just render first frame to get a static image + match p.set_state(gstreamer::State::Paused) + .into_result() { + Ok(_) => gstreamer::FlowReturn::Eos, + Err(_) => gstreamer::FlowReturn::Error + } + } else { + gstreamer::FlowReturn::Ok + } + } + Err(err) => { + println!("{:?}", err); + gstreamer::FlowReturn::Error } } - Err(err) => { - println!("{:?}", err); - gstreamer::FlowReturn::Error - } - } + } else { gstreamer::FlowReturn::Error } } }) @@ -161,67 +165,95 @@ fn video_preview(path: &String, player.set_state(gstreamer::State::Playing).into_result()?; - read_keys(player)?; + + + + read_keys(player, Some(renderer))?; Ok(()) } #[cfg(feature = "video")] -pub fn read_keys(player: gstreamer::Element) -> MResult<()> { +fn read_keys(player: gstreamer::Element, + renderer: Option>>) -> MResult<()> { let seek_time = gstreamer::ClockTime::from_seconds(5); - for key in std::io::stdin().keys() { - match key { - Ok(Key::Char('q')) => std::process::exit(0), - Ok(Key::Char('>')) => { - if let Some(mut time) = player.query_position::() { - time += seek_time; - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } - }, - Ok(Key::Char('<')) => { - if let Some(mut time) = player.query_position::() { - if time >= seek_time { - time -= seek_time; - } else { - time = gstreamer::ClockTime(Some(0)); + let stdin = std::io::stdin(); + let mut stdin = stdin.lock(); + + loop { + let input = stdin + .read_line()? + .unwrap_or_else(|| String::from("q")); + + + match input.as_str() { + "q" => std::process::exit(0), + ">" => { + if let Some(mut time) = player + .query_position::() { + time += seek_time; + + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; } + }, + "<" => { + if let Some(mut time) = player + .query_position::() { + if time >= seek_time { + time -= seek_time; + } else { + time = gstreamer::ClockTime(Some(0)); + } - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; + } } - Ok(Key::Char('p')) => { + "p" => { player.set_state(gstreamer::State::Playing).into_result()?; // To actually start playing again - if let Some(time) = player.query_position::() { - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } + if let Some(time) = player + .query_position::() { + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; + } } - Ok(Key::Char('a')) => { + "a" => { player.set_state(gstreamer::State::Paused).into_result()?; } - Ok(Key::Char('m')) => { + "m" => { player.set_property("volume", &0.0)?; } - Ok(Key::Char('u')) => { + "u" => { player.set_property("volume", &1.0)?; } + "xy" => { + if let Some(ref renderer) = renderer { + let xsize = stdin.read_line()?; + let ysize = stdin.read_line()?; + let xsize = xsize.unwrap_or(String::from("0")).parse::()?; + let ysize = ysize.unwrap_or(String::from("0")).parse::()?; + let mut renderer = renderer + .write() + .map_err(|_| format_err!("Renderer RwLock failed!"))?; + + renderer.set_size(xsize, ysize)?; + } + } _ => {} } } - Ok(()) } #[cfg(feature = "video")] @@ -275,7 +307,7 @@ pub fn audio_preview(path: &String, player.set_state(gstreamer::State::Playing).into_result()?; } - read_keys(player)?; + read_keys(player, None)?; Ok(()) } @@ -326,22 +358,66 @@ pub fn make_gstreamer() -> MResult<(gstreamer::Element, struct Renderer { xsize: usize, - ysize: usize + ysize: usize, + #[cfg(feature = "video")] + last_frame: Option, + #[cfg(feature = "video")] + position: Option, + #[cfg(feature = "video")] + duration: Option } impl Renderer { - fn send_image(&self, image: DynamicImage) -> MResult<()> { + fn new(xsize: usize, ysize: usize) -> Renderer { + Renderer { + xsize, + ysize, + #[cfg(feature = "video")] + last_frame: None, + #[cfg(feature = "video")] + position: None, + #[cfg(feature = "video")] + duration: None + } + } + + + #[cfg(feature = "video")] + fn set_size(&mut self, xsize: usize, ysize: usize) -> MResult<()> { + self.xsize = xsize; + self.ysize = ysize; + + if let Some(ref frame) = self.last_frame { + let pos = self.position.unwrap_or(0); + let dur = self.duration.unwrap_or(0); + + // Use send_image, because send_frame takes SampleRef + self.send_image(frame)?; + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + + writeln!(stdout, "")?; + writeln!(stdout, "{}", pos)?; + writeln!(stdout, "{}", dur)?; + } + Ok(()) + } + + fn send_image(&self, image: &DynamicImage) -> MResult<()> { let rendered_img = self.render_image(image); + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); for line in rendered_img { - write!(std::io::stdout(), "{}\n", line)?; + writeln!(stdout, "{}", line)?; } Ok(()) } #[cfg(feature = "video")] - fn send_frame(&self, + fn send_frame(&mut self, frame: &gstreamer::sample::SampleRef, position: u64, duration: u64) @@ -354,23 +430,30 @@ impl Renderer { let img = image::load_from_memory_with_format(&map, image::ImageFormat::PNM)?; - let rendered_img = self.render_image(img); + let rendered_img = self.render_image(&img); + + self.last_frame = Some(img); + self.position = Some(position as usize); + self.duration = Some(duration as usize); + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); for line in rendered_img { - writeln!(std::io::stdout(), "{}", line)?; + writeln!(stdout, "{}", line)?; } // Empty line means end of frame - writeln!(std::io::stdout(), "")?; + writeln!(stdout, "")?; // Send position and duration - writeln!(std::io::stdout(), "{}", position)?; - writeln!(std::io::stdout(), "{}", duration)?; + writeln!(stdout, "{}", position)?; + writeln!(stdout, "{}", duration)?; Ok(()) } - pub fn render_image(&self, image: DynamicImage) -> Vec { + pub fn render_image(&self, image: &DynamicImage) -> Vec { let (xsize, ysize) = self.max_size(&image); let img = image.resize_exact(xsize as u32, @@ -405,18 +488,37 @@ impl Renderer { pub fn max_size(&self, image: &DynamicImage) -> (usize, usize) { let xsize = self.xsize; + let ysize = self.ysize; let img_xsize = image.width(); let img_ysize = image.height(); let img_ratio = img_xsize as f32 / img_ysize as f32; - let mut new_y = if img_ratio < 1 as f32 { - xsize as f32 * img_ratio + let mut new_x = xsize; + let mut new_y; + + new_y = if img_ratio < 1 as f32 { + (xsize as f32 * img_ratio) as usize } else { - xsize as f32 / img_ratio + (xsize as f32 / img_ratio) as usize }; - if new_y as u32 % 2 == 1 { - new_y += 1 as f32; + + // Multiply by two because of half-block + if new_y > ysize*2 { + new_y = self.ysize * 2; + + new_x = if img_ratio < 1 as f32 { + (ysize as f32 / img_ratio) as usize * 2 + } else { + (ysize as f32 * img_ratio) as usize * 2 + }; } - (xsize, new_y as usize) + + // To make half-block encoding easier, y should be divisible by 2 + if new_y as u32 % 2 == 1 { + new_y += 1; + } + + + (new_x as usize, new_y as usize) } }