From 745f87f21ff6a1fca2d2dbf909b93a752736eab7 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sat, 24 Feb 2024 03:33:04 +0100 Subject: [PATCH] mac-avcapture: Fix frame rate collection for camera device formats Some devices will report different framerate ranges for formats that are identical apart from color primaries. Without taking these into account, only framerates for one color primary variant would be used to populate the framerate dropdown in the property view of the camera source. Checking for a difference in color primaries when iterating over all available formats for a device thus requires checking for this variation and adding the additional frame rate range as well. --- plugins/mac-avcapture/OBSAVCapture.m | 1 + plugins/mac-avcapture/plugin-properties.m | 53 ++++++++++++++--------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/plugins/mac-avcapture/OBSAVCapture.m b/plugins/mac-avcapture/OBSAVCapture.m index df1a03e6cf5b69..ba8507ad30b8dd 100644 --- a/plugins/mac-avcapture/OBSAVCapture.m +++ b/plugins/mac-avcapture/OBSAVCapture.m @@ -197,6 +197,7 @@ - (BOOL)switchCaptureDevice:(NSString *)uuid withError:(NSError *__autoreleasing [self.deviceInput.device unlockForConfiguration]; self.deviceInput = nil; self.isDeviceLocked = NO; + self.presetFormat = nil; } if (!device) { diff --git a/plugins/mac-avcapture/plugin-properties.m b/plugins/mac-avcapture/plugin-properties.m index 4aed2eda980293..8bd37bbea1951d 100644 --- a/plugins/mac-avcapture/plugin-properties.m +++ b/plugins/mac-avcapture/plugin-properties.m @@ -338,6 +338,8 @@ bool properties_update_config(OBSAVCapture *capture, obs_properties_t *propertie BOOL hasFoundColorSpace = capture.isFastPath; BOOL hasFoundVideoRange = capture.isFastPath; + CFPropertyListRef priorColorPrimary = @""; + if (device) { // Iterate over all formats reported by the device and gather them for property lists for (AVCaptureDeviceFormat *format in device.formats) { @@ -419,29 +421,38 @@ bool properties_update_config(OBSAVCapture *capture, obs_properties_t *propertie } // Only iterate over available framerates if input format, color space, and resolution are matching - if (hasFoundInputFormat && hasFoundColorSpace && hasFoundResolution && !hasFoundFramerate) { - for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges.reverseObjectEnumerator) { - FourCharCode formatSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription); - int device_format = [OBSAVCapture formatFromSubtype:formatSubType]; - - if (input_format == device_format) { - struct media_frames_per_second min_fps = { - .numerator = (uint32_t) clamp_Uint(range.maxFrameDuration.timescale, 0, UINT32_MAX), - .denominator = (uint32_t) clamp_Uint(range.maxFrameDuration.value, 0, UINT32_MAX)}; - struct media_frames_per_second max_fps = { - .numerator = (uint32_t) clamp_Uint(range.minFrameDuration.timescale, 0, UINT32_MAX), - .denominator = (uint32_t) clamp_Uint(range.minFrameDuration.value, 0, UINT32_MAX)}; - - if (![frameRates containsObject:range]) { - obs_property_frame_rate_fps_range_add(prop_framerate, min_fps, max_fps); - [frameRates addObject:range]; - } - - if (!hasFoundFramerate && CMTimeCompare(range.maxFrameDuration, time) >= 0 && - CMTimeCompare(range.minFrameDuration, time) <= 0) { - hasFoundFramerate = YES; + if (hasFoundInputFormat && hasFoundColorSpace && hasFoundResolution) { + CFPropertyListRef colorPrimary = CMFormatDescriptionGetExtension( + format.formatDescription, kCMFormatDescriptionExtension_ColorPrimaries); + + CFComparisonResult isColorPrimaryMatch = CFStringCompare(colorPrimary, priorColorPrimary, 0); + + if (isColorPrimaryMatch != kCFCompareEqualTo || !hasFoundFramerate) { + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges.reverseObjectEnumerator) { + FourCharCode formatSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription); + int device_format = [OBSAVCapture formatFromSubtype:formatSubType]; + + if (input_format == device_format) { + struct media_frames_per_second min_fps = { + .numerator = (uint32_t) clamp_Uint(range.maxFrameDuration.timescale, 0, UINT32_MAX), + .denominator = (uint32_t) clamp_Uint(range.maxFrameDuration.value, 0, UINT32_MAX)}; + struct media_frames_per_second max_fps = { + .numerator = (uint32_t) clamp_Uint(range.minFrameDuration.timescale, 0, UINT32_MAX), + .denominator = (uint32_t) clamp_Uint(range.minFrameDuration.value, 0, UINT32_MAX)}; + + if (![frameRates containsObject:range]) { + obs_property_frame_rate_fps_range_add(prop_framerate, min_fps, max_fps); + [frameRates addObject:range]; + } + + if (!hasFoundFramerate && CMTimeCompare(range.maxFrameDuration, time) >= 0 && + CMTimeCompare(range.minFrameDuration, time) <= 0) { + hasFoundFramerate = YES; + } } } + + priorColorPrimary = colorPrimary; } } }