Camera fixes

This commit is contained in:
Ilya Laktyushin 2023-09-30 22:17:04 +04:00
parent 0a2532b802
commit df13a48bf7
7 changed files with 100 additions and 58 deletions

View File

@ -95,7 +95,7 @@ final class CameraDeviceContext {
return 30.0 return 30.0
} }
switch DeviceModel.current { switch DeviceModel.current {
case .iPhone14ProMax, .iPhone13ProMax: case .iPhone15ProMax, .iPhone14ProMax, .iPhone13ProMax:
return 60.0 return 60.0
default: default:
return 30.0 return 30.0

View File

@ -152,6 +152,10 @@ final class CameraDevice {
if device.isLowLightBoostSupported { if device.isLowLightBoostSupported {
device.automaticallyEnablesLowLightBoostWhenAvailable = true device.automaticallyEnablesLowLightBoostWhenAvailable = true
} }
if device.isExposureModeSupported(.continuousAutoExposure) {
device.exposureMode = .continuousAutoExposure
}
} }
} }

View File

@ -7,6 +7,10 @@ public extension Camera {
case iPhone14Plus case iPhone14Plus
case iPhone14Pro case iPhone14Pro
case iPhone14ProMax case iPhone14ProMax
case iPhone15
case iPhone15Plus
case iPhone15Pro
case iPhone15ProMax
case unknown case unknown
init(model: DeviceModel) { init(model: DeviceModel) {
@ -21,6 +25,14 @@ public extension Camera {
self = .iPhone14Pro self = .iPhone14Pro
case .iPhone14ProMax: case .iPhone14ProMax:
self = .iPhone14ProMax self = .iPhone14ProMax
case .iPhone15:
self = .iPhone15
case .iPhone15Plus:
self = .iPhone15Plus
case .iPhone15Pro:
self = .iPhone15Pro
case .iPhone15ProMax:
self = .iPhone15ProMax
case .unknown: case .unknown:
self = .unknown self = .unknown
default: default:
@ -32,13 +44,9 @@ public extension Camera {
switch self { switch self {
case .singleCamera: case .singleCamera:
return [1.0] return [1.0]
case .iPhone14: case .iPhone14, .iPhone14Plus, .iPhone15, .iPhone15Plus:
return [0.5, 1.0, 2.0] return [0.5, 1.0, 2.0]
case .iPhone14Plus: case .iPhone14Pro, .iPhone14ProMax, .iPhone15Pro, .iPhone15ProMax:
return [0.5, 1.0, 2.0]
case .iPhone14Pro:
return [0.5, 1.0, 2.0, 3.0]
case .iPhone14ProMax:
return [0.5, 1.0, 2.0, 3.0] return [0.5, 1.0, 2.0, 3.0]
case .unknown: case .unknown:
return [1.0, 2.0] return [1.0, 2.0]
@ -91,7 +99,11 @@ enum DeviceModel: CaseIterable, Equatable {
.iPhone14, .iPhone14,
.iPhone14Plus, .iPhone14Plus,
.iPhone14Pro, .iPhone14Pro,
.iPhone14ProMax .iPhone14ProMax,
.iPhone15,
.iPhone15Plus,
.iPhone15Pro,
.iPhone15ProMax
] ]
} }
@ -154,6 +166,11 @@ enum DeviceModel: CaseIterable, Equatable {
case iPhone14Pro case iPhone14Pro
case iPhone14ProMax case iPhone14ProMax
case iPhone15
case iPhone15Plus
case iPhone15Pro
case iPhone15ProMax
case unknown(String) case unknown(String)
var modelId: [String] { var modelId: [String] {
@ -248,6 +265,14 @@ enum DeviceModel: CaseIterable, Equatable {
return ["iPhone15,2"] return ["iPhone15,2"]
case .iPhone14ProMax: case .iPhone14ProMax:
return ["iPhone15,3"] return ["iPhone15,3"]
case .iPhone15:
return ["iPhone15,4"]
case .iPhone15Plus:
return ["iPhone15,5"]
case .iPhone15Pro:
return ["iPhone16,1"]
case .iPhone15ProMax:
return ["iPhone16,2"]
case let .unknown(modelId): case let .unknown(modelId):
return [modelId] return [modelId]
} }
@ -345,6 +370,14 @@ enum DeviceModel: CaseIterable, Equatable {
return "iPhone 14 Pro" return "iPhone 14 Pro"
case .iPhone14ProMax: case .iPhone14ProMax:
return "iPhone 14 Pro Max" return "iPhone 14 Pro Max"
case .iPhone15:
return "iPhone 15"
case .iPhone15Plus:
return "iPhone 15 Plus"
case .iPhone15Pro:
return "iPhone 15 Pro"
case .iPhone15ProMax:
return "iPhone 15 Pro Max"
case let .unknown(modelId): case let .unknown(modelId):
if modelId.hasPrefix("iPhone") { if modelId.hasPrefix("iPhone") {
return "Unknown iPhone" return "Unknown iPhone"

View File

@ -48,7 +48,7 @@ typedef enum
@class PGCameraDeviceAngleSampler; @class PGCameraDeviceAngleSampler;
@class TGCameraPreviewView; @class TGCameraPreviewView;
@interface PGCamera : NSObject @interface PGCamera : NSObject <AVCapturePhotoCaptureDelegate>
@property (readonly, nonatomic) PGCameraCaptureSession *captureSession; @property (readonly, nonatomic) PGCameraCaptureSession *captureSession;
@property (readonly, nonatomic) PGCameraDeviceAngleSampler *deviceAngleSampler; @property (readonly, nonatomic) PGCameraDeviceAngleSampler *deviceAngleSampler;

View File

@ -9,10 +9,8 @@
@interface PGCameraCaptureSession : AVCaptureSession @interface PGCameraCaptureSession : AVCaptureSession
@property (nonatomic, readonly) AVCaptureDevice *videoDevice; @property (nonatomic, readonly) AVCaptureDevice *videoDevice;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations" @property (nonatomic, readonly) AVCapturePhotoOutput *imageOutput;
@property (nonatomic, readonly) AVCaptureStillImageOutput *imageOutput;
#pragma clang diagnostic pop
@property (nonatomic, readonly) AVCaptureVideoDataOutput *videoOutput; @property (nonatomic, readonly) AVCaptureVideoDataOutput *videoOutput;
@property (nonatomic, readonly) AVCaptureAudioDataOutput *audioOutput; @property (nonatomic, readonly) AVCaptureAudioDataOutput *audioOutput;
@property (nonatomic, readonly) AVCaptureMetadataOutput *metadataOutput; @property (nonatomic, readonly) AVCaptureMetadataOutput *metadataOutput;

View File

@ -16,7 +16,7 @@ NSString *const PGCameraTorchActiveKey = @"torchActive";
NSString *const PGCameraTorchAvailableKey = @"torchAvailable"; NSString *const PGCameraTorchAvailableKey = @"torchAvailable";
NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
@interface PGCamera () @interface PGCamera ()
{ {
dispatch_queue_t cameraProcessingQueue; dispatch_queue_t cameraProcessingQueue;
dispatch_queue_t audioProcessingQueue; dispatch_queue_t audioProcessingQueue;
@ -34,11 +34,16 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
bool _capturing; bool _capturing;
bool _moment; bool _moment;
TGCameraPreviewView *_previewView; TGCameraPreviewView *_previewView;
UIInterfaceOrientation _currentPhotoOrientation;
NSTimeInterval _captureStartTime; NSTimeInterval _captureStartTime;
} }
@property (nonatomic, copy) void(^photoCaptureCompletionBlock)(UIImage *image, PGCameraShotMetadata *metadata);
@end @end
@implementation PGCamera @implementation PGCamera
@ -367,57 +372,57 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
{ {
bool videoMirrored = !self.disableResultMirroring ? _previewView.captureConnection.videoMirrored : false; bool videoMirrored = !self.disableResultMirroring ? _previewView.captureConnection.videoMirrored : false;
[[PGCameraCaptureSession cameraQueue] dispatch:^ void (^takePhoto)(void) = ^
{ {
if (!self.captureSession.isRunning || self.captureSession.imageOutput.isCapturingStillImage || _invalidated) self.photoCaptureCompletionBlock = completion;
return; [[PGCameraCaptureSession cameraQueue] dispatch:^
void (^takePhoto)(void) = ^
{ {
if (!self.captureSession.isRunning || _invalidated)
return;
AVCaptureConnection *imageConnection = [self.captureSession.imageOutput connectionWithMediaType:AVMediaTypeVideo]; AVCaptureConnection *imageConnection = [self.captureSession.imageOutput connectionWithMediaType:AVMediaTypeVideo];
[imageConnection setVideoMirrored:videoMirrored]; [imageConnection setVideoMirrored:videoMirrored];
UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait; UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait;
if (self.requestedCurrentInterfaceOrientation != nil) if (self.requestedCurrentInterfaceOrientation != nil)
orientation = self.requestedCurrentInterfaceOrientation(NULL); orientation = self.requestedCurrentInterfaceOrientation(NULL);
[imageConnection setVideoOrientation:[PGCamera _videoOrientationForInterfaceOrientation:orientation mirrored:false]]; [imageConnection setVideoOrientation:[PGCamera _videoOrientationForInterfaceOrientation:orientation mirrored:false]];
[self.captureSession.imageOutput captureStillImageAsynchronouslyFromConnection:self.captureSession.imageOutput.connections.firstObject completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) _currentPhotoOrientation = orientation;
{
if (imageDataSampleBuffer != NULL && error == nil) AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];
{ [self.captureSession.imageOutput capturePhotoWithSettings:photoSettings delegate:self];
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; }];
UIImage *image = [[UIImage alloc] initWithData:imageData]; };
if (self.cameraMode == PGCameraModeSquarePhoto || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing) NSTimeInterval delta = CFAbsoluteTimeGetCurrent() - _captureStartTime;
{ if (CFAbsoluteTimeGetCurrent() - _captureStartTime > 0.4)
CGFloat shorterSide = MIN(image.size.width, image.size.height); takePhoto();
CGFloat longerSide = MAX(image.size.width, image.size.height); else
TGDispatchAfter(0.4 - delta, [[PGCameraCaptureSession cameraQueue] _dispatch_queue], takePhoto);
CGRect cropRect = CGRectMake(CGFloor((longerSide - shorterSide) / 2.0f), 0, shorterSide, shorterSide); }
CGImageRef croppedCGImage = CGImageCreateWithImageInRect(image.CGImage, cropRect);
image = [UIImage imageWithCGImage:croppedCGImage scale:image.scale orientation:image.imageOrientation]; - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
CGImageRelease(croppedCGImage); if (error) {
} NSLog(@"Error capturing photo: %@", error);
return;
PGCameraShotMetadata *metadata = [[PGCameraShotMetadata alloc] init]; }
metadata.deviceAngle = [PGCameraShotMetadata relativeDeviceAngleFromAngle:_deviceAngleSampler.currentDeviceAngle orientation:orientation];
NSData *photoData = [photo fileDataRepresentation];
image = [self normalizeImageOrientation:image]; UIImage *capturedImage = [UIImage imageWithData:photoData];
if (completion != nil) PGCameraShotMetadata *metadata = [[PGCameraShotMetadata alloc] init];
completion(image, metadata); metadata.deviceAngle = [PGCameraShotMetadata relativeDeviceAngleFromAngle:_deviceAngleSampler.currentDeviceAngle orientation:_currentPhotoOrientation];
}
}]; UIImage *image = [self normalizeImageOrientation:capturedImage];
};
TGDispatchOnMainThread(^
NSTimeInterval delta = CFAbsoluteTimeGetCurrent() - _captureStartTime; {
if (CFAbsoluteTimeGetCurrent() - _captureStartTime > 0.4) if (self.photoCaptureCompletionBlock != nil) {
takePhoto(); self.photoCaptureCompletionBlock(image, metadata);
else self.photoCaptureCompletionBlock = nil;
TGDispatchAfter(0.4 - delta, [[PGCameraCaptureSession cameraQueue] _dispatch_queue], takePhoto); }
}]; });
} }
- (void)startVideoRecordingForMoment:(bool)moment completion:(void (^)(NSURL *, CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success))completion - (void)startVideoRecordingForMoment:(bool)moment completion:(void (^)(NSURL *, CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success))completion

View File

@ -130,8 +130,10 @@ const NSInteger PGCameraFrameRate = 30;
[self setFrameRate:PGCameraFrameRate forDevice:_videoDevice]; [self setFrameRate:PGCameraFrameRate forDevice:_videoDevice];
} }
AVCaptureStillImageOutput *imageOutput = [[AVCaptureStillImageOutput alloc] init]; AVCapturePhotoOutput *imageOutput = [[AVCapturePhotoOutput alloc] init];
[imageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}]; if (@available(iOS 13.0, *)) {
[imageOutput setMaxPhotoQualityPrioritization:AVCapturePhotoQualityPrioritizationBalanced];
}
if ([self canAddOutput:imageOutput]) if ([self canAddOutput:imageOutput])
{ {
#if !TARGET_IPHONE_SIMULATOR #if !TARGET_IPHONE_SIMULATOR