mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-04-02 05:39:20 +00:00
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
This commit is contained in:
@@ -320,10 +320,37 @@ static bool COREMEDIA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
|
||||
|
||||
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 @@ static bool COREMEDIA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
|
||||
}
|
||||
|
||||
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 @@ static bool COREMEDIA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
|
||||
}
|
||||
[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];
|
||||
|
||||
Reference in New Issue
Block a user