Add a fourth, virtual zoom factor for triple camera iPhones that have a large difference between their zoom factors. Closes #663

This commit is contained in:
Lior Halphon
2024-11-23 20:55:44 +02:00
parent a39b1913b8
commit 150f0fcad4

View File

@@ -61,9 +61,10 @@
AVCaptureDevicePosition _cameraPosition; AVCaptureDevicePosition _cameraPosition;
UIButton *_cameraPositionButton; UIButton *_cameraPositionButton;
UIButton *_changeCameraButton; UIButton *_changeCameraButton;
NSArray *_allCaptureDevices; AVCaptureDevice *_frontCaptureDevice;
NSArray *_backCaptureDevices; AVCaptureDevice *_backCaptureDevice;
AVCaptureDevice *_selectedBackCaptureDevice; NSMutableArray<NSNumber *> *_zoomLevels;
unsigned _currentZoomIndex;
__weak GCController *_lastController; __weak GCController *_lastController;
@@ -285,32 +286,42 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
_motionManager = [[CMMotionManager alloc] init]; _motionManager = [[CMMotionManager alloc] init];
_cameraPosition = AVCaptureDevicePositionBack; _cameraPosition = AVCaptureDevicePositionBack;
_selectedBackCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
// Back camera setup // Back camera setup
NSArray *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, NSArray *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera,
AVCaptureDeviceTypeBuiltInTelephotoCamera]; AVCaptureDeviceTypeBuiltInTelephotoCamera,
AVCaptureDeviceTypeBuiltInDualCamera];
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
// AVCaptureDeviceTypeBuiltInUltraWideCamera is only available in iOS 13+ // AVCaptureDeviceTypeBuiltInUltraWideCamera is only available in iOS 13+
deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera,
AVCaptureDeviceTypeBuiltInUltraWideCamera, AVCaptureDeviceTypeBuiltInUltraWideCamera,
AVCaptureDeviceTypeBuiltInTelephotoCamera]; AVCaptureDeviceTypeBuiltInTelephotoCamera,
AVCaptureDeviceTypeBuiltInTripleCamera,
AVCaptureDeviceTypeBuiltInDualWideCamera,
AVCaptureDeviceTypeBuiltInDualCamera];
} }
// Use a discovery session to gather the capture devices (all back cameras as well as the front camera) // Use a discovery session to gather the capture devices (all back cameras as well as the front camera)
AVCaptureDeviceDiscoverySession *cameraDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes AVCaptureDeviceDiscoverySession *cameraDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes
mediaType:AVMediaTypeVideo mediaType:AVMediaTypeVideo
position:AVCaptureDevicePositionUnspecified]; position:AVCaptureDevicePositionUnspecified];
_allCaptureDevices = cameraDiscoverySession.devices; for (AVCaptureDevice *device in cameraDiscoverySession.devices) {
// Filter only the back cameras into a list used for switching between them
NSMutableArray *filteredBackCameras = [NSMutableArray array];
for (AVCaptureDevice *device in _allCaptureDevices) {
if ([device position] == AVCaptureDevicePositionBack) { if ([device position] == AVCaptureDevicePositionBack) {
[filteredBackCameras addObject:device]; if (!_backCaptureDevice ||
_backCaptureDevice.virtualDeviceSwitchOverVideoZoomFactors.count < device.virtualDeviceSwitchOverVideoZoomFactors.count) {
_backCaptureDevice = device;
}
}
else if ([device position] == AVCaptureDevicePositionFront) {
_frontCaptureDevice = device;
} }
} }
_backCaptureDevices = filteredBackCameras;
_zoomLevels = _backCaptureDevice.virtualDeviceSwitchOverVideoZoomFactors.mutableCopy;
[_zoomLevels insertObject:@1 atIndex:0];
if (_zoomLevels.count == 3 && _zoomLevels[2].doubleValue > 5.5 && _zoomLevels[1].doubleValue < 3.5) {
[_zoomLevels insertObject:@4 atIndex:2];
}
_cameraPositionButton = [[UIButton alloc] init]; _cameraPositionButton = [[UIButton alloc] init];
[self didRotateFromInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]; [self didRotateFromInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation];
@@ -332,7 +343,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
action:@selector(changeCamera) action:@selector(changeCamera)
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
// Only show the change camera button if we have more than one back camera to swap between. // Only show the change camera button if we have more than one back camera to swap between.
if ([_backCaptureDevices count] > 1) { if (_zoomLevels.count > 1) {
[_backgroundView addSubview:_changeCameraButton]; [_backgroundView addSubview:_changeCameraButton];
} }
} }
@@ -1561,21 +1572,10 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
- (AVCaptureDevice *)captureDevice - (AVCaptureDevice *)captureDevice
{ {
for (AVCaptureDevice *device in _allCaptureDevices) { if (_cameraPosition == AVCaptureDevicePositionFront) {
if ([device position] == _cameraPosition) { return _frontCaptureDevice ?: [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
// There is only one front camera, return it
if (_cameraPosition == AVCaptureDevicePositionFront) {
return device;
}
// There may be several back cameras, return the one with the matching type
if ([device deviceType] == [_selectedBackCaptureDevice deviceType]) {
return device;
}
}
} }
// Return the default camera return _backCaptureDevice ?: [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
return [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
} }
- (void)cameraRequestUpdate - (void)cameraRequestUpdate
@@ -1756,19 +1756,13 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
- (void)changeCamera - (void)changeCamera
{ {
dispatch_async(_cameraQueue, ^{ dispatch_async(_cameraQueue, ^{
// Get index of selected camera and select the next one, wrapping to the beginning if (![_backCaptureDevice lockForConfiguration:nil]) return;
NSUInteger i = [_backCaptureDevices indexOfObject:_selectedBackCaptureDevice]; _currentZoomIndex++;
int nextIndex = (i + 1) % _backCaptureDevices.count; if (_currentZoomIndex == _zoomLevels.count) {
_selectedBackCaptureDevice = _backCaptureDevices[nextIndex]; _currentZoomIndex = 0;
[_cameraSession stopRunning];
_cameraSession = nil;
_cameraConnection = nil;
_cameraOutput = nil;
if (_cameraNeedsUpdate) {
_cameraNeedsUpdate = false;
GB_camera_updated(&_gb);
} }
[_backCaptureDevice rampToVideoZoomFactor:_zoomLevels[_currentZoomIndex].doubleValue withRate:2];
[_backCaptureDevice unlockForConfiguration];
}); });
} }