@@ -4,7 +4,7 @@ use ::windows::{
4
4
Win32 :: Graphics :: Direct3D11 :: { D3D11_BOX , ID3D11Device } ,
5
5
} ;
6
6
use cap_fail:: fail_err;
7
- use cap_timestamp:: PerformanceCounterTimestamp ;
7
+ use cap_timestamp:: { PerformanceCounterTimestamp , Timestamps } ;
8
8
use cpal:: traits:: { DeviceTrait , HostTrait } ;
9
9
use kameo:: prelude:: * ;
10
10
use scap_ffmpeg:: * ;
@@ -52,6 +52,9 @@ struct FrameHandler {
52
52
last_log : Instant ,
53
53
frame_events : VecDeque < ( Instant , bool ) > ,
54
54
video_tx : Sender < ( scap_direct3d:: Frame , Timestamp ) > ,
55
+ target_fps : u32 ,
56
+ last_timestamp : Option < Timestamp > ,
57
+ timestamps : Timestamps ,
55
58
}
56
59
57
60
impl Actor for FrameHandler {
@@ -126,14 +129,30 @@ impl Message<NewFrame> for FrameHandler {
126
129
msg : NewFrame ,
127
130
ctx : & mut kameo:: prelude:: Context < Self , Self :: Reply > ,
128
131
) -> Self :: Reply {
129
- let Ok ( timestamp) = msg. frame . inner ( ) . SystemRelativeTime ( ) else {
132
+ let Ok ( timestamp) = msg. 0 . inner ( ) . SystemRelativeTime ( ) else {
130
133
return ;
131
134
} ;
132
135
133
- let frame_dropped = match self . video_tx . try_send ( (
134
- msg. frame ,
135
- Timestamp :: PerformanceCounter ( PerformanceCounterTimestamp :: new ( timestamp. Duration ) ) ,
136
- ) ) {
136
+ let timestamp =
137
+ Timestamp :: PerformanceCounter ( PerformanceCounterTimestamp :: new ( timestamp. Duration ) ) ;
138
+
139
+ // manual FPS limiter
140
+ if let Some ( last_timestamp) = self . last_timestamp
141
+ && let Some ( time_since_last) = timestamp
142
+ . duration_since ( self . timestamps )
143
+ . checked_sub ( last_timestamp. duration_since ( self . timestamps ) )
144
+ {
145
+ let target_interval = 1.0 / self . target_fps as f32 ;
146
+ let tolerance = target_interval * 0.8 ; // Allow 20% early arrival
147
+
148
+ if time_since_last. as_secs_f32 ( ) < tolerance {
149
+ return ;
150
+ }
151
+ }
152
+
153
+ self . last_timestamp = Some ( timestamp) ;
154
+
155
+ let frame_dropped = match self . video_tx . try_send ( ( msg. 0 , timestamp) ) {
137
156
Err ( flume:: TrySendError :: Disconnected ( _) ) => {
138
157
warn ! ( "Pipeline disconnected" ) ;
139
158
let _ = ctx. actor_ref ( ) . stop_gracefully ( ) . await ;
@@ -232,6 +251,9 @@ impl PipelineSourceTask for ScreenCaptureSource<Direct3DCapture> {
232
251
frames_dropped : Default :: default ( ) ,
233
252
last_cleanup : Instant :: now ( ) ,
234
253
last_log : Instant :: now ( ) ,
254
+ target_fps : config. fps ,
255
+ last_timestamp : None ,
256
+ timestamps : Timestamps :: now ( ) ,
235
257
} ) ;
236
258
237
259
let mut settings = scap_direct3d:: Settings {
@@ -249,7 +271,6 @@ impl PipelineSourceTask for ScreenCaptureSource<Direct3DCapture> {
249
271
back : 1 ,
250
272
}
251
273
} ) ,
252
- min_update_interval : Some ( Duration :: from_millis ( 16 ) ) ,
253
274
..Default :: default ( )
254
275
} ;
255
276
@@ -380,10 +401,7 @@ pub enum StartCapturingError {
380
401
StartCapturer ( :: windows:: core:: Error ) ,
381
402
}
382
403
383
- pub struct NewFrame {
384
- pub frame : scap_direct3d:: Frame ,
385
- pub display_time : SystemTime ,
386
- }
404
+ pub struct NewFrame ( pub scap_direct3d:: Frame ) ;
387
405
388
406
impl Message < StartCapturing > for ScreenCaptureActor {
389
407
type Reply = Result < ( ) , StartCapturingError > ;
@@ -410,15 +428,7 @@ impl Message<StartCapturing> for ScreenCaptureActor {
410
428
msg. target ,
411
429
msg. settings ,
412
430
move |frame| {
413
- let display_time = SystemTime :: now ( ) ;
414
-
415
- let _ = msg
416
- . frame_handler
417
- . tell ( NewFrame {
418
- frame,
419
- display_time,
420
- } )
421
- . try_send ( ) ;
431
+ let _ = msg. frame_handler . tell ( NewFrame ( frame) ) . try_send ( ) ;
422
432
423
433
Ok ( ( ) )
424
434
} ,
0 commit comments