add support for changing which back camera is being used

This commit is contained in:
Cal Moody
2024-06-25 22:27:16 -04:00
parent 08178c9f3a
commit 25576899d8

View File

@@ -52,6 +52,10 @@
NSTimer *_disableCameraTimer; NSTimer *_disableCameraTimer;
AVCaptureDevicePosition _cameraPosition; AVCaptureDevicePosition _cameraPosition;
UIButton *_cameraPositionButton; UIButton *_cameraPositionButton;
NSArray *_allCaptureDevices;
NSArray *_backCaptureDevices;
AVCaptureDevice *_selectedCaptureDevice;
UIButton *_changeCameraButton;
__weak GCController *_lastController; __weak GCController *_lastController;
@@ -241,30 +245,82 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
_motionManager = [[CMMotionManager alloc] init]; _motionManager = [[CMMotionManager alloc] init];
_cameraPosition = AVCaptureDevicePositionBack; _cameraPosition = AVCaptureDevicePositionBack;
_selectedCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
// Back camera setup
NSArray *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera,
AVCaptureDeviceTypeBuiltInTelephotoCamera];
if (@available(iOS 13.0, *)) {
// AVCaptureDeviceTypeBuiltInUltraWideCamera is only available in iOS 13+
deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera,
AVCaptureDeviceTypeBuiltInUltraWideCamera,
AVCaptureDeviceTypeBuiltInTelephotoCamera];
}
// 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) {
if ([device position] == AVCaptureDevicePositionBack) {
[filteredBackCameras addObject:device];
}
}
_backCaptureDevices = filteredBackCameras;
_cameraPositionButton = [[UIButton alloc] initWithFrame:CGRectMake(8, _cameraPositionButton = [[UIButton alloc] initWithFrame:CGRectMake(8,
0, 0,
32, 32,
32)]; 32)];
_changeCameraButton = [[UIButton alloc] initWithFrame:CGRectMake(8,
0,
32,
32)];
[self didRotateFromInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation]; [self didRotateFromInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation];
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
[_cameraPositionButton setImage:[UIImage systemImageNamed:@"camera.rotate" [_cameraPositionButton setImage:[UIImage systemImageNamed:@"camera.rotate"
withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleLarge]] withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleLarge]]
forState:UIControlStateNormal]; forState:UIControlStateNormal];
_cameraPositionButton.backgroundColor = [UIColor systemBackgroundColor]; _cameraPositionButton.backgroundColor = [UIColor systemBackgroundColor];
[_changeCameraButton setImage:[UIImage systemImageNamed:@"camera"
withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleLarge]]
forState:UIControlStateNormal];
_changeCameraButton.backgroundColor = [UIColor systemBackgroundColor];
} }
else { else {
UIImage *image = [[UIImage imageNamed:@"CameraRotateTemplate"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; UIImage *rotateImage = [[UIImage imageNamed:@"CameraRotateTemplate"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[_cameraPositionButton setImage:image [_cameraPositionButton setImage:rotateImage
forState:UIControlStateNormal]; forState:UIControlStateNormal];
_cameraPositionButton.backgroundColor = [UIColor whiteColor]; _cameraPositionButton.backgroundColor = [UIColor whiteColor];
UIImage *selectCameraImage = [[UIImage imageNamed:@"CameraTemplate"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[_changeCameraButton setImage:selectCameraImage
forState:UIControlStateNormal];
_changeCameraButton.backgroundColor = [UIColor whiteColor];
} }
_cameraPositionButton.layer.cornerRadius = 6; _cameraPositionButton.layer.cornerRadius = 6;
_cameraPositionButton.alpha = 0; _cameraPositionButton.alpha = 0;
[_cameraPositionButton addTarget:self [_cameraPositionButton addTarget:self
action:@selector(rotateCamera) action:@selector(rotateCamera)
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
_changeCameraButton.layer.cornerRadius = 6;
_changeCameraButton.alpha = 0;
[_changeCameraButton addTarget:self
action:@selector(changeCamera)
forControlEvents:UIControlEventTouchUpInside];
[_backgroundView addSubview:_cameraPositionButton]; [_backgroundView addSubview:_cameraPositionButton];
// Only show the select camera button if we have more than one back camera to swap between.
if ([_backCaptureDevices count] > 1) {
[_backgroundView addSubview:_changeCameraButton];
}
_cameraQueue = dispatch_queue_create("SameBoy Camera Queue", NULL); _cameraQueue = dispatch_queue_create("SameBoy Camera Queue", NULL);
[UNUserNotificationCenter currentNotificationCenter].delegate = self; [UNUserNotificationCenter currentNotificationCenter].delegate = self;
@@ -686,6 +742,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
{ {
UIEdgeInsets insets = self.window.safeAreaInsets; UIEdgeInsets insets = self.window.safeAreaInsets;
_cameraPositionButton.frame = CGRectMake(insets.left + 8, _backgroundView.bounds.size.height - 8 - insets.bottom - 32, 32, 32); _cameraPositionButton.frame = CGRectMake(insets.left + 8, _backgroundView.bounds.size.height - 8 - insets.bottom - 32, 32, 32);
_changeCameraButton.frame = CGRectMake(insets.right - 8, _backgroundView.bounds.size.height - 8 - insets.bottom - 32, 32, 32);
} }
- (UIInterfaceOrientationMask)supportedInterfaceOrientations - (UIInterfaceOrientationMask)supportedInterfaceOrientations
@@ -1123,12 +1180,20 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
- (AVCaptureDevice *)captureDevice - (AVCaptureDevice *)captureDevice
{ {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in _allCaptureDevices) {
for (AVCaptureDevice *device in devices) {
if ([device position] == _cameraPosition) { if ([device position] == _cameraPosition) {
return device; // 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] == [_selectedCaptureDevice deviceType]) {
return device;
}
} }
} }
// Return the default camera
return [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo]; return [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
} }
@@ -1292,4 +1357,23 @@ 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:_selectedCaptureDevice];
int nextIndex = (i + 1) % _backCaptureDevices.count;
_selectedCaptureDevice = _backCaptureDevices[nextIndex];
[_cameraSession stopRunning];
_cameraSession = nil;
_cameraConnection = nil;
_cameraOutput = nil;
if (_cameraNeedsUpdate) {
_cameraNeedsUpdate = false;
GB_camera_updated(&_gb);
}
});
}
@end @end