mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-28 03:26:15 +08: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;
|
const float FRAMERATE_EPSILON = 0.01f;
|
||||||
for (AVFrameRateRange *framerate in format.videoSupportedFrameRateRanges) {
|
for (AVFrameRateRange *framerate in format.videoSupportedFrameRateRanges) {
|
||||||
if (rate > (framerate.minFrameRate - FRAMERATE_EPSILON) &&
|
// Check if the requested rate is within the supported range
|
||||||
rate < (framerate.maxFrameRate + FRAMERATE_EPSILON)) {
|
if (rate >= (framerate.minFrameRate - FRAMERATE_EPSILON) &&
|
||||||
spec_format = format;
|
rate <= (framerate.maxFrameRate + FRAMERATE_EPSILON)) {
|
||||||
break;
|
|
||||||
|
// 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;
|
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];
|
[avdevice unlockForConfiguration];
|
||||||
|
|
||||||
AVCaptureSession *session = [[AVCaptureSession alloc] init];
|
AVCaptureSession *session = [[AVCaptureSession alloc] init];
|
||||||
@@ -394,6 +437,15 @@ static bool COREMEDIA_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
|
|||||||
}
|
}
|
||||||
[session addOutput:output];
|
[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];
|
[session commitConfiguration];
|
||||||
|
|
||||||
SDLPrivateCameraData *hidden = [[SDLPrivateCameraData alloc] init];
|
SDLPrivateCameraData *hidden = [[SDLPrivateCameraData alloc] init];
|
||||||
|
|||||||
Reference in New Issue
Block a user