add resize handling of media and take ysize into account

This commit is contained in:
rabite 2019-05-23 14:42:54 +02:00
parent 6e81584eca
commit b7317949e5
3 changed files with 241 additions and 96 deletions

View File

@ -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(())

View File

@ -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(())

View File

@ -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
};
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)
}
}