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.

137 lines
5.3 KiB

  1. //
  2. // SCBlackCameraDetectorNoOutput.m
  3. // Snapchat
  4. //
  5. // Created by Derek Wang on 05/12/2017.
  6. //
  7. // This detector is used to detect the case that session is running, but there is no sample buffer output
  8. #import "SCBlackCameraNoOutputDetector.h"
  9. #import "SCBlackCameraReporter.h"
  10. #import <SCFoundation/SCAssertWrapper.h>
  11. #import <SCFoundation/SCLog.h>
  12. #import <SCFoundation/SCQueuePerformer.h>
  13. #import <SCFoundation/SCTraceODPCompatible.h>
  14. #import <SCFoundation/SCZeroDependencyExperiments.h>
  15. #import <SCLogger/SCCameraMetrics.h>
  16. #import <SCLogger/SCLogger.h>
  17. static CGFloat const kShortCheckingDelay = 0.5f;
  18. static CGFloat const kLongCheckingDelay = 3.0f;
  19. static char *const kSCBlackCameraDetectorQueueLabel = "com.snapchat.black-camera-detector";
  20. @interface SCBlackCameraNoOutputDetector () {
  21. BOOL _sampleBufferReceived;
  22. BOOL _blackCameraDetected;
  23. // Whether we receive first frame after we detected black camera, that's maybe because the checking delay is too
  24. // short, and we will switch to kLongCheckingDelay next time we do the checking
  25. BOOL _blackCameraRecovered;
  26. // Whether checking is scheduled, to avoid duplicated checking
  27. BOOL _checkingScheduled;
  28. // Whether AVCaptureSession is stopped, if stopped, we don't need to check black camera any more
  29. // It is set on main thread, read on background queue
  30. BOOL _sessionStoppedRunning;
  31. }
  32. @property (nonatomic) SCQueuePerformer *queuePerformer;
  33. @property (nonatomic) SCBlackCameraReporter *reporter;
  34. @end
  35. @implementation SCBlackCameraNoOutputDetector
  36. - (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter
  37. {
  38. self = [super init];
  39. if (self) {
  40. _queuePerformer = [[SCQueuePerformer alloc] initWithLabel:kSCBlackCameraDetectorQueueLabel
  41. qualityOfService:QOS_CLASS_BACKGROUND
  42. queueType:DISPATCH_QUEUE_SERIAL
  43. context:SCQueuePerformerContextCamera];
  44. _reporter = reporter;
  45. }
  46. return self;
  47. }
  48. - (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
  49. didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
  50. devicePosition:(SCManagedCaptureDevicePosition)devicePosition
  51. {
  52. // The block is very light-weight
  53. [self.queuePerformer perform:^{
  54. if (_blackCameraDetected) {
  55. // Detected a black camera case
  56. _blackCameraDetected = NO;
  57. _blackCameraRecovered = YES;
  58. SCLogCoreCameraInfo(@"[BlackCamera] Black camera recovered");
  59. if (SCExperimentWithBlackCameraReporting()) {
  60. [[SCLogger sharedInstance] logUnsampledEvent:KSCCameraBlackCamera
  61. parameters:@{
  62. @"type" : @"RECOVERED"
  63. }
  64. secretParameters:nil
  65. metrics:nil];
  66. }
  67. }
  68. // Received buffer!
  69. _sampleBufferReceived = YES;
  70. }];
  71. }
  72. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didStartRunning:(SCManagedCapturerState *)state
  73. {
  74. SCAssertMainThread();
  75. if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
  76. SCLogCoreCameraInfo(@"[BlackCamera] In background, skip checking");
  77. return;
  78. }
  79. _sessionStoppedRunning = NO;
  80. [self.queuePerformer perform:^{
  81. SCTraceODPCompatibleStart(2);
  82. if (_checkingScheduled) {
  83. SCLogCoreCameraInfo(@"[BlackCamera] Checking is scheduled, skip");
  84. return;
  85. }
  86. if (_sessionStoppedRunning) {
  87. SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped, should not check");
  88. return;
  89. }
  90. _sampleBufferReceived = NO;
  91. if (_blackCameraRecovered) {
  92. SCLogCoreCameraInfo(@"[BlackCamera] Last black camera recovered, let's wait longer to check this time");
  93. }
  94. SCLogCoreCameraInfo(@"[BlackCamera] Schedule black camera checking");
  95. [self.queuePerformer perform:^{
  96. SCTraceODPCompatibleStart(2);
  97. if (!_sessionStoppedRunning) {
  98. if (!_sampleBufferReceived) {
  99. _blackCameraDetected = YES;
  100. [_reporter reportBlackCameraWithCause:SCBlackCameraNoOutputData];
  101. [self.delegate detector:self didDetectBlackCamera:managedCapturer];
  102. } else {
  103. SCLogCoreCameraInfo(@"[BlackCamera] No black camera");
  104. _blackCameraDetected = NO;
  105. }
  106. } else {
  107. SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped");
  108. _blackCameraDetected = NO;
  109. }
  110. _blackCameraRecovered = NO;
  111. _checkingScheduled = NO;
  112. }
  113. after:_blackCameraRecovered ? kLongCheckingDelay : kShortCheckingDelay];
  114. _checkingScheduled = YES;
  115. }];
  116. }
  117. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didStopRunning:(SCManagedCapturerState *)state
  118. {
  119. SCAssertMainThread();
  120. _sessionStoppedRunning = YES;
  121. [self.queuePerformer perform:^{
  122. SCTraceODPCompatibleStart(2);
  123. _sampleBufferReceived = NO;
  124. }];
  125. }
  126. @end