@@ -205,15 +205,18 @@ async fn create_pipeline(
205205 let screen_info = screen_source. info ( ) ;
206206
207207 let output_resolution = max_output_size
208- . map ( |max_size| {
209- // restrict width if more square-y and w > h, or excessively tall
210- // we don't just uniformly restrict width or height as this massively squashes ultrawide/ultratall captures
208+ . map ( |max_output_width| {
209+ (
210+ max_output_width,
211+ ( max_output_width as f64 / 16.0 * 9.0 ) as u32 ,
212+ )
213+ } )
214+ . map ( |( max_width, max_height) | {
215+ // 16/9-ish
211216 if screen_info. width >= screen_info. height
212- && ( screen_info. width as f64 / screen_info. height as f64 ) <= 16.9
213- || ( screen_info. width < screen_info. height
214- && ( screen_info. width as f64 / screen_info. height as f64 ) >= 16.9 )
217+ && ( screen_info. width as f64 / screen_info. height as f64 ) <= 16.0 / 9.0
215218 {
216- let mut width = max_size . min ( screen_info. width ) ;
219+ let mut width = max_width . min ( screen_info. width ) ;
217220 if width % 2 != 0 {
218221 width -= 1 ;
219222 }
@@ -225,8 +228,29 @@ async fn create_pipeline(
225228 }
226229
227230 ( width, height)
228- } else {
229- let mut height = max_size. min ( screen_info. height ) ;
231+ }
232+ // 9/16-ish
233+ else if screen_info. width <= screen_info. height
234+ && ( screen_info. width as f64 / screen_info. height as f64 ) >= 9.0 / 16.0
235+ {
236+ let mut height = max_height. min ( screen_info. height ) ;
237+ if height % 2 != 0 {
238+ height -= 1 ;
239+ }
240+
241+ let width_ratio = screen_info. width as f64 / screen_info. height as f64 ;
242+ let mut width = ( width_ratio * height as f64 ) . round ( ) as u32 ;
243+ if width % 2 != 0 {
244+ width -= 1 ;
245+ }
246+
247+ ( width, height)
248+ }
249+ // ultrawide
250+ else if screen_info. width >= screen_info. height
251+ && ( screen_info. width as f64 / screen_info. height as f64 ) > 16.0 / 9.0
252+ {
253+ let mut height = max_height. min ( screen_info. height ) ;
230254 if height % 2 != 0 {
231255 height -= 1 ;
232256 }
@@ -239,6 +263,26 @@ async fn create_pipeline(
239263
240264 ( width, height)
241265 }
266+ // ultratall
267+ else if screen_info. width < screen_info. height
268+ && ( screen_info. width as f64 / screen_info. height as f64 ) <= 9.0 / 16.0
269+ {
270+ // swapped since max_width/height assume horizontal
271+ let mut width = max_height. min ( screen_info. width ) ;
272+ if width % 2 != 0 {
273+ width -= 1 ;
274+ }
275+
276+ let height_ratio = screen_info. height as f64 / screen_info. width as f64 ;
277+ let mut height = ( height_ratio * width as f64 ) . round ( ) as u32 ;
278+ if height % 2 != 0 {
279+ height -= 1 ;
280+ }
281+
282+ ( width, height)
283+ } else {
284+ unreachable ! ( )
285+ }
242286 } )
243287 . unwrap_or ( ( screen_info. width , screen_info. height ) ) ;
244288
@@ -423,3 +467,175 @@ fn current_time_f64() -> f64 {
423467 . unwrap ( )
424468 . as_secs_f64 ( )
425469}
470+
471+ fn clamp_size ( input : ( u32 , u32 ) , max : ( u32 , u32 ) ) -> ( u32 , u32 ) {
472+ // 16/9-ish
473+ if input. 0 >= input. 1 && ( input. 0 as f64 / input. 1 as f64 ) <= 16.0 / 9.0 {
474+ let mut width = max. 0 . min ( input. 0 ) ;
475+ if width % 2 != 0 {
476+ width -= 1 ;
477+ }
478+
479+ let height_ratio = input. 1 as f64 / input. 0 as f64 ;
480+ let mut height = ( height_ratio * width as f64 ) . round ( ) as u32 ;
481+ if height % 2 != 0 {
482+ height -= 1 ;
483+ }
484+
485+ ( width, height)
486+ }
487+ // 9/16-ish
488+ else if input. 0 <= input. 1 && ( input. 0 as f64 / input. 1 as f64 ) >= 9.0 / 16.0 {
489+ let mut height = max. 0 . min ( input. 1 ) ;
490+ if height % 2 != 0 {
491+ height -= 1 ;
492+ }
493+
494+ let width_ratio = input. 0 as f64 / input. 1 as f64 ;
495+ let mut width = ( width_ratio * height as f64 ) . round ( ) as u32 ;
496+ if width % 2 != 0 {
497+ width -= 1 ;
498+ }
499+
500+ ( width, height)
501+ }
502+ // ultrawide
503+ else if input. 0 >= input. 1 && ( input. 0 as f64 / input. 1 as f64 ) > 16.0 / 9.0 {
504+ let mut height = max. 1 . min ( input. 1 ) ;
505+ if height % 2 != 0 {
506+ height -= 1 ;
507+ }
508+
509+ let width_ratio = input. 0 as f64 / input. 1 as f64 ;
510+ let mut width = ( width_ratio * height as f64 ) . round ( ) as u32 ;
511+ if width % 2 != 0 {
512+ width -= 1 ;
513+ }
514+
515+ ( width, height)
516+ }
517+ // ultratall
518+ else if input. 0 < input. 1 && ( input. 0 as f64 / input. 1 as f64 ) <= 9.0 / 16.0 {
519+ // swapped since max_width/height assume horizontal
520+ let mut width = max. 1 . min ( input. 0 ) ;
521+ if width % 2 != 0 {
522+ width -= 1 ;
523+ }
524+
525+ let height_ratio = input. 1 as f64 / input. 0 as f64 ;
526+ let mut height = ( height_ratio * width as f64 ) . round ( ) as u32 ;
527+ if height % 2 != 0 {
528+ height -= 1 ;
529+ }
530+
531+ ( width, height)
532+ } else {
533+ unreachable ! ( )
534+ }
535+ }
536+
537+ #[ cfg( test) ]
538+ mod tests {
539+ use super :: * ;
540+
541+ #[ test]
542+ fn test_clamp_size_16_9_ish_landscape ( ) {
543+ // Test 16:9 aspect ratio (boundary case)
544+ let result = clamp_size ( ( 1920 , 1080 ) , ( 1920 , 1080 ) ) ;
545+ assert_eq ! ( result, ( 1920 , 1080 ) ) ;
546+
547+ // Test aspect ratio less than 16:9 (wider than tall, but not ultrawide)
548+ let result = clamp_size ( ( 1600 , 1200 ) , ( 1920 , 1080 ) ) ; // 4:3 ratio
549+ assert_eq ! ( result, ( 1600 , 1200 ) ) ;
550+
551+ // Test scaling down when input exceeds max width
552+ let result = clamp_size ( ( 2560 , 1440 ) , ( 1920 , 1080 ) ) ; // 16:9 ratio, needs scaling
553+ assert_eq ! ( result, ( 1920 , 1080 ) ) ;
554+ }
555+
556+ #[ test]
557+ fn test_clamp_size_9_16_ish_portrait ( ) {
558+ // Test 9:16 aspect ratio (boundary case)
559+ let result = clamp_size ( ( 1080 , 1920 ) , ( 1920 , 1080 ) ) ;
560+ assert_eq ! ( result, ( 1080 , 1920 ) ) ;
561+
562+ // Test aspect ratio greater than 9:16 but still portrait
563+ let result = clamp_size ( ( 1200 , 1600 ) , ( 1920 , 1080 ) ) ; // 3:4 ratio
564+ assert_eq ! ( result, ( 1200 , 1600 ) ) ;
565+
566+ // Test square format (1:1 ratio) - should use portrait path when width <= height
567+ let result = clamp_size ( ( 1080 , 1080 ) , ( 1920 , 1080 ) ) ;
568+ assert_eq ! ( result, ( 1080 , 1080 ) ) ;
569+ }
570+
571+ #[ test]
572+ fn test_clamp_size_ultrawide ( ) {
573+ // Test ultrawide aspect ratio (> 16:9)
574+ let result = clamp_size ( ( 2560 , 1080 ) , ( 1920 , 1080 ) ) ; // ~2.37:1 ratio
575+ assert_eq ! ( result, ( 2560 , 1080 ) ) ;
576+
577+ // Test very ultrawide
578+ let result = clamp_size ( ( 3440 , 1440 ) , ( 1920 , 1080 ) ) ; // ~2.39:1 ratio
579+ assert_eq ! ( result, ( 2580 , 1080 ) ) ;
580+
581+ // Test when height constraint is the limiting factor
582+ let result = clamp_size ( ( 3840 , 1600 ) , ( 1920 , 1080 ) ) ; // 2.4:1 ratio
583+ assert_eq ! ( result, ( 2592 , 1080 ) ) ;
584+
585+ // Test even number enforcement for height
586+ let result = clamp_size ( ( 2561 , 1080 ) , ( 1920 , 1081 ) ) ; // Odd max height
587+ assert_eq ! ( result, ( 2560 , 1080 ) ) ; // Height should be made even
588+
589+ // Test even number enforcement for calculated width
590+ let result = clamp_size ( ( 2561 , 1080 ) , ( 1920 , 1080 ) ) ; // Results in odd width calculation
591+ assert_eq ! ( result, ( 2560 , 1080 ) ) ; // Width should be made even
592+ }
593+
594+ #[ test]
595+ fn test_clamp_size_ultratall ( ) {
596+ // Test ultratall aspect ratio (< 9:16)
597+ let result = clamp_size ( ( 1080 , 2560 ) , ( 1920 , 1920 ) ) ; // ~9:21.3 ratio
598+ assert_eq ! ( result, ( 1080 , 2560 ) ) ;
599+
600+ // Test very ultratall that needs scaling
601+ let result = clamp_size ( ( 800 , 3200 ) , ( 1920 , 2000 ) ) ; // 1:4 ratio
602+ assert_eq ! ( result, ( 800 , 3200 ) ) ;
603+
604+ // Test when width constraint is the limiting factor (using max.1 as width limit)
605+ let result = clamp_size ( ( 500 , 3000 ) , ( 1920 , 1000 ) ) ; // Very tall, width limited by max.1
606+ assert_eq ! ( result, ( 500 , 3000 ) ) ;
607+
608+ // Test even number enforcement for width (using max.1)
609+ let result = clamp_size ( ( 500 , 3000 ) , ( 1920 , 1001 ) ) ; // Odd max.1 used as width
610+ assert_eq ! ( result, ( 500 , 3000 ) ) ; // Width should be made even
611+
612+ // Test even number enforcement for calculated height
613+ let result = clamp_size ( ( 500 , 3000 ) , ( 1920 , 1000 ) ) ; // Results in odd height calculation
614+ assert_eq ! ( result, ( 500 , 3000 ) ) ; // Height should be made even
615+ }
616+
617+ #[ test]
618+ fn test_clamp_size_edge_cases ( ) {
619+ // Test minimum sizes
620+ let result = clamp_size ( ( 2 , 2 ) , ( 1920 , 1080 ) ) ;
621+ assert_eq ! ( result, ( 2 , 2 ) ) ;
622+
623+ // Test when input is smaller than max in all dimensions
624+ let result = clamp_size ( ( 800 , 600 ) , ( 1920 , 1080 ) ) ;
625+ assert_eq ! ( result, ( 800 , 600 ) ) ;
626+
627+ // Test exact 16:9 boundary
628+ let sixteen_nine = 16.0 / 9.0 ;
629+ let width = 1920 ;
630+ let height = ( width as f64 / sixteen_nine) as u32 ; // Should be exactly 1080
631+ let result = clamp_size ( ( width, height) , ( 1920 , 1080 ) ) ;
632+ assert_eq ! ( result, ( 1920 , 1080 ) ) ;
633+
634+ // Test exact 9:16 boundary
635+ let nine_sixteen = 9.0 / 16.0 ;
636+ let height = 1920 ;
637+ let width = ( height as f64 * nine_sixteen) as u32 ; // Should be exactly 1080
638+ let result = clamp_size ( ( width, height) , ( 1920 , 1080 ) ) ;
639+ assert_eq ! ( result, ( 1080 , 1920 ) ) ;
640+ }
641+ }
0 commit comments