2014 snapchat source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
16 KiB

  1. //
  2. // SCManagedStillImageCapturer.m
  3. // Snapchat
  4. //
  5. // Created by Liu Liu on 4/30/15.
  6. // Copyright (c) 2015 Liu Liu. All rights reserved.
  7. //
  8. #import "SCManagedStillImageCapturer.h"
  9. #import "SCCameraSettingUtils.h"
  10. #import "SCCameraTweaks.h"
  11. #import "SCCaptureResource.h"
  12. #import "SCLogger+Camera.h"
  13. #import "SCManagedCaptureSession.h"
  14. #import "SCManagedCapturer.h"
  15. #import "SCManagedCapturerLensAPI.h"
  16. #import "SCManagedFrameHealthChecker.h"
  17. #import "SCManagedLegacyStillImageCapturer.h"
  18. #import "SCManagedPhotoCapturer.h"
  19. #import "SCManagedStillImageCapturerHandler.h"
  20. #import "SCManagedStillImageCapturer_Protected.h"
  21. #import <SCFoundation/NSException+Exceptions.h>
  22. #import <SCFoundation/SCLog.h>
  23. #import <SCFoundation/SCPerforming.h>
  24. #import <SCFoundation/SCQueuePerformer.h>
  25. #import <SCFoundation/SCTrace.h>
  26. #import <SCFoundation/UIImage+CVPixelBufferRef.h>
  27. #import <SCLenses/SCLens.h>
  28. #import <SCLogger/SCCameraMetrics.h>
  29. #import <SCWebP/UIImage+WebP.h>
  30. #import <ImageIO/ImageIO.h>
  31. NSString *const kSCManagedStillImageCapturerErrorDomain = @"kSCManagedStillImageCapturerErrorDomain";
  32. NSInteger const kSCCameraShutterSoundID = 1108;
  33. #if !TARGET_IPHONE_SIMULATOR
  34. NSInteger const kSCManagedStillImageCapturerNoStillImageConnection = 1101;
  35. #endif
  36. NSInteger const kSCManagedStillImageCapturerApplicationStateBackground = 1102;
  37. // We will do the image capture regardless if these is still camera adjustment in progress after 0.4 seconds.
  38. NSTimeInterval const kSCManagedStillImageCapturerDeadline = 0.4;
  39. NSTimeInterval const kSCCameraRetryInterval = 0.1;
  40. BOOL SCPhotoCapturerIsEnabled(void)
  41. {
  42. // Due to the native crash in https://jira.sc-corp.net/browse/CCAM-4904, we guard it >= 10.2
  43. return SC_AT_LEAST_IOS_10_2;
  44. }
  45. NSDictionary *cameraInfoForBuffer(CMSampleBufferRef imageDataSampleBuffer)
  46. {
  47. CFDictionaryRef exifAttachments =
  48. (CFDictionaryRef)CMGetAttachment(imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL);
  49. float brightness = [retrieveBrightnessFromEXIFAttachments(exifAttachments) floatValue];
  50. NSInteger ISOSpeedRating = [retrieveISOSpeedRatingFromEXIFAttachments(exifAttachments) integerValue];
  51. return @{
  52. (__bridge NSString *) kCGImagePropertyExifISOSpeedRatings : @(ISOSpeedRating), (__bridge NSString *)
  53. kCGImagePropertyExifBrightnessValue : @(brightness)
  54. };
  55. }
  56. @implementation SCManagedStillImageCapturer
  57. + (instancetype)capturerWithCaptureResource:(SCCaptureResource *)captureResource
  58. {
  59. if (SCPhotoCapturerIsEnabled()) {
  60. return [[SCManagedPhotoCapturer alloc] initWithSession:captureResource.managedSession.avSession
  61. performer:captureResource.queuePerformer
  62. lensProcessingCore:captureResource.lensProcessingCore
  63. delegate:captureResource.stillImageCapturerHandler];
  64. } else {
  65. return [[SCManagedLegacyStillImageCapturer alloc] initWithSession:captureResource.managedSession.avSession
  66. performer:captureResource.queuePerformer
  67. lensProcessingCore:captureResource.lensProcessingCore
  68. delegate:captureResource.stillImageCapturerHandler];
  69. }
  70. }
  71. - (instancetype)initWithSession:(AVCaptureSession *)session
  72. performer:(id<SCPerforming>)performer
  73. lensProcessingCore:(id<SCManagedCapturerLensAPI>)lensAPI
  74. delegate:(id<SCManagedStillImageCapturerDelegate>)delegate
  75. {
  76. self = [super init];
  77. if (self) {
  78. _session = session;
  79. _performer = performer;
  80. _lensAPI = lensAPI;
  81. _delegate = delegate;
  82. }
  83. return self;
  84. }
  85. - (void)setupWithSession:(AVCaptureSession *)session
  86. {
  87. UNIMPLEMENTED_METHOD;
  88. }
  89. - (void)setAsOutput:(AVCaptureSession *)session
  90. {
  91. UNIMPLEMENTED_METHOD;
  92. }
  93. - (void)setHighResolutionStillImageOutputEnabled:(BOOL)highResolutionStillImageOutputEnabled
  94. {
  95. UNIMPLEMENTED_METHOD;
  96. }
  97. - (void)enableStillImageStabilization
  98. {
  99. UNIMPLEMENTED_METHOD;
  100. }
  101. - (void)removeAsOutput:(AVCaptureSession *)session
  102. {
  103. UNIMPLEMENTED_METHOD;
  104. }
  105. - (void)setPortraitModeCaptureEnabled:(BOOL)enabled
  106. {
  107. UNIMPLEMENTED_METHOD;
  108. }
  109. - (void)setPortraitModePointOfInterest:(CGPoint)pointOfInterest
  110. {
  111. UNIMPLEMENTED_METHOD;
  112. }
  113. - (void)captureStillImageWithAspectRatio:(CGFloat)aspectRatio
  114. atZoomFactor:(float)zoomFactor
  115. fieldOfView:(float)fieldOfView
  116. state:(SCManagedCapturerState *)state
  117. captureSessionID:(NSString *)captureSessionID
  118. shouldCaptureFromVideo:(BOOL)shouldCaptureFromVideo
  119. completionHandler:
  120. (sc_managed_still_image_capturer_capture_still_image_completion_handler_t)completionHandler
  121. {
  122. UNIMPLEMENTED_METHOD;
  123. }
  124. #pragma mark - SCManagedDeviceCapacityAnalyzerListener
  125. - (void)managedDeviceCapacityAnalyzer:(SCManagedDeviceCapacityAnalyzer *)managedDeviceCapacityAnalyzer
  126. didChangeAdjustingExposure:(BOOL)adjustingExposure
  127. {
  128. UNIMPLEMENTED_METHOD;
  129. }
  130. - (void)managedDeviceCapacityAnalyzer:(SCManagedDeviceCapacityAnalyzer *)managedDeviceCapacityAnalyzer
  131. didChangeLightingCondition:(SCCapturerLightingConditionType)lightingCondition
  132. {
  133. UNIMPLEMENTED_METHOD;
  134. }
  135. #pragma mark - SCManagedCapturerListener
  136. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeAdjustingExposure:(SCManagedCapturerState *)state
  137. {
  138. UNIMPLEMENTED_METHOD;
  139. }
  140. - (UIImage *)imageFromData:(NSData *)data
  141. currentZoomFactor:(float)currentZoomFactor
  142. targetAspectRatio:(CGFloat)targetAspectRatio
  143. fieldOfView:(float)fieldOfView
  144. state:(SCManagedCapturerState *)state
  145. sampleBuffer:(CMSampleBufferRef)sampleBuffer
  146. {
  147. UIImage *capturedImage = [self imageFromImage:[UIImage sc_imageWithData:data]
  148. currentZoomFactor:currentZoomFactor
  149. targetAspectRatio:targetAspectRatio
  150. fieldOfView:fieldOfView
  151. state:state];
  152. // Check capture frame health before showing preview
  153. NSDictionary *metadata =
  154. [[SCManagedFrameHealthChecker sharedInstance] metadataForSampleBuffer:sampleBuffer
  155. photoCapturerEnabled:SCPhotoCapturerIsEnabled()
  156. lensEnabled:state.lensesActive
  157. lensID:[_lensAPI activeLensId]];
  158. [[SCManagedFrameHealthChecker sharedInstance] checkImageHealthForCaptureFrameImage:capturedImage
  159. captureSettings:metadata
  160. captureSessionID:_captureSessionID];
  161. _captureSessionID = nil;
  162. return capturedImage;
  163. }
  164. - (UIImage *)imageFromData:(NSData *)data
  165. currentZoomFactor:(float)currentZoomFactor
  166. targetAspectRatio:(CGFloat)targetAspectRatio
  167. fieldOfView:(float)fieldOfView
  168. state:(SCManagedCapturerState *)state
  169. metadata:(NSDictionary *)metadata
  170. {
  171. UIImage *capturedImage = [self imageFromImage:[UIImage sc_imageWithData:data]
  172. currentZoomFactor:currentZoomFactor
  173. targetAspectRatio:targetAspectRatio
  174. fieldOfView:fieldOfView
  175. state:state];
  176. // Check capture frame health before showing preview
  177. NSDictionary *newMetadata =
  178. [[SCManagedFrameHealthChecker sharedInstance] metadataForMetadata:metadata
  179. photoCapturerEnabled:SCPhotoCapturerIsEnabled()
  180. lensEnabled:state.lensesActive
  181. lensID:[_lensAPI activeLensId]];
  182. [[SCManagedFrameHealthChecker sharedInstance] checkImageHealthForCaptureFrameImage:capturedImage
  183. captureSettings:newMetadata
  184. captureSessionID:_captureSessionID];
  185. _captureSessionID = nil;
  186. return capturedImage;
  187. }
  188. - (UIImage *)imageFromImage:(UIImage *)image
  189. currentZoomFactor:(float)currentZoomFactor
  190. targetAspectRatio:(CGFloat)targetAspectRatio
  191. fieldOfView:(float)fieldOfView
  192. state:(SCManagedCapturerState *)state
  193. {
  194. UIImage *fullScreenImage = image;
  195. if (state.lensesActive && _lensAPI.isLensApplied) {
  196. fullScreenImage = [_lensAPI processImage:fullScreenImage
  197. maxPixelSize:[_lensAPI maxPixelSize]
  198. devicePosition:state.devicePosition
  199. fieldOfView:fieldOfView];
  200. }
  201. // Resize and crop
  202. return [self resizeImage:fullScreenImage currentZoomFactor:currentZoomFactor targetAspectRatio:targetAspectRatio];
  203. }
  204. - (UIImage *)resizeImage:(UIImage *)image
  205. currentZoomFactor:(float)currentZoomFactor
  206. targetAspectRatio:(CGFloat)targetAspectRatio
  207. {
  208. SCTraceStart();
  209. if (currentZoomFactor == 1) {
  210. return SCCropImageToTargetAspectRatio(image, targetAspectRatio);
  211. } else {
  212. @autoreleasepool {
  213. return [self resizeImageUsingCG:image
  214. currentZoomFactor:currentZoomFactor
  215. targetAspectRatio:targetAspectRatio
  216. maxPixelSize:[_lensAPI maxPixelSize]];
  217. }
  218. }
  219. }
  220. - (UIImage *)resizeImageUsingCG:(UIImage *)inputImage
  221. currentZoomFactor:(float)currentZoomFactor
  222. targetAspectRatio:(CGFloat)targetAspectRatio
  223. maxPixelSize:(CGFloat)maxPixelSize
  224. {
  225. size_t imageWidth = CGImageGetWidth(inputImage.CGImage);
  226. size_t imageHeight = CGImageGetHeight(inputImage.CGImage);
  227. SCLogGeneralInfo(@"Captured still image at %dx%d", (int)imageWidth, (int)imageHeight);
  228. size_t targetWidth, targetHeight;
  229. float zoomFactor = currentZoomFactor;
  230. if (imageWidth > imageHeight) {
  231. targetWidth = maxPixelSize;
  232. targetHeight = (maxPixelSize * imageHeight + imageWidth / 2) / imageWidth;
  233. // Update zoom factor here
  234. zoomFactor *= (float)maxPixelSize / imageWidth;
  235. } else {
  236. targetHeight = maxPixelSize;
  237. targetWidth = (maxPixelSize * imageWidth + imageHeight / 2) / imageHeight;
  238. zoomFactor *= (float)maxPixelSize / imageHeight;
  239. }
  240. if (targetAspectRatio != kSCManagedCapturerAspectRatioUnspecified) {
  241. SCCropImageSizeToAspectRatio(targetWidth, targetHeight, inputImage.imageOrientation, targetAspectRatio,
  242. &targetWidth, &targetHeight);
  243. }
  244. CGContextRef context =
  245. CGBitmapContextCreate(NULL, targetWidth, targetHeight, CGImageGetBitsPerComponent(inputImage.CGImage),
  246. CGImageGetBitsPerPixel(inputImage.CGImage) * targetWidth / 8,
  247. CGImageGetColorSpace(inputImage.CGImage), CGImageGetBitmapInfo(inputImage.CGImage));
  248. CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
  249. CGContextDrawImage(context, CGRectMake(targetWidth * 0.5 - imageWidth * 0.5 * zoomFactor,
  250. targetHeight * 0.5 - imageHeight * 0.5 * zoomFactor, imageWidth * zoomFactor,
  251. imageHeight * zoomFactor),
  252. inputImage.CGImage);
  253. CGImageRef thumbnail = CGBitmapContextCreateImage(context);
  254. CGContextRelease(context);
  255. UIImage *image =
  256. [UIImage imageWithCGImage:thumbnail scale:inputImage.scale orientation:inputImage.imageOrientation];
  257. CGImageRelease(thumbnail);
  258. return image;
  259. }
  260. - (CMTime)adjustedExposureDurationForNightModeWithCurrentExposureDuration:(CMTime)exposureDuration
  261. {
  262. CMTime adjustedExposureDuration = exposureDuration;
  263. if (_lightingConditionType == SCCapturerLightingConditionTypeDark) {
  264. adjustedExposureDuration = CMTimeMultiplyByFloat64(exposureDuration, 1.5);
  265. } else if (_lightingConditionType == SCCapturerLightingConditionTypeExtremeDark) {
  266. adjustedExposureDuration = CMTimeMultiplyByFloat64(exposureDuration, 2.5);
  267. }
  268. return adjustedExposureDuration;
  269. }
  270. #pragma mark - SCManagedVideoDataSourceListener
  271. - (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
  272. didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
  273. devicePosition:(SCManagedCaptureDevicePosition)devicePosition
  274. {
  275. SCTraceStart();
  276. SC_GUARD_ELSE_RETURN(_captureImageFromVideoImmediately);
  277. _captureImageFromVideoImmediately = NO;
  278. @weakify(self);
  279. CFRetain(sampleBuffer);
  280. [_performer performImmediatelyIfCurrentPerformer:^{
  281. SCTraceStart();
  282. @strongify(self);
  283. SC_GUARD_ELSE_RETURN(self);
  284. [self _didCapturePhotoFromVideoBuffer];
  285. UIImageOrientation orientation = devicePosition == SCManagedCaptureDevicePositionBack
  286. ? UIImageOrientationRight
  287. : UIImageOrientationLeftMirrored;
  288. UIImage *videoImage = [UIImage imageWithPixelBufferRef:CMSampleBufferGetImageBuffer(sampleBuffer)
  289. backingType:UIImageBackingTypeCGImage
  290. orientation:orientation
  291. context:[CIContext contextWithOptions:nil]];
  292. UIImage *fullScreenImage = [self imageFromImage:videoImage
  293. currentZoomFactor:_zoomFactor
  294. targetAspectRatio:_aspectRatio
  295. fieldOfView:_fieldOfView
  296. state:_state];
  297. NSMutableDictionary *cameraInfo = [cameraInfoForBuffer(sampleBuffer) mutableCopy];
  298. cameraInfo[@"capture_image_from_video_buffer"] = @"enabled";
  299. [self _didFinishProcessingFromVideoBufferWithImage:fullScreenImage cameraInfo:cameraInfo];
  300. CFRelease(sampleBuffer);
  301. }];
  302. }
  303. - (void)_willBeginCapturePhotoFromVideoBuffer
  304. {
  305. SCTraceStart();
  306. @weakify(self);
  307. [_performer performImmediatelyIfCurrentPerformer:^{
  308. SCTraceStart();
  309. @strongify(self);
  310. SC_GUARD_ELSE_RETURN(self);
  311. if ([self->_delegate respondsToSelector:@selector(managedStillImageCapturerWillCapturePhoto:)]) {
  312. [self->_delegate managedStillImageCapturerWillCapturePhoto:self];
  313. }
  314. }];
  315. }
  316. - (void)_didCapturePhotoFromVideoBuffer
  317. {
  318. SCTraceStart();
  319. @weakify(self);
  320. [_performer performImmediatelyIfCurrentPerformer:^{
  321. SCTraceStart();
  322. @strongify(self);
  323. SC_GUARD_ELSE_RETURN(self);
  324. if ([self->_delegate respondsToSelector:@selector(managedStillImageCapturerDidCapturePhoto:)]) {
  325. [self->_delegate managedStillImageCapturerDidCapturePhoto:self];
  326. }
  327. }];
  328. }
  329. - (void)_didFinishProcessingFromVideoBufferWithImage:(UIImage *)image cameraInfo:(NSDictionary *)cameraInfo
  330. {
  331. SCTraceStart();
  332. @weakify(self);
  333. [_performer performImmediatelyIfCurrentPerformer:^{
  334. SCTraceStart();
  335. @strongify(self);
  336. SC_GUARD_ELSE_RETURN(self);
  337. [[SCLogger sharedInstance] logPreCaptureOperationFinishedAt:CACurrentMediaTime()];
  338. [[SCCoreCameraLogger sharedInstance]
  339. logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:CACurrentMediaTime()];
  340. sc_managed_still_image_capturer_capture_still_image_completion_handler_t completionHandler = _completionHandler;
  341. _completionHandler = nil;
  342. if (completionHandler) {
  343. completionHandler(image, cameraInfo, nil);
  344. }
  345. }];
  346. }
  347. - (void)captureStillImageFromVideoBuffer
  348. {
  349. SCTraceStart();
  350. @weakify(self);
  351. [_performer performImmediatelyIfCurrentPerformer:^{
  352. SCTraceStart();
  353. @strongify(self);
  354. SC_GUARD_ELSE_RETURN(self);
  355. AudioServicesPlaySystemSoundWithCompletion(kSCCameraShutterSoundID, nil);
  356. [self _willBeginCapturePhotoFromVideoBuffer];
  357. self->_captureImageFromVideoImmediately = YES;
  358. }];
  359. }
  360. @end