Improve camera frame rate matching on macOS

- Prefer formats with narrower frame rate ranges
- Set frame duration on both device and connection
  for better enforcement
diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m
index f02c4ac..308bbbf 100644
--- a/src/camera/coremedia/SDL_camera_coremedia.m
+++ b/src/camera/coremedia/SDL_camera_coremedia.m
@@ -320,10 +320,37 @@
 
         const float FRAMERATE_EPSILON = 0.01f;
         for (AVFrameRateRange *framerate in format.videoSupportedFrameRateRanges) {
-            if (rate > (framerate.minFrameRate - FRAMERATE_EPSILON) &&
-                rate < (framerate.maxFrameRate + FRAMERATE_EPSILON)) {
-                spec_format = format;
-                break;
+            // Check if the requested rate is within the supported range
+            if (rate >= (framerate.minFrameRate - FRAMERATE_EPSILON) &&
+                rate <= (framerate.maxFrameRate + FRAMERATE_EPSILON)) {
+
+                // Prefer formats with narrower frame rate ranges that are closer to our target
+                // This helps avoid formats that support a wide range (like 10-60 FPS)
+                // when we want a specific rate (like 30 FPS)
+                bool should_select = false;
+                if (spec_format == nil) {
+                    should_select = true;
+                } else {
+                    AVFrameRateRange *current_range = spec_format.videoSupportedFrameRateRanges.firstObject;
+                    float current_range_width = current_range.maxFrameRate - current_range.minFrameRate;
+                    float new_range_width = framerate.maxFrameRate - framerate.minFrameRate;
+
+                    // Prefer formats with narrower ranges, or if ranges are similar, prefer closer to target
+                    if (new_range_width < current_range_width) {
+                        should_select = true;
+                    } else if (SDL_fabsf(new_range_width - current_range_width) < 0.1f) {
+                        // Similar range width, prefer the one closer to our target rate
+                        float current_distance = SDL_fabsf(rate - current_range.minFrameRate);
+                        float new_distance = SDL_fabsf(rate - framerate.minFrameRate);
+                        if (new_distance < current_distance) {
+                            should_select = true;
+                        }
+                    }
+                }
+
+                if (should_select) {
+                    spec_format = format;
+                }
             }
         }
 
@@ -339,6 +366,22 @@
     }
 
     avdevice.activeFormat = spec_format;
+
+    // Try to set the frame duration to enforce the requested frame rate
+    const float frameRate = (float)spec->framerate_numerator / spec->framerate_denominator;
+    const CMTime frameDuration = CMTimeMake(1, (int32_t)frameRate);
+
+    // Check if the device supports setting frame duration
+    if ([avdevice respondsToSelector:@selector(setActiveVideoMinFrameDuration:)] &&
+        [avdevice respondsToSelector:@selector(setActiveVideoMaxFrameDuration:)]) {
+        @try {
+            avdevice.activeVideoMinFrameDuration = frameDuration;
+            avdevice.activeVideoMaxFrameDuration = frameDuration;
+        } @catch (NSException *exception) {
+            // Some devices don't support setting frame duration, that's okay
+        }
+    }
+
     [avdevice unlockForConfiguration];
 
     AVCaptureSession *session = [[AVCaptureSession alloc] init];
@@ -394,6 +437,15 @@
     }
     [session addOutput:output];
 
+    // Try to set the frame rate on the connection
+    AVCaptureConnection *connection = [output connectionWithMediaType:AVMediaTypeVideo];
+    if (connection && connection.isVideoMinFrameDurationSupported) {
+        connection.videoMinFrameDuration = frameDuration;
+        if (connection.isVideoMaxFrameDurationSupported) {
+            connection.videoMaxFrameDuration = frameDuration;
+        }
+    }
+
     [session commitConfiguration];
 
     SDLPrivateCameraData *hidden = [[SDLPrivateCameraData alloc] init];