Skip to content

Commit

Permalink
feat(#36): Add framerate cli option
Browse files Browse the repository at this point in the history
- retyping of u128 to Timecode not everywhere fully clean
- impl thread safe frame dropping strategy with comparing image hashes
- keeping a linked list of `FrameEssences`
  • Loading branch information
sassman committed Apr 18, 2022
1 parent d745666 commit 88d8bad
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 82 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "t-rec"
version = "0.7.3"
version = "0.7.3-alpha.1"
authors = ["Sven Assmann <sven.assmann.it@gmail.com>"]
edition = "2018"
license = "GPL-3.0-only"
Expand Down
45 changes: 45 additions & 0 deletions src/capture/frame_comparator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::collections::LinkedList;

use crate::capture::{FrameDropStrategy, FrameEssence};

pub struct FrameComparator {
last_frames: LinkedList<FrameEssence>,
strategy: FrameDropStrategy,
}

impl FrameComparator {
pub fn new(strategy: FrameDropStrategy) -> Self {
Self {
last_frames: LinkedList::new(),
strategy,
}
}
}

impl FrameComparator {
pub fn should_drop_frame(&mut self, frame_essence: FrameEssence) -> bool {
match self.strategy {
FrameDropStrategy::DoNotDropAny => false,
FrameDropStrategy::DropIdenticalFrames => {
if let Some(FrameEssence { when, what }) = self.last_frames.pop_back() {
if frame_essence.when > when && what == frame_essence.what {
// so the last frame and this one is the same... so let's drop it..
// but add the current frame
self.last_frames.push_back(frame_essence);
true
} else {
let previous_should_drop_frame = self.should_drop_frame(frame_essence);
// restore the popped frame..
self.last_frames.push_back(FrameEssence { when, what });

previous_should_drop_frame
}
} else {
self.last_frames.push_back(frame_essence);

false
}
}
}
}
}
24 changes: 24 additions & 0 deletions src/capture/frame_essence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use blockhash::{blockhash256, Blockhash256};

use crate::capture::{Frame, Timecode};

#[derive(Eq, PartialEq, Clone)]
pub enum FrameDropStrategy {
DoNotDropAny,
DropIdenticalFrames,
}

#[derive(Eq, PartialOrd, PartialEq, Clone)]
pub struct FrameEssence {
pub(crate) when: Timecode,
pub(crate) what: Blockhash256,
}

impl FrameEssence {
pub fn new(frame: &Frame, timecode: &Timecode) -> Self {
Self {
when: timecode.clone(),
what: blockhash256(frame),
}
}
}
4 changes: 4 additions & 0 deletions src/capture/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
mod frame;
mod frame_comparator;
mod frame_essence;
mod framerate;
mod processor;
mod timecode;

pub use frame::*;
pub use frame_comparator::*;
pub use frame_essence::*;
pub use framerate::*;
pub use processor::*;
pub use timecode::*;
96 changes: 18 additions & 78 deletions src/capture/processor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use blockhash::{blockhash256, Blockhash256};
use crossbeam_channel::Receiver;
use rayon::ThreadPoolBuilder;
use std::borrow::Borrow;
Expand All @@ -7,39 +6,12 @@ use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tempfile::TempDir;

use crate::capture::frame_comparator::FrameComparator;
use crate::capture::frame_essence::{FrameDropStrategy, FrameEssence};
use crate::capture::{Framerate, Timecode};
use crate::utils::file_name_for;
use crate::{Frame, PlatformApi, Result, WindowId};

#[derive(Eq, PartialEq, Clone)]
pub enum FrameDropStrategy {
DoNotDropAny,
DropIdenticalFrames,
}

#[derive(Clone)]
struct FrameComparator {
last_frames: Vec<(Timecode, Timecode, Blockhash256)>,
_strategy: FrameDropStrategy,
}

impl FrameComparator {
pub fn should_drop_frame(&mut self, timecode: &Timecode, frame: &Frame) -> bool {
let hash = blockhash256(frame);
if let Some((_last_time_code, _other_time_code, last_hash)) = self.last_frames.last() {
let last_eq = last_hash == &hash;
if !last_eq {
self.last_frames.pop();
self.last_frames.push((timecode.clone(), hash));
}
last_eq
} else {
self.last_frames.push((timecode.clone(), hash));
false
}
}
}

/// captures screenshots as file on disk
/// collects also the timecodes when they have been captured
/// stops once receiving something in rx
Expand All @@ -55,23 +27,16 @@ pub fn capture_thread(
let pool = ThreadPoolBuilder::default().build()?;
let duration = Duration::from_secs(1) / *framerate.as_ref();
let start = Instant::now();
// let mut idle_duration = Duration::from_millis(0);
// let mut last_frame: Option<ImageOnHeap> = None;
// let mut identical_frames = 0;
let mut last_time = Instant::now();
let api = Arc::new(api);
let comp = Arc::new(Mutex::new(FrameComparator {
last_frames: Vec::new(),
_strategy: frame_drop_strategy.clone(),
}));
// let rx = Arc::new(rx);
// let mut results: Arc<Mutex<Vec<Result<()>>>> = Arc::new(Mutex::new(Vec::new()));
let comparator = Arc::new(Mutex::new(FrameComparator::new(
frame_drop_strategy.clone(),
)));

pool.scope(|s| {
loop {
let delta = Instant::now().saturating_duration_since(last_time);
let sleep_time = duration.sub(delta);
// thread::sleep(sleep_time);
// blocks for a timeout
if rx.recv_timeout(sleep_time).is_ok() {
if pool.current_thread_has_pending_tasks().unwrap_or(false) {
Expand All @@ -85,53 +50,28 @@ pub fn capture_thread(
s.spawn({
let api = api.clone();
let tempdir = tempdir.clone();
let comp = comp.clone();
let comp = comparator.clone();
let time_codes = time_codes.clone();
move |_| {
let tc: Timecode = now.saturating_duration_since(start).as_millis().into();

let frame = api.capture_window_screenshot(win_id);
if let Ok(frame) = frame {
if let Ok(frame) = api.capture_window_screenshot(win_id) {
let frame: Frame = frame.into();
if comp.lock().unwrap().should_drop_frame(&tc, &frame) {
return;
let frame_essence = FrameEssence::new(&frame, &tc);
{
let mut lock = comp.try_lock();
if let Ok(ref mut mutex) = lock {
if mutex.should_drop_frame(frame_essence) {
return;
}
} else {
dbg!(" locking failed...");
}
}
frame.save(&tc, tempdir.borrow(), file_name_for).unwrap();
time_codes.lock().unwrap().push(tc);
// results.borrow_mut().lock().unwrap().push(result);
}
}
});

/*
let image = api.capture_window_screenshot(win_id)?;
if frame_drop_strategy == &FrameDropStrategy::DropIdenticalFrames {
if last_frame.is_some()
&& image
.samples
.as_slice()
.eq(last_frame.as_ref().unwrap().samples.as_slice())
{
identical_frames += 1;
} else {
identical_frames = 0;
}
}
if identical_frames > 0 {
// let's track now the duration as idle
idle_duration = idle_duration.add(now.duration_since(last_now));
} else {
if let Err(e) = save_frame(&image, tc, tempdir.lock().unwrap().borrow(), file_name_for)
{
eprintln!("{}", &e);
return Err(e);
}
time_codes.lock().unwrap().push(tc);
last_frame = Some(image);
identical_frames = 0;
}
*/
});

last_time = now;
}
Expand Down
2 changes: 1 addition & 1 deletion src/capture/timecode.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct Timecode(u32);

impl AsRef<u32> for Timecode {
Expand Down
2 changes: 1 addition & 1 deletion src/generators/gif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn generate_gif_with_convert(
println!("🎉 🚀 Generating {target}");
let mut cmd = Command::new(PROGRAM);
cmd.arg("-loop").arg("0");
let mut delay = 0;
let mut delay: u32 = 0;
let temp = tempdir.path();
let last_frame_i = time_codes.last();
if last_frame_i.is_none() {
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ fn main() -> Result<()> {

let target = target_file(args.value_of("file").unwrap());
let mut time = Duration::default();
time_codes.lock().unwrap().sort_unstable();

if should_generate_gif {
time += prof! {
Expand Down

0 comments on commit 88d8bad

Please sign in to comment.