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 ;
@@ -23,6 +26,8 @@ pub(crate) mod rfx;
2326
2427pub ( crate ) use fast_path:: * ;
2528
29+ const VIDEO_HINT_FPS : usize = 5 ;
30+
2631#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
2732#[ repr( u8 ) ]
2833enum CodecId {
@@ -58,35 +63,42 @@ pub(crate) struct UpdateEncoder {
5863 desktop_size : DesktopSize ,
5964 framebuffer : Option < Framebuffer > ,
6065 bitmap_updater : BitmapUpdater ,
66+ video_updater : Option < BitmapUpdater > ,
67+ region_update_times : HashMap < Rect , Vec < Instant > > ,
6168}
6269
6370impl fmt:: Debug for UpdateEncoder {
6471 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
6572 f. debug_struct ( "UpdateEncoder" )
6673 . field ( "bitmap_update" , & self . bitmap_updater )
74+ . field ( "video_updater" , & self . video_updater )
6775 . finish ( )
6876 }
6977}
7078
7179impl UpdateEncoder {
7280 #[ cfg_attr( feature = "__bench" , visibility:: make( pub ) ) ]
7381 pub ( crate ) fn new ( desktop_size : DesktopSize , surface_flags : CmdFlags , codecs : UpdateEncoderCodecs ) -> Self {
74- let bitmap_updater = if surface_flags. contains ( CmdFlags :: SET_SURFACE_BITS ) {
82+ let ( bitmap_updater, video_updater ) = if surface_flags. contains ( CmdFlags :: SET_SURFACE_BITS ) {
7583 let mut bitmap = BitmapUpdater :: None ( NoneHandler ) ;
84+ let mut video = None ;
7685
7786 if let Some ( ( algo, id) ) = codecs. remotefx {
7887 bitmap = BitmapUpdater :: RemoteFx ( RemoteFxHandler :: new ( algo, id, desktop_size) ) ;
88+ video = Some ( bitmap. clone ( ) ) ;
7989 }
8090
81- bitmap
91+ ( bitmap, video )
8292 } else {
83- BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) )
93+ ( BitmapUpdater :: Bitmap ( BitmapHandler :: new ( ) ) , None )
8494 } ;
8595
8696 Self {
8797 desktop_size,
8898 framebuffer : None ,
8999 bitmap_updater,
100+ video_updater,
101+ region_update_times : HashMap :: new ( ) ,
90102 }
91103 }
92104
@@ -153,6 +165,35 @@ impl UpdateEncoder {
153165 Ok ( UpdateFragmenter :: new ( UpdateCode :: PositionPointer , encode_vec ( & pos) ?) )
154166 }
155167
168+ // This is a very naive heuristic for detecting video regions
169+ // based on the number of updates in the last second.
170+ // Feel free to improve it! :)
171+ fn diff_hints ( & mut self , now : Instant , off_x : usize , off_y : usize , regions : Vec < Rect > ) -> Vec < HintRect > {
172+ // keep the updates from the last second
173+ for ( _region, ts) in self . region_update_times . iter_mut ( ) {
174+ ts. retain ( |ts| now - * ts < Duration :: from_millis ( 1000 ) ) ;
175+ }
176+ self . region_update_times . retain ( |_, times| !times. is_empty ( ) ) ;
177+
178+ let mut diffs = Vec :: new ( ) ;
179+ for rect in regions {
180+ let rect_root = rect. clone ( ) . add_xy ( off_x, off_y) ;
181+ let entry = self . region_update_times . entry ( rect_root) . or_default ( ) ;
182+ entry. push ( now) ;
183+
184+ let hint = if entry. len ( ) >= VIDEO_HINT_FPS {
185+ HintType :: Video
186+ } else {
187+ HintType :: Image
188+ } ;
189+
190+ let diff = HintRect :: new ( rect, hint) ;
191+ diffs. push ( diff) ;
192+ }
193+
194+ diffs
195+ }
196+
156197 fn bitmap_diffs ( & mut self , bitmap : & BitmapUpdate ) -> Vec < Rect > {
157198 // TODO: we may want to make it optional for servers that already provide damaged regions
158199 const USE_DIFFS : bool = true ;
@@ -202,21 +243,46 @@ impl UpdateEncoder {
202243 }
203244 }
204245
205- async fn bitmap ( & mut self , bitmap : BitmapUpdate ) -> Result < UpdateFragmenter > {
246+ async fn bitmap ( & mut self , bitmap : BitmapUpdate , hint : HintType ) -> Result < UpdateFragmenter > {
247+ let updater = match hint {
248+ HintType :: Image => & self . bitmap_updater ,
249+ HintType :: Video => {
250+ trace ! ( ?bitmap, "Encoding with video hint" ) ;
251+ self . video_updater . as_ref ( ) . unwrap_or ( & self . bitmap_updater )
252+ }
253+ } ;
206254 // Clone to satisfy spawn_blocking 'static requirement
207255 // this should be cheap, even if using bitmap, since vec![] will be empty
208- let mut updater = self . bitmap_updater . clone ( ) ;
256+ let mut updater = updater . clone ( ) ;
209257 tokio:: task:: spawn_blocking ( move || time_warn ! ( "Encoding bitmap" , 10 , updater. handle( & bitmap) ) )
210258 . await
211259 . unwrap ( )
212260 }
213261}
214262
263+ #[ derive( Copy , Clone , Debug ) ]
264+ enum HintType {
265+ Image ,
266+ Video ,
267+ }
268+
269+ #[ derive( Debug ) ]
270+ struct HintRect {
271+ rect : Rect ,
272+ hint : HintType ,
273+ }
274+
275+ impl HintRect {
276+ fn new ( rect : Rect , hint : HintType ) -> Self {
277+ Self { rect, hint }
278+ }
279+ }
280+
215281#[ derive( Debug , Default ) ]
216282enum State {
217283 Start ( DisplayUpdate ) ,
218284 BitmapDiffs {
219- diffs : Vec < Rect > ,
285+ diffs : Vec < HintRect > ,
220286 bitmap : BitmapUpdate ,
221287 pos : usize ,
222288 } ,
@@ -241,6 +307,8 @@ impl EncoderIter<'_> {
241307 State :: Start ( update) => match update {
242308 DisplayUpdate :: Bitmap ( bitmap) => {
243309 let diffs = encoder. bitmap_diffs ( & bitmap) ;
310+ let diffs =
311+ encoder. diff_hints ( Instant :: now ( ) , usize:: from ( bitmap. x ) , usize:: from ( bitmap. y ) , diffs) ;
244312 self . state = State :: BitmapDiffs { diffs, bitmap, pos : 0 } ;
245313 continue ;
246314 }
@@ -252,12 +320,14 @@ impl EncoderIter<'_> {
252320 DisplayUpdate :: Resize ( _) => return None ,
253321 } ,
254322 State :: BitmapDiffs { diffs, bitmap, pos } => {
255- let Some ( rect) = diffs. get ( pos) else {
323+ let Some ( diff) = diffs. get ( pos) else {
324+ let diffs = diffs. into_iter ( ) . map ( |diff| diff. rect ) . collect :: < Vec < _ > > ( ) ;
256325 encoder. bitmap_update_framebuffer ( bitmap, & diffs) ;
257326 self . state = State :: Ended ;
258327 return None ;
259328 } ;
260- let Rect { x, y, width, height } = * rect;
329+
330+ let Rect { x, y, width, height } = diff. rect ;
261331 let Some ( sub) = bitmap. sub (
262332 u16:: try_from ( x) . unwrap ( ) ,
263333 u16:: try_from ( y) . unwrap ( ) ,
@@ -267,12 +337,13 @@ impl EncoderIter<'_> {
267337 warn ! ( "Failed to extract bitmap subregion" ) ;
268338 return None ;
269339 } ;
340+ let hint = diff. hint ;
270341 self . state = State :: BitmapDiffs {
271342 diffs,
272343 bitmap,
273344 pos : pos + 1 ,
274345 } ;
275- encoder. bitmap ( sub) . await
346+ encoder. bitmap ( sub, hint ) . await
276347 }
277348 State :: Ended => return None ,
278349 } ;
0 commit comments