1
1
use core:: fmt;
2
2
use core:: num:: NonZeroU16 ;
3
+ use core:: time:: Duration ;
4
+ use std:: collections:: HashMap ;
5
+ use std:: time:: Instant ;
3
6
4
7
use anyhow:: { Context , Result } ;
5
8
use ironrdp_acceptor:: DesktopSize ;
@@ -22,6 +25,8 @@ pub(crate) mod rfx;
22
25
23
26
pub ( crate ) use fast_path:: * ;
24
27
28
+ const VIDEO_HINT_FPS : usize = 5 ;
29
+
25
30
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
26
31
#[ repr( u8 ) ]
27
32
enum CodecId {
@@ -57,35 +62,42 @@ pub(crate) struct UpdateEncoder {
57
62
desktop_size : DesktopSize ,
58
63
framebuffer : Option < Framebuffer > ,
59
64
bitmap_updater : BitmapUpdater ,
65
+ video_updater : Option < BitmapUpdater > ,
66
+ region_update_times : HashMap < Rect , Vec < Instant > > ,
60
67
}
61
68
62
69
impl fmt:: Debug for UpdateEncoder {
63
70
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
64
71
f. debug_struct ( "UpdateEncoder" )
65
72
. field ( "bitmap_update" , & self . bitmap_updater )
73
+ . field ( "video_updater" , & self . video_updater )
66
74
. finish ( )
67
75
}
68
76
}
69
77
70
78
impl UpdateEncoder {
71
79
#[ cfg_attr( feature = "__bench" , visibility:: make( pub ) ) ]
72
80
pub ( crate ) fn new ( desktop_size : DesktopSize , surface_flags : CmdFlags , codecs : UpdateEncoderCodecs ) -> Self {
73
- let bitmap_updater = if surface_flags. contains ( CmdFlags :: SET_SURFACE_BITS ) {
81
+ let ( bitmap_updater, video_updater ) = if surface_flags. contains ( CmdFlags :: SET_SURFACE_BITS ) {
74
82
let mut bitmap = BitmapUpdater :: None ( NoneHandler ) ;
83
+ let mut video = None ;
75
84
76
85
if let Some ( ( algo, id) ) = codecs. remotefx {
77
86
bitmap = BitmapUpdater :: RemoteFx ( RemoteFxHandler :: new ( algo, id, desktop_size) ) ;
87
+ video = Some ( bitmap. clone ( ) ) ;
78
88
}
79
89
80
- bitmap
90
+ ( bitmap, video )
81
91
} else {
82
- BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) )
92
+ ( BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) ) , None )
83
93
} ;
84
94
85
95
Self {
86
96
desktop_size,
87
97
framebuffer : None ,
88
98
bitmap_updater,
99
+ video_updater,
100
+ region_update_times : HashMap :: new ( ) ,
89
101
}
90
102
}
91
103
@@ -152,6 +164,35 @@ impl UpdateEncoder {
152
164
Ok ( UpdateFragmenter :: new ( UpdateCode :: PositionPointer , encode_vec ( & pos) ?) )
153
165
}
154
166
167
+ // This is a very naive heuristic for detecting video regions
168
+ // based on the number of updates in the last second.
169
+ // Feel free to improve it! :)
170
+ fn diff_hints ( & mut self , now : Instant , off_x : usize , off_y : usize , regions : Vec < Rect > ) -> Vec < HintRect > {
171
+ // keep the updates from the last second
172
+ for ( _region, ts) in self . region_update_times . iter_mut ( ) {
173
+ ts. retain ( |ts| now - * ts < Duration :: from_millis ( 1000 ) ) ;
174
+ }
175
+ self . region_update_times . retain ( |_, times| !times. is_empty ( ) ) ;
176
+
177
+ let mut diffs = Vec :: new ( ) ;
178
+ for rect in regions {
179
+ let rect_root = rect. clone ( ) . add_xy ( off_x, off_y) ;
180
+ let entry = self . region_update_times . entry ( rect_root) . or_default ( ) ;
181
+ entry. push ( now) ;
182
+
183
+ let hint = if entry. len ( ) >= VIDEO_HINT_FPS {
184
+ HintType :: Video
185
+ } else {
186
+ HintType :: Image
187
+ } ;
188
+
189
+ let diff = HintRect :: new ( rect, hint) ;
190
+ diffs. push ( diff) ;
191
+ }
192
+
193
+ diffs
194
+ }
195
+
155
196
fn bitmap_diffs ( & mut self , bitmap : & BitmapUpdate ) -> Vec < Rect > {
156
197
const USE_DIFFS : bool = true ;
157
198
@@ -200,21 +241,46 @@ impl UpdateEncoder {
200
241
}
201
242
}
202
243
203
- async fn bitmap ( & mut self , bitmap : BitmapUpdate ) -> Result < UpdateFragmenter > {
244
+ async fn bitmap ( & mut self , bitmap : BitmapUpdate , hint : HintType ) -> Result < UpdateFragmenter > {
245
+ let updater = match hint {
246
+ HintType :: Image => & self . bitmap_updater ,
247
+ HintType :: Video => {
248
+ trace ! ( ?bitmap, "Encoding with video hint" ) ;
249
+ self . video_updater . as_ref ( ) . unwrap_or ( & self . bitmap_updater )
250
+ }
251
+ } ;
204
252
// Clone to satisfy spawn_blocking 'static requirement
205
253
// this should be cheap, even if using bitmap, since vec![] will be empty
206
- let mut updater = self . bitmap_updater . clone ( ) ;
254
+ let mut updater = updater . clone ( ) ;
207
255
tokio:: task:: spawn_blocking ( move || time_warn ! ( "Encoding bitmap" , 10 , updater. handle( & bitmap) ) )
208
256
. await
209
257
. unwrap ( )
210
258
}
211
259
}
212
260
261
+ #[ derive( Copy , Clone , Debug ) ]
262
+ enum HintType {
263
+ Image ,
264
+ Video ,
265
+ }
266
+
267
+ #[ derive( Debug ) ]
268
+ struct HintRect {
269
+ rect : Rect ,
270
+ hint : HintType ,
271
+ }
272
+
273
+ impl HintRect {
274
+ fn new ( rect : Rect , hint : HintType ) -> Self {
275
+ Self { rect, hint }
276
+ }
277
+ }
278
+
213
279
#[ derive( Debug , Default ) ]
214
280
enum State {
215
281
Start ( DisplayUpdate ) ,
216
282
BitmapDiffs {
217
- diffs : Vec < Rect > ,
283
+ diffs : Vec < HintRect > ,
218
284
bitmap : BitmapUpdate ,
219
285
pos : usize ,
220
286
} ,
@@ -239,6 +305,8 @@ impl EncoderIter<'_> {
239
305
State :: Start ( update) => match update {
240
306
DisplayUpdate :: Bitmap ( bitmap) => {
241
307
let diffs = encoder. bitmap_diffs ( & bitmap) ;
308
+ let diffs =
309
+ encoder. diff_hints ( Instant :: now ( ) , usize:: from ( bitmap. x ) , usize:: from ( bitmap. y ) , diffs) ;
242
310
self . state = State :: BitmapDiffs { diffs, bitmap, pos : 0 } ;
243
311
continue ;
244
312
}
@@ -250,12 +318,14 @@ impl EncoderIter<'_> {
250
318
DisplayUpdate :: Resize ( _) => return None ,
251
319
} ,
252
320
State :: BitmapDiffs { diffs, bitmap, pos } => {
253
- let Some ( rect) = diffs. get ( pos) else {
321
+ let Some ( diff) = diffs. get ( pos) else {
322
+ let diffs = diffs. into_iter ( ) . map ( |diff| diff. rect ) . collect :: < Vec < _ > > ( ) ;
254
323
encoder. bitmap_update_framebuffer ( bitmap, & diffs) ;
255
324
self . state = State :: Ended ;
256
325
return None ;
257
326
} ;
258
- let Rect { x, y, width, height } = * rect;
327
+
328
+ let Rect { x, y, width, height } = diff. rect ;
259
329
let Some ( sub) = bitmap. sub (
260
330
u16:: try_from ( x) . unwrap ( ) ,
261
331
u16:: try_from ( y) . unwrap ( ) ,
@@ -265,12 +335,13 @@ impl EncoderIter<'_> {
265
335
warn ! ( "Failed to extract bitmap subregion" ) ;
266
336
return None ;
267
337
} ;
338
+ let hint = diff. hint ;
268
339
self . state = State :: BitmapDiffs {
269
340
diffs,
270
341
bitmap,
271
342
pos : pos + 1 ,
272
343
} ;
273
- encoder. bitmap ( sub) . await
344
+ encoder. bitmap ( sub, hint ) . await
274
345
}
275
346
State :: Ended => return None ,
276
347
} ;
0 commit comments