summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-05-23 14:42:54 +0200
committerrabite <rabite@posteo.de>2019-05-23 14:42:54 +0200
commitb7317949e5ff22bc9e651677839b458ac1b8f2a4 (patch)
tree00db620e2bc6be16a12fdc6ffacdd639064b4320
parent6e81584eca50c33176be83e9d9617b42823b63ee (diff)
add resize handling of media and take ysize into account
-rw-r--r--src/imgview.rs32
-rw-r--r--src/mediaview.rs39
-rw-r--r--src/preview-gen.rs266
3 files changed, 241 insertions, 96 deletions
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<ImgView> {
- 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<String>) {
@@ -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<T> = Result<T, Error>;
@@ -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<Arc<RwLock<Renderer>>>) -> 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::<gstreamer::ClockTime>() {
- 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::<gstreamer::ClockTime>() {
- 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::<gstreamer::ClockTime>() {
+ time += seek_time;
+
+ player.seek_simple(
+ gstreamer::SeekFlags::FLUSH,
+ gstreamer::format::GenericFormattedValue::from_time(time)
+ )?;
}
+ },
+ "<" => {
+ if let Some(mut time) = player
+ .query_position::<gstreamer::ClockTime>() {
+ 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::<gstreamer::ClockTime>() {
- player.seek_simple(
- gstreamer::SeekFlags::FLUSH,
- gstreamer::format::GenericFormattedValue::from_time(time)
- )?;
- }
+ if let Some(time) = player
+ .query_position::<gstreamer::ClockTime>() {
+ 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::<usize>()?;
+ let ysize = ysize.unwrap_or(String::from("0")).parse::<usize>()?;
+ 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<DynamicImage>,
+ #[cfg(feature = "video")]
+ position: Option<usize>,
+ #[cfg(feature = "video")]
+ duration: Option<usize>
}
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<String> {
+ pub fn render_image(&self, image: &DynamicImage) -> Vec<String> {
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
};
+
+ // 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
+ };
+ }
+
+ // To make half-block encoding easier, y should be divisible by 2
if new_y as u32 % 2 == 1 {
- new_y += 1 as f32;
+ new_y += 1;
}
- (xsize, new_y as usize)
+
+
+ (new_x as usize, new_y as usize)
}
}