1- use anyhow :: Context ;
1+ use blockhash :: { blockhash256 , Blockhash256 } ;
22use crossbeam_channel:: Receiver ;
3- use image:: save_buffer;
4- use image:: ColorType :: Rgba8 ;
53use rayon:: ThreadPoolBuilder ;
64use std:: borrow:: Borrow ;
75use std:: ops:: Sub ;
86use std:: sync:: { Arc , Mutex } ;
97use std:: time:: { Duration , Instant } ;
108use tempfile:: TempDir ;
119
12- use crate :: capture:: Framerate ;
13- use crate :: utils:: { file_name_for, IMG_EXT } ;
14- use crate :: { ImageOnHeap , PlatformApi , Result , WindowId } ;
10+ use crate :: capture:: { Framerate , Timecode } ;
11+ use crate :: utils:: file_name_for;
12+ use crate :: { Frame , PlatformApi , Result , WindowId } ;
1513
16- #[ derive( Eq , PartialEq ) ]
14+ #[ derive( Eq , PartialEq , Clone ) ]
1715pub enum FrameDropStrategy {
1816 DoNotDropAny ,
1917 DropIdenticalFrames ,
2018}
2119
2220#[ derive( Clone ) ]
23- struct FrameComparator < ' a > {
24- last_frame : Option < ImageOnHeap > ,
25- strategy : & ' a FrameDropStrategy ,
21+ struct FrameComparator {
22+ last_frames : Vec < ( Timecode , Timecode , Blockhash256 ) > ,
23+ _strategy : FrameDropStrategy ,
2624}
2725
28- impl < ' a > FrameComparator < ' a > {
29- pub fn drop_frame ( & mut self , frame : ImageOnHeap ) -> bool {
30- if self . last_frame . is_none ( ) {
31- self . last_frame = Some ( frame) ;
32- false
26+ impl FrameComparator {
27+ pub fn should_drop_frame ( & mut self , timecode : & Timecode , frame : & Frame ) -> bool {
28+ let hash = blockhash256 ( frame) ;
29+ if let Some ( ( _last_time_code, _other_time_code, last_hash) ) = self . last_frames . last ( ) {
30+ let last_eq = last_hash == & hash;
31+ if !last_eq {
32+ self . last_frames . pop ( ) ;
33+ self . last_frames . push ( ( timecode. clone ( ) , hash) ) ;
34+ }
35+ last_eq
3336 } else {
37+ self . last_frames . push ( ( timecode. clone ( ) , hash) ) ;
3438 false
3539 }
3640 }
@@ -43,23 +47,23 @@ pub fn capture_thread(
4347 rx : & Receiver < ( ) > ,
4448 api : impl PlatformApi + Sync ,
4549 win_id : WindowId ,
46- time_codes : Arc < Mutex < Vec < u128 > > > ,
47- tempdir : Arc < Mutex < TempDir > > ,
50+ time_codes : Arc < Mutex < Vec < Timecode > > > ,
51+ tempdir : Arc < TempDir > ,
4852 frame_drop_strategy : & FrameDropStrategy ,
4953 framerate : & Framerate ,
5054) -> Result < ( ) > {
5155 let pool = ThreadPoolBuilder :: default ( ) . build ( ) ?;
5256 let duration = Duration :: from_secs ( 1 ) / * framerate. as_ref ( ) ;
5357 let start = Instant :: now ( ) ;
54- let mut idle_duration = Duration :: from_millis ( 0 ) ;
55- let mut last_frame: Option < ImageOnHeap > = None ;
56- let mut identical_frames = 0 ;
58+ // let mut idle_duration = Duration::from_millis(0);
59+ // let mut last_frame: Option<ImageOnHeap> = None;
60+ // let mut identical_frames = 0;
5761 let mut last_time = Instant :: now ( ) ;
5862 let api = Arc :: new ( api) ;
59- let comp = Arc :: new ( FrameComparator {
60- last_frame : None ,
61- strategy : frame_drop_strategy,
62- } ) ;
63+ let comp = Arc :: new ( Mutex :: new ( FrameComparator {
64+ last_frames : Vec :: new ( ) ,
65+ _strategy : frame_drop_strategy. clone ( ) ,
66+ } ) ) ;
6367 // let rx = Arc::new(rx);
6468 // let mut results: Arc<Mutex<Vec<Result<()>>>> = Arc::new(Mutex::new(Vec::new()));
6569
@@ -72,26 +76,32 @@ pub fn capture_thread(
7276 if rx. recv_timeout ( sleep_time) . is_ok ( ) {
7377 if pool. current_thread_has_pending_tasks ( ) . unwrap_or ( false ) {
7478 println ! (
75- "there is a backlog of frames that needs to be persisted , this may take a bit ..." ,
79+ "there is a backlog of frames that needs to be stored , this may take a bit ..." ,
7680 ) ;
7781 }
7882 return ;
7983 }
8084 let now = Instant :: now ( ) ;
81- let timecode = now. saturating_duration_since ( start) . as_millis ( ) ;
82- // let effective_now = now.sub(idle_duration);
83- let api = api. clone ( ) ;
84- let tempdir = tempdir. clone ( ) ;
85- time_codes. lock ( ) . unwrap ( ) . push ( timecode) ;
86-
87- s. spawn ( move |_| {
88- let frame = api. capture_window_screenshot ( win_id) ;
85+ s. spawn ( {
86+ let api = api. clone ( ) ;
87+ let tempdir = tempdir. clone ( ) ;
88+ let comp = comp. clone ( ) ;
89+ let time_codes = time_codes. clone ( ) ;
90+ move |_| {
91+ let tc: Timecode = now. saturating_duration_since ( start) . as_millis ( ) . into ( ) ;
8992
90- if let Ok ( frame) = frame {
91- save_frame ( & frame, timecode, tempdir. borrow ( ) , file_name_for) . unwrap ( ) ;
92- // results.borrow_mut().lock().unwrap().push(result);
93+ let frame = api. capture_window_screenshot ( win_id) ;
94+ if let Ok ( frame) = frame {
95+ let frame: Frame = frame. into ( ) ;
96+ if comp. lock ( ) . unwrap ( ) . should_drop_frame ( & tc, & frame) {
97+ return ;
98+ }
99+ frame. save ( & tc, tempdir. borrow ( ) , file_name_for) . unwrap ( ) ;
100+ time_codes. lock ( ) . unwrap ( ) . push ( tc) ;
101+ // results.borrow_mut().lock().unwrap().push(result);
102+ }
93103 }
94- } ) ;
104+ } ) ;
95105
96106 /*
97107 let image = api.capture_window_screenshot(win_id)?;
@@ -129,20 +139,3 @@ pub fn capture_thread(
129139
130140 Ok ( ( ) )
131141}
132-
133- /// saves a frame as a tga file
134- pub fn save_frame (
135- image : & ImageOnHeap ,
136- time_code : u128 ,
137- tempdir : & TempDir ,
138- file_name_for : fn ( & u128 , & str ) -> String ,
139- ) -> Result < ( ) > {
140- save_buffer (
141- tempdir. path ( ) . join ( file_name_for ( & time_code, IMG_EXT ) ) ,
142- & image. samples ,
143- image. layout . width ,
144- image. layout . height ,
145- image. color_hint . unwrap_or ( Rgba8 ) ,
146- )
147- . context ( "Cannot save frame" )
148- }
0 commit comments