diff --git a/lib/src/types.dart b/lib/src/types.dart index d6fcf4f..a11fed4 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -159,6 +159,9 @@ class VideoDimensions { /// Returns the larger value int max() => math.max(width, height); + /// Returns the smaller value + int min() => math.min(width, height); + VideoDimensions copyWith({ int? width, int? height, diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 8bc4264..7645927 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -231,18 +231,42 @@ class Utils { if (i >= videoRids.length) { return; } - final size = dimensions.max(); - final encodingSize = e.dimensions.max(); final rid = videoRids[i]; result.add(e.encoding.toRTCRtpEncoding( rid: rid, - scaleResolutionDownBy: size / encodingSize, + scaleResolutionDownBy: findEvenScaleDownBy(dimensions, e.dimensions), )); }); return result; } + @internal + static double findEvenScaleDownBy( + VideoDimensions sourceDimensions, + VideoDimensions targetDimensions, + ) { + bool isEven(int v) => v % 2 == 0; + + final sourceSize = sourceDimensions.max(); + final targetSize = targetDimensions.max(); + + for (int i = 0; i <= 30; i++) { + final scaleDownBy = sourceSize.toDouble() / (targetSize + i); + // Internally, WebRTC casts directly to int without rounding. + // https://github.com/webrtc-sdk/webrtc/blob/8c7139f8e6fa19ddf2c91510c177a19746e1ded3/media/engine/webrtc_video_engine.cc#L3676 + final scaledWidth = sourceDimensions.width ~/ scaleDownBy; + final scaledHeight = sourceDimensions.height ~/ scaleDownBy; + + if (isEven(scaledWidth) && isEven(scaledHeight)) { + return scaleDownBy; + } + } + + // couldn't find an even scale, just return original scale and hope it works. + return sourceSize / targetSize; + } + @internal static List? computeVideoEncodings({ required bool isScreenShare,