From 150f0fcad40d2e26361bf482df7d80a4a5876aa5 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 23 Nov 2024 20:55:44 +0200 Subject: [PATCH] Add a fourth, virtual zoom factor for triple camera iPhones that have a large difference between their zoom factors. Closes #663 --- iOS/GBViewController.m | 74 +++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m index c760156ca..6f8779a04 100644 --- a/iOS/GBViewController.m +++ b/iOS/GBViewController.m @@ -61,9 +61,10 @@ AVCaptureDevicePosition _cameraPosition; UIButton *_cameraPositionButton; UIButton *_changeCameraButton; - NSArray *_allCaptureDevices; - NSArray *_backCaptureDevices; - AVCaptureDevice *_selectedBackCaptureDevice; + AVCaptureDevice *_frontCaptureDevice; + AVCaptureDevice *_backCaptureDevice; + NSMutableArray *_zoomLevels; + unsigned _currentZoomIndex; __weak GCController *_lastController; @@ -285,32 +286,42 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) _motionManager = [[CMMotionManager alloc] init]; _cameraPosition = AVCaptureDevicePositionBack; - _selectedBackCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; // Back camera setup NSArray *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, - AVCaptureDeviceTypeBuiltInTelephotoCamera]; + AVCaptureDeviceTypeBuiltInTelephotoCamera, + AVCaptureDeviceTypeBuiltInDualCamera]; if (@available(iOS 13.0, *)) { // AVCaptureDeviceTypeBuiltInUltraWideCamera is only available in iOS 13+ deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInUltraWideCamera, - AVCaptureDeviceTypeBuiltInTelephotoCamera]; + AVCaptureDeviceTypeBuiltInTelephotoCamera, + AVCaptureDeviceTypeBuiltInTripleCamera, + AVCaptureDeviceTypeBuiltInDualWideCamera, + AVCaptureDeviceTypeBuiltInDualCamera]; } // Use a discovery session to gather the capture devices (all back cameras as well as the front camera) AVCaptureDeviceDiscoverySession *cameraDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; - _allCaptureDevices = cameraDiscoverySession.devices; - - // Filter only the back cameras into a list used for switching between them - NSMutableArray *filteredBackCameras = [NSMutableArray array]; - for (AVCaptureDevice *device in _allCaptureDevices) { + for (AVCaptureDevice *device in cameraDiscoverySession.devices) { 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]; [self didRotateFromInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]; @@ -332,7 +343,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) action:@selector(changeCamera) forControlEvents:UIControlEventTouchUpInside]; // 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]; } } @@ -1561,21 +1572,10 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response - (AVCaptureDevice *)captureDevice { - for (AVCaptureDevice *device in _allCaptureDevices) { - if ([device position] == _cameraPosition) { - // 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; - } - } + if (_cameraPosition == AVCaptureDevicePositionFront) { + return _frontCaptureDevice ?: [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; } - // Return the default camera - return [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; + return _backCaptureDevice ?: [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; } - (void)cameraRequestUpdate @@ -1756,19 +1756,13 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response - (void)changeCamera { dispatch_async(_cameraQueue, ^{ - // Get index of selected camera and select the next one, wrapping to the beginning - NSUInteger i = [_backCaptureDevices indexOfObject:_selectedBackCaptureDevice]; - int nextIndex = (i + 1) % _backCaptureDevices.count; - _selectedBackCaptureDevice = _backCaptureDevices[nextIndex]; - - [_cameraSession stopRunning]; - _cameraSession = nil; - _cameraConnection = nil; - _cameraOutput = nil; - if (_cameraNeedsUpdate) { - _cameraNeedsUpdate = false; - GB_camera_updated(&_gb); + if (![_backCaptureDevice lockForConfiguration:nil]) return; + _currentZoomIndex++; + if (_currentZoomIndex == _zoomLevels.count) { + _currentZoomIndex = 0; } + [_backCaptureDevice rampToVideoZoomFactor:_zoomLevels[_currentZoomIndex].doubleValue withRate:2]; + [_backCaptureDevice unlockForConfiguration]; }); }