11use core:: fmt;
22use core:: num:: NonZeroU16 ;
3+ use core:: time:: Duration ;
4+ use std:: collections:: HashMap ;
5+ use std:: time:: Instant ;
36
47use anyhow:: { Context , Result } ;
58use ironrdp_acceptor:: DesktopSize ;
@@ -22,6 +25,8 @@ pub(crate) mod rfx;
2225
2326pub ( crate ) use fast_path:: * ;
2427
28+ const VIDEO_HINT_FPS : usize = 5 ;
29+
2530#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
2631#[ repr( u8 ) ]
2732enum CodecId {
@@ -57,35 +62,42 @@ pub(crate) struct UpdateEncoder {
5762 desktop_size : DesktopSize ,
5863 framebuffer : Option < Framebuffer > ,
5964 bitmap_updater : BitmapUpdater ,
65+ video_updater : Option < BitmapUpdater > ,
66+ region_update_times : HashMap < Rect , Vec < Instant > > ,
6067}
6168
6269impl fmt:: Debug for UpdateEncoder {
6370 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
6471 f. debug_struct ( "UpdateEncoder" )
6572 . field ( "bitmap_update" , & self . bitmap_updater )
73+ . field ( "video_updater" , & self . video_updater )
6674 . finish ( )
6775 }
6876}
6977
7078impl UpdateEncoder {
7179 #[ cfg_attr( feature = "__bench" , visibility:: make( pub ) ) ]
7280 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 ) {
7482 let mut bitmap = BitmapUpdater :: None ( NoneHandler ) ;
83+ let mut video = None ;
7584
7685 if let Some ( ( algo, id) ) = codecs. remotefx {
7786 bitmap = BitmapUpdater :: RemoteFx ( RemoteFxHandler :: new ( algo, id, desktop_size) ) ;
87+ video = Some ( bitmap. clone ( ) ) ;
7888 }
7989
80- bitmap
90+ ( bitmap, video )
8191 } else {
82- BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) )
92+ ( BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) ) , None )
8393 } ;
8494
8595 Self {
8696 desktop_size,
8797 framebuffer : None ,
8898 bitmap_updater,
99+ video_updater,
100+ region_update_times : HashMap :: new ( ) ,
89101 }
90102 }
91103
@@ -152,6 +164,35 @@ impl UpdateEncoder {
152164 Ok ( UpdateFragmenter :: new ( UpdateCode :: PositionPointer , encode_vec ( & pos) ?) )
153165 }
154166
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+
155196 fn bitmap_diffs ( & mut self , bitmap : & BitmapUpdate ) -> Vec < Rect > {
156197 const USE_DIFFS : bool = true ;
157198
@@ -200,21 +241,46 @@ impl UpdateEncoder {
200241 }
201242 }
202243
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+ } ;
204252 // Clone to satisfy spawn_blocking 'static requirement
205253 // 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 ( ) ;
207255 tokio:: task:: spawn_blocking ( move || time_warn ! ( "Encoding bitmap" , 10 , updater. handle( & bitmap) ) )
208256 . await
209257 . unwrap ( )
210258 }
211259}
212260
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+
213279#[ derive( Debug , Default ) ]
214280enum State {
215281 Start ( DisplayUpdate ) ,
216282 BitmapDiffs {
217- diffs : Vec < Rect > ,
283+ diffs : Vec < HintRect > ,
218284 bitmap : BitmapUpdate ,
219285 pos : usize ,
220286 } ,
@@ -239,6 +305,8 @@ impl EncoderIter<'_> {
239305 State :: Start ( update) => match update {
240306 DisplayUpdate :: Bitmap ( bitmap) => {
241307 let diffs = encoder. bitmap_diffs ( & bitmap) ;
308+ let diffs =
309+ encoder. diff_hints ( Instant :: now ( ) , usize:: from ( bitmap. x ) , usize:: from ( bitmap. y ) , diffs) ;
242310 self . state = State :: BitmapDiffs { diffs, bitmap, pos : 0 } ;
243311 continue ;
244312 }
@@ -250,12 +318,14 @@ impl EncoderIter<'_> {
250318 DisplayUpdate :: Resize ( _) => return None ,
251319 } ,
252320 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 < _ > > ( ) ;
254323 encoder. bitmap_update_framebuffer ( bitmap, & diffs) ;
255324 self . state = State :: Ended ;
256325 return None ;
257326 } ;
258- let Rect { x, y, width, height } = * rect;
327+
328+ let Rect { x, y, width, height } = diff. rect ;
259329 let Some ( sub) = bitmap. sub (
260330 u16:: try_from ( x) . unwrap ( ) ,
261331 u16:: try_from ( y) . unwrap ( ) ,
@@ -265,12 +335,13 @@ impl EncoderIter<'_> {
265335 warn ! ( "Failed to extract bitmap subregion" ) ;
266336 return None ;
267337 } ;
338+ let hint = diff. hint ;
268339 self . state = State :: BitmapDiffs {
269340 diffs,
270341 bitmap,
271342 pos : pos + 1 ,
272343 } ;
273- encoder. bitmap ( sub) . await
344+ encoder. bitmap ( sub, hint ) . await
274345 }
275346 State :: Ended => return None ,
276347 } ;
0 commit comments