@@ -153,6 +153,59 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
153
153
error: error];
154
154
}
155
155
156
+ // Returns frame rate supported by format closest to targetFrameRate.
157
+ static double bestFrameRateForFormat (AVCaptureDeviceFormat *format, double targetFrameRate) {
158
+ double bestFrameRate = 0 ;
159
+ double minDistance = DBL_MAX;
160
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges ) {
161
+ double frameRate = MIN (MAX (targetFrameRate, range.minFrameRate ), range.maxFrameRate );
162
+ double distance = fabs (frameRate - targetFrameRate);
163
+ if (distance < minDistance) {
164
+ bestFrameRate = frameRate;
165
+ minDistance = distance;
166
+ }
167
+ }
168
+ return bestFrameRate;
169
+ }
170
+
171
+ // Finds format with same resolution as current activeFormat in captureDevice for which
172
+ // bestFrameRateForFormat returned frame rate closest to mediaSettings.framesPerSecond.
173
+ // Preferred are formats with the same subtype as current activeFormat. Sets this format
174
+ // as activeFormat and also updates mediaSettings.framesPerSecond to value which
175
+ // bestFrameRateForFormat returned for that format.
176
+ static void selectBestFormatForRequestedFrameRate (
177
+ AVCaptureDevice *captureDevice, FCPPlatformMediaSettings *mediaSettings,
178
+ VideoDimensionsForFormat videoDimensionsForFormat) {
179
+ CMVideoDimensions targetResolution = videoDimensionsForFormat (captureDevice.activeFormat );
180
+ double targetFrameRate = mediaSettings.framesPerSecond .doubleValue ;
181
+ FourCharCode preferredSubType =
182
+ CMFormatDescriptionGetMediaSubType (captureDevice.activeFormat .formatDescription );
183
+ AVCaptureDeviceFormat *bestFormat = captureDevice.activeFormat ;
184
+ double bestFrameRate = bestFrameRateForFormat (bestFormat, targetFrameRate);
185
+ double minDistance = fabs (bestFrameRate - targetFrameRate);
186
+ BOOL isBestSubTypePreferred = YES ;
187
+ for (AVCaptureDeviceFormat *format in captureDevice.formats ) {
188
+ CMVideoDimensions resolution = videoDimensionsForFormat (format);
189
+ if (resolution.width != targetResolution.width ||
190
+ resolution.height != targetResolution.height ) {
191
+ continue ;
192
+ }
193
+ double frameRate = bestFrameRateForFormat (format, targetFrameRate);
194
+ double distance = fabs (frameRate - targetFrameRate);
195
+ FourCharCode subType = CMFormatDescriptionGetMediaSubType (format.formatDescription );
196
+ BOOL isSubTypePreferred = subType == preferredSubType;
197
+ if (distance < minDistance ||
198
+ (distance == minDistance && isSubTypePreferred && !isBestSubTypePreferred)) {
199
+ bestFormat = format;
200
+ bestFrameRate = frameRate;
201
+ minDistance = distance;
202
+ isBestSubTypePreferred = isSubTypePreferred;
203
+ }
204
+ }
205
+ captureDevice.activeFormat = bestFormat;
206
+ mediaSettings.framesPerSecond = @(bestFrameRate);
207
+ }
208
+
156
209
- (instancetype )initWithMediaSettings : (FCPPlatformMediaSettings *)mediaSettings
157
210
mediaSettingsAVWrapper : (FLTCamMediaSettingsAVWrapper *)mediaSettingsAVWrapper
158
211
orientation : (UIDeviceOrientation)orientation
@@ -226,6 +279,9 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings
226
279
return nil ;
227
280
}
228
281
282
+ selectBestFormatForRequestedFrameRate (_captureDevice, _mediaSettings,
283
+ _videoDimensionsForFormat);
284
+
229
285
// Set frame rate with 1/10 precision allowing not integral values.
230
286
int fpsNominator = floor ([_mediaSettings.framesPerSecond doubleValue ] * 10.0 );
231
287
CMTime duration = CMTimeMake (10 , fpsNominator);
@@ -474,56 +530,42 @@ - (BOOL)setCaptureSessionPreset:(FCPPlatformResolutionPreset)resolutionPreset
474
530
// Set the best device format found and finish the device configuration.
475
531
_captureDevice.activeFormat = bestFormat;
476
532
[_captureDevice unlockForConfiguration ];
477
-
478
- // Set the preview size based on values from the current capture device.
479
- _previewSize =
480
- CGSizeMake (_captureDevice.activeFormat .highResolutionStillImageDimensions .width ,
481
- _captureDevice.activeFormat .highResolutionStillImageDimensions .height );
482
533
break ;
483
534
}
484
535
}
485
536
}
486
537
case FCPPlatformResolutionPresetUltraHigh:
487
538
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPreset3840x2160]) {
488
539
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset3840x2160;
489
- _previewSize = CGSizeMake (3840 , 2160 );
490
540
break ;
491
541
}
492
542
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPresetHigh]) {
493
543
_videoCaptureSession.sessionPreset = AVCaptureSessionPresetHigh;
494
- _previewSize =
495
- CGSizeMake (_captureDevice.activeFormat .highResolutionStillImageDimensions .width ,
496
- _captureDevice.activeFormat .highResolutionStillImageDimensions .height );
497
544
break ;
498
545
}
499
546
case FCPPlatformResolutionPresetVeryHigh:
500
547
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPreset1920x1080]) {
501
548
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset1920x1080;
502
- _previewSize = CGSizeMake (1920 , 1080 );
503
549
break ;
504
550
}
505
551
case FCPPlatformResolutionPresetHigh:
506
552
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPreset1280x720]) {
507
553
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset1280x720;
508
- _previewSize = CGSizeMake (1280 , 720 );
509
554
break ;
510
555
}
511
556
case FCPPlatformResolutionPresetMedium:
512
557
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPreset640x480]) {
513
558
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset640x480;
514
- _previewSize = CGSizeMake (640 , 480 );
515
559
break ;
516
560
}
517
561
case FCPPlatformResolutionPresetLow:
518
562
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPreset352x288]) {
519
563
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset352x288;
520
- _previewSize = CGSizeMake (352 , 288 );
521
564
break ;
522
565
}
523
566
default :
524
567
if ([_videoCaptureSession canSetSessionPreset: AVCaptureSessionPresetLow]) {
525
568
_videoCaptureSession.sessionPreset = AVCaptureSessionPresetLow;
526
- _previewSize = CGSizeMake (352 , 288 );
527
569
} else {
528
570
if (error != nil ) {
529
571
*error =
@@ -537,23 +579,33 @@ - (BOOL)setCaptureSessionPreset:(FCPPlatformResolutionPreset)resolutionPreset
537
579
return NO ;
538
580
}
539
581
}
582
+ CMVideoDimensions size = self.videoDimensionsForFormat (_captureDevice.activeFormat );
583
+ _previewSize = CGSizeMake (size.width , size.height );
540
584
_audioCaptureSession.sessionPreset = _videoCaptureSession.sessionPreset ;
541
585
return YES ;
542
586
}
543
587
544
588
// / Finds the highest available resolution in terms of pixel count for the given device.
589
+ // / Preferred are formats with the same subtype as current activeFormat.
545
590
- (AVCaptureDeviceFormat *)highestResolutionFormatForCaptureDevice :
546
591
(AVCaptureDevice *)captureDevice {
592
+ FourCharCode preferredSubType =
593
+ CMFormatDescriptionGetMediaSubType (_captureDevice.activeFormat .formatDescription );
547
594
AVCaptureDeviceFormat *bestFormat = nil ;
548
595
NSUInteger maxPixelCount = 0 ;
596
+ BOOL isBestSubTypePreferred = NO ;
549
597
for (AVCaptureDeviceFormat *format in _captureDevice.formats ) {
550
598
CMVideoDimensions res = self.videoDimensionsForFormat (format);
551
599
NSUInteger height = res.height ;
552
600
NSUInteger width = res.width ;
553
601
NSUInteger pixelCount = height * width;
554
- if (pixelCount > maxPixelCount) {
555
- maxPixelCount = pixelCount;
602
+ FourCharCode subType = CMFormatDescriptionGetMediaSubType (format.formatDescription );
603
+ BOOL isSubTypePreferred = subType == preferredSubType;
604
+ if (pixelCount > maxPixelCount ||
605
+ (pixelCount == maxPixelCount && isSubTypePreferred && !isBestSubTypePreferred)) {
556
606
bestFormat = format;
607
+ maxPixelCount = pixelCount;
608
+ isBestSubTypePreferred = isSubTypePreferred;
557
609
}
558
610
}
559
611
return bestFormat;
0 commit comments