Skip to content

Commit

Permalink
add FPS fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
jcm93 committed Jan 22, 2025
1 parent 3be5eaa commit 64be9fc
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
12 changes: 12 additions & 0 deletions plugins/mac-avcapture/OBSAVCapture.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ typedef struct obs_source_frame OBSAVCaptureVideoFrame;
typedef struct obs_source_audio OBSAVCaptureAudioFrame;
typedef struct gs_texture OBSAVCaptureTexture;
typedef struct gs_effect OBSAVCaptureEffect;
typedef struct media_frames_per_second OBSAVCaptureMediaFPS;

/// C struct for errors encountered in capture callback
typedef enum : NSUInteger {
Expand Down Expand Up @@ -152,6 +153,17 @@ typedef struct av_capture_info {
/// - Returns: [CMVideoDimensions](https://developer.apple.com/documentation/coremedia/cmvideodimensions?language=objc) struct with resolution from user settings
+ (CMVideoDimensions)legacyDimensionsFromSettings:(void *)settings;

/// Generates an appropriate frame rate value to fall back to (based on OBS's configured output framerate) when the user selects a format that does not support their previously configured frame rate.
///
/// This function fetches OBS's configured output frame rate and uses it to determine the appropriate default frame rate supported by the format. It will return:
///
/// * The frame rate nearest up to and including OBS's configured output frame rate.
/// * If that does not exist on the format, the frame rate nearest above OBS's configured output frame rate.
/// * If that does not exist, a struct representing an invalid frame rate.
/// - Parameter format: [AVCaptureDeviceFormat](https://developer.apple.com/documentation/avfoundation/avcapturedevice/format?language=objc) instance that we are determining a fallback FPS for.
/// - Returns: Struct representing a frames per second value as defined in ``libobs``.
+ (OBSAVCaptureMediaFPS)fallbackFrameRateForFormat:(AVCaptureDeviceFormat *)format;

/// Generates a new [NSString](https://developer.apple.com/documentation/foundation/nsstring?language=objc) instance containing a human-readable aspect ratio for a given pixel width and height.
/// - Parameter dimensions: [CMVideoDimensions](https://developer.apple.com/documentation/coremedia/cmvideodimensions?language=objc) struct containing the width and height in pixels.
/// - Returns: New [NSString](https://developer.apple.com/documentation/foundation/nsstring?language=objc) instance containing the aspect ratio description.
Expand Down
53 changes: 49 additions & 4 deletions plugins/mac-avcapture/OBSAVCapture.m
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ - (BOOL)configureSessionWithPreset:(AVCaptureSessionPreset)preset withError:(NSE

- (BOOL)configureSession:(NSError *__autoreleasing *)error
{
struct media_frames_per_second fps;
OBSAVCaptureMediaFPS fps;
if (!obs_data_get_frames_per_second(self.captureInfo->settings, "frame_rate", &fps, NULL)) {
[self AVCaptureLog:LOG_DEBUG withFormat:@"No valid framerate found in settings"];
return NO;
Expand Down Expand Up @@ -477,9 +477,23 @@ - (BOOL)configureSession:(NSError *__autoreleasing *)error
}

if (!fpsSupported) {
[self AVCaptureLog:LOG_WARNING withFormat:@"Frame rate is not supported: %g FPS (%u/%u)",
media_frames_per_second_to_fps(fps), fps.numerator, fps.denominator];
return NO;
OBSAVCaptureMediaFPS fallbackFPS = [OBSAVCapture fallbackFrameRateForFormat:format];
if (fallbackFPS.denominator > 0 && fallbackFPS.numerator > 0) {
[self AVCaptureLog:LOG_WARNING withFormat:@"Frame rate is not supported: %g FPS (%u/%u), \n"
" falling back to value supported by device: %G FPS (%u/%u)",
media_frames_per_second_to_fps(fps), fps.numerator,
fps.denominator, media_frames_per_second_to_fps(fallbackFPS),
fallbackFPS.numerator, fallbackFPS.denominator];
obs_data_set_frames_per_second(self.captureInfo->settings, "frame_rate", fallbackFPS, NULL);
time.value = fallbackFPS.denominator;
time.timescale = fallbackFPS.numerator;
} else {
[self AVCaptureLog:LOG_WARNING
withFormat:@"Frame rate is not supported: %g FPS (%u/%u), \n"
" no supported fallback FPS found",
media_frames_per_second_to_fps(fps), fps.numerator, fps.denominator];
return NO;
}
}

[self.session beginConfiguration];
Expand Down Expand Up @@ -643,6 +657,37 @@ + (CMVideoDimensions)legacyDimensionsFromSettings:(void *)settings
return dimensions;
}

+ (OBSAVCaptureMediaFPS)fallbackFrameRateForFormat:(AVCaptureDeviceFormat *)format
{
struct obs_video_info video_info;
bool result = obs_get_video_info(&video_info);

double outputFPS = result ? ((double) video_info.fps_num / (double) video_info.fps_den) : 0;

double closestUpTo = 0;
double closestAbove = DBL_MAX;
OBSAVCaptureMediaFPS closestUpToMFPS = {};
OBSAVCaptureMediaFPS closestAboveMFPS = {};

for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
if (range.maxFrameRate > closestUpTo && range.maxFrameRate <= outputFPS) {
closestUpTo = range.maxFrameRate;
closestUpToMFPS.numerator = (uint32_t) clamp_Uint(range.minFrameDuration.timescale, 0, UINT32_MAX);
closestUpToMFPS.denominator = (uint32_t) clamp_Uint(range.minFrameDuration.value, 0, UINT32_MAX);
}
if (range.minFrameRate > outputFPS && range.minFrameRate < closestAbove) {
closestAbove = range.minFrameRate;
closestAboveMFPS.numerator = (uint32_t) clamp_Uint(range.maxFrameDuration.timescale, 0, UINT32_MAX);
closestAboveMFPS.denominator = (uint32_t) clamp_Uint(range.maxFrameDuration.value, 0, UINT32_MAX);
}
}
if (closestUpTo > 0) {
return closestUpToMFPS;
} else {
return closestAboveMFPS;
}
}

+ (NSString *)aspectRatioStringFromDimensions:(CMVideoDimensions)dimensions
{
if (dimensions.width <= 0 || dimensions.height <= 0) {
Expand Down

0 comments on commit 64be9fc

Please sign in to comment.