From ba3d78f0443ea691c8fc13709e94bc3dd6aced1b Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 4 Nov 2019 11:53:11 -0500 Subject: allow limiting the max length of a ttyrec frame --- src/cmd/play.rs | 34 ++++++++++++++++++++++++++++------ src/config.rs | 30 ++++++++++++++++++++++++++++++ src/error.rs | 3 +++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/cmd/play.rs b/src/cmd/play.rs index 8925701..c83bb88 100644 --- a/src/cmd/play.rs +++ b/src/cmd/play.rs @@ -27,6 +27,7 @@ impl crate::config::Config for Config { Box::new(PlaySession::new( &self.ttyrec.filename, self.play.playback_ratio, + self.play.max_frame_length, )) } } @@ -67,22 +68,34 @@ enum FileState { struct PlaySession { file: FileState, + playback_ratio: f32, + max_frame_length: Option, + to_write: DumbDelayQueue>, // to_write: tokio::timer::delay_queue::DelayQueue>, base_time: std::time::Instant, - playback_ratio: f32, + last_frame_time: std::time::Duration, + total_time_clamped: std::time::Duration, } impl PlaySession { - fn new(filename: &str, playback_ratio: f32) -> Self { + fn new( + filename: &str, + playback_ratio: f32, + max_frame_length: Option, + ) -> Self { Self { file: FileState::Closed { filename: filename.to_string(), }, + playback_ratio, + max_frame_length, + to_write: DumbDelayQueue::new(), // to_write: tokio::timer::delay_queue::DelayQueue::new(), base_time: std::time::Instant::now(), - playback_ratio, + last_frame_time: std::time::Duration::default(), + total_time_clamped: std::time::Duration::default(), } } } @@ -132,12 +145,21 @@ impl PlaySession { .poll_read() .context(crate::error::ReadTtyrec)) { + let frame_time = frame.time - reader.offset().unwrap(); + let frame_dur = (frame_time - self.last_frame_time) + .div_f32(self.playback_ratio); + self.total_time_clamped += self + .max_frame_length + .map_or(frame_dur, |max_frame_length| { + frame_dur.min(max_frame_length) + }); + self.to_write.insert_at( frame.data, - self.base_time - + (frame.time - reader.offset().unwrap()) - .div_f32(self.playback_ratio), + self.base_time + self.total_time_clamped, ); + + self.last_frame_time = frame_time; } else { self.file = FileState::Eof; } diff --git a/src/config.rs b/src/config.rs index 85daf27..0387fa8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ const FILENAME_OPTION: &str = "filename"; const LISTEN_ADDRESS_OPTION: &str = "listen-address"; const LOGIN_PLAIN_OPTION: &str = "login-plain"; const LOGIN_RECURSE_CENTER_OPTION: &str = "login-recurse-center"; +const MAX_FRAME_LENGTH_OPTION: &str = "max-frame-length"; const PLAYBACK_RATIO_OPTION: &str = "playback-ratio"; const READ_TIMEOUT_OPTION: &str = "read-timeout-secs"; const TLS_IDENTITY_FILE_OPTION: &str = "tls-identity-file"; @@ -700,12 +701,17 @@ fn default_ttyrec_filename() -> String { pub struct Play { #[serde(default = "default_playback_ratio")] pub playback_ratio: f32, + + #[serde(default, deserialize_with = "max_frame_length")] + pub max_frame_length: Option, } impl Play { pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { let playback_ratio_help = "Speed to play back the ttyrec at (defaults to 1.0)"; + let max_frame_length_help = + "Clamp frame duration at this number of seconds"; app.arg( clap::Arg::with_name(PLAYBACK_RATIO_OPTION) .long(PLAYBACK_RATIO_OPTION) @@ -713,6 +719,13 @@ impl Play { .value_name("RATIO") .help(playback_ratio_help), ) + .arg( + clap::Arg::with_name(MAX_FRAME_LENGTH_OPTION) + .long(MAX_FRAME_LENGTH_OPTION) + .takes_value(true) + .value_name("SECS") + .help(max_frame_length_help), + ) } pub fn merge_args<'a>( @@ -729,6 +742,11 @@ impl Play { name: PLAYBACK_RATIO_OPTION, })?; } + self.max_frame_length = matches + .value_of(MAX_FRAME_LENGTH_OPTION) + .map(|len| len.parse().map(std::time::Duration::from_secs)) + .transpose() + .context(crate::error::ParseMaxFrameLength)?; Ok(()) } } @@ -737,6 +755,7 @@ impl Default for Play { fn default() -> Self { Self { playback_ratio: default_playback_ratio(), + max_frame_length: None, } } } @@ -745,6 +764,17 @@ fn default_playback_ratio() -> f32 { 1.0 } +fn max_frame_length<'a, D>( + deserializer: D, +) -> std::result::Result, D::Error> +where + D: serde::de::Deserializer<'a>, +{ + Ok(Some(std::time::Duration::from_secs(u64::deserialize( + deserializer, + )?))) +} + pub fn oauth_configs<'a, D>( deserializer: D, ) -> std::result::Result< diff --git a/src/error.rs b/src/error.rs index 3c3d663..e3c5206 100644 --- a/src/error.rs +++ b/src/error.rs @@ -226,6 +226,9 @@ pub enum Error { #[snafu(display("failed to parse response json: {}", source))] ParseJson { source: reqwest::Error }, + #[snafu(display("failed to parse max frame length: {}", source))] + ParseMaxFrameLength { source: std::num::ParseIntError }, + #[snafu(display( "failed to parse port {} from address: {}", string, -- cgit v1.2.3-54-g00ecf