Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#28): higher framerate configurability #112

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Cargo.lock

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

6 changes: 4 additions & 2 deletions 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 <[email protected]>"]
edition = "2018"
license = "GPL-3.0-only"
Expand All @@ -26,6 +26,8 @@ log = "0.4.16"
env_logger = "0.9.0"
simplerand = "1.3.0"
humantime = "2.1.0"
crossbeam-channel = "0.5"
blockhash = "0.3"

[dependencies.clap]
version = "3.1.9"
Expand All @@ -52,7 +54,7 @@ e2e_tests = []
section = "x11"
depends = "imagemagick"
extended-description = """## Features
- Screenshotting your terminal with 4 frames per second (every 250ms)
- Screenshotting your terminal with 4/8 frames per second
- Generates high quality small sized animated gif images
- **Build-In idle frames detection and optimization** (for super fluid
presentations)
Expand Down
80 changes: 54 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Blazingly fast terminal recorder that generates animated gif images for the web
![demo](./docs/demo.gif)

## Features
- Screenshotting your terminal with 4 frames per second (every 250ms)
- Generates high quality small sized animated gif images or mp4 videos
- Screenshotting your terminal with 4/8 frames per second
- Generates high-quality small-sized animated gif images or mp4 videos
- **Build-In idle frames detection and optimization** (for super fluid presentations)
- Applies (can be disabled) border decor effects like drop shadow
- Runs on MacOS and Linux
- Runs on macOS and Linux
- Uses native efficient APIs
- Runs without any cloud service and entirely offline
- No issues with terminal sizes larger than 80x24
Expand Down Expand Up @@ -123,7 +123,7 @@ t-rec /bin/sh
### Full Options

```sh
t-rec 0.7.0
t-rec 0.7.1
Sven Assmann <[email protected]>
Blazingly fast terminal recorder that generates animated gif images for the web written in rust.

Expand All @@ -135,28 +135,56 @@ ARGS:
pass it here. For example '/bin/sh'

OPTIONS:
-b, --bg <bg> Background color when decors are used [default: transparent]
[possible values: white, black, transparent]
-d, --decor <decor> Decorates the animation with certain, mostly border effects
[default: none] [possible values: shadow, none]
-e, --end-pause <s | ms | m> to specify the pause time at the end of the animation, that
time the gif will show the last frame
-h, --help Print help information
-l, --ls-win If you want to see a list of windows available for recording
by their id, you can set env var 'WINDOWID' or `--win-id` to
record this specific window only
-m, --video Generates additionally to the gif a mp4 video of the recording
-M, --video-only Generates only a mp4 video and not gif
-n, --natural If you want a very natural typing experience and disable the
idle detection and sampling optimization
-q, --quiet Quiet mode, suppresses the banner: 'Press Ctrl+D to end
recording'
-s, --start-pause <s | ms | m> to specify the pause time at the start of the animation, that
time the gif will show the first frame
-v, --verbose Enable verbose insights for the curious
-V, --version Print version information
-w, --win-id <win-id> Window Id (see --ls-win) that should be captured, instead of
the current terminal
-b, --bg <bg>
Background color when decors are used [default: transparent] [possible values: white,
black, transparent]

-d, --decor <decor>
Decorates the animation with certain, mostly border effects [default: none] [possible
values: shadow, none]

-e, --end-pause <s | ms | m>
Specify the pause time at the end of the animation, that time the gif will show the last
frame

-f, --framerate <frames per second>
Increase the screen capturing rate (framerate) [default: 4] [possible values: 4, 8]

-h, --help
Print help information

-l, --ls-win
If you want to see a list of windows available for recording by their id, you can set
env var 'WINDOWID' or `--win-id` to record this specific window only

-m, --video
Generates additionally to the gif a mp4 video of the recording

-M, --video-only
Generates only a mp4 video and not gif

-n, --natural
If you want a very natural typing experience and disable the idle detection and sampling
optimization

-o, --output <file>
Specify the output file (without extension) [default: t-rec]

-q, --quiet
Quiet mode, suppresses the banner: 'Press Ctrl+D to end recording'

-s, --start-pause <s | ms | m>
Specify the pause time at the start of the animation, that time the gif will show the
first frame

-v, --verbose
Enable verbose insights for the curious

-V, --version
Print version information

-w, --win-id <win-id>
Window Id (see --ls-win) that should be captured, instead of the current terminal
```

### Disable idle detection & optimization
Expand Down
87 changes: 0 additions & 87 deletions src/capture.rs

This file was deleted.

55 changes: 55 additions & 0 deletions src/capture/frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use anyhow::Context;
pub use blockhash::Image;
use image::flat::View;
use image::ColorType::Rgba8;
use image::{save_buffer, FlatSamples, GenericImageView, Rgba};
use tempfile::TempDir;

use crate::capture::Timecode;
use crate::utils::IMG_EXT;
use crate::{ImageOnHeap, Result};

pub struct Frame(FlatSamples<Vec<u8>>);

impl Frame {
/// saves a frame as a IMG_EXT file
pub fn save(
&self,
tc: &Timecode,
tempdir: &TempDir,
file_name_for: fn(&Timecode, &str) -> String,
) -> Result<()> {
let image = self.as_ref();
save_buffer(
tempdir.path().join(file_name_for(tc, IMG_EXT)),
&image.samples,
image.layout.width,
image.layout.height,
image.color_hint.unwrap_or(Rgba8),
)
.context("Cannot save frame")
}
}

impl Image for Frame {
fn dimensions(&self) -> (u32, u32) {
(self.0.layout.width, self.0.layout.height)
}

fn get_pixel(&self, x: u32, y: u32) -> [u8; 4] {
let image: View<_, Rgba<u8>> = self.0.as_view().unwrap();
image.get_pixel(x, y).0
}
}

impl AsRef<FlatSamples<Vec<u8>>> for Frame {
fn as_ref(&self) -> &FlatSamples<Vec<u8>> {
&self.0
}
}

impl From<ImageOnHeap> for Frame {
fn from(img: ImageOnHeap) -> Self {
Self(*img)
}
}
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),
}
}
}
Loading