ios - 在 iOS 相机中停止相机捕获 session

标签 ios avcapturesession avcapturedevice avcapture

我使用 AVCaptureSession、AVCaptureDeviceInput、AVCaptureVideoDataOutput 从 iPhone 摄像头捕获图像。

图像捕获实现为

        dispatch_queue_t sessionQueue = dispatch_queue_create("session queue", DISPATCH_QUEUE_SERIAL);
        [self setSessionQueue:sessionQueue];
        //we will use a separate dispatch session not to block the main queue in processing
        dispatch_queue_t  im_processingQueue = dispatch_queue_create("im_processing queue", DISPATCH_QUEUE_SERIAL);
        [self setIm_processingQueue:im_processingQueue];

        dispatch_async(sessionQueue, ^{
            [self setBackgroundRecordingID:UIBackgroundTaskInvalid];

            NSError *error = nil;

            AVCaptureDevice *videoDevice = [RecordViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];

            AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];

            if (error)
            {
                NSLog(@"%@", error);
            }
            [(AVCaptureVideoPreviewLayer *)[[self previewView] layer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
            if ([session canAddInput:videoDeviceInput])
            {
                [session addInput:videoDeviceInput];
                [self setVideoDeviceInput:videoDeviceInput];

                dispatch_async(dispatch_get_main_queue(), ^{
                    // Why are we dispatching this to the main queue?
                    // Because AVCaptureVideoPreviewLayer is the backing layer for AVCamPreviewView and UIView can only be manipulated on main thread.
                    // Note: As an exception to the above rule, it is not necessary to serialize video orientation changes on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
                    //[self previewView] layer

                    [[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]];
                });
            }

            AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
            AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];

            if (error)
            {
                NSLog(@"%@", error);
            }

            if ([session canAddInput:audioDeviceInput])
            {
                [session addInput:audioDeviceInput];
            }

            AVCaptureVideoDataOutput *vid_Output = [[AVCaptureVideoDataOutput alloc] init];
            [vid_Output setSampleBufferDelegate:self queue:im_processingQueue];
            vid_Output.alwaysDiscardsLateVideoFrames = YES;
            // Set the video output to store frame in BGRA (It is supposed to be faster)
            NSDictionary* videoSettings = @{(__bridge NSString*)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]};
            [vid_Output setVideoSettings:videoSettings];
            if ([session canAddOutput:vid_Output])
            {
                [session addOutput:vid_Output];
                AVCaptureConnection *connection = [vid_Output connectionWithMediaType:AVMediaTypeVideo];
                if ([connection isVideoStabilizationSupported])
                    //[connection setEnablesVideoStabilizationWhenAvailable:YES];
                    connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
                [self setVid_Output:vid_Output];

            }

      });

viewWillAppear 中,捕获 session 运行为

- (void)viewWillAppear:(BOOL)animated
{
    //[super viewWillAppear:YES];
    dispatch_async([self sessionQueue], ^{
        [self addObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:SessionRunningAndDeviceAuthorizedContext];

        [self addObserver:self forKeyPath:@"vid_Output.recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]];

        __weak RecordViewController *weakSelf = self;
        [self setRuntimeErrorHandlingObserver:[[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureSessionRuntimeErrorNotification object:[self session] queue:nil usingBlock:^(NSNotification *note) {
            RecordViewController *strongSelf = weakSelf;
            dispatch_async([strongSelf sessionQueue], ^{
                // Manually restarting the session since it must have been stopped due to an error.
                [[strongSelf session] startRunning];

            });
        }]];
        [[self session] startRunning];
    });
}

然后停止为

- (void) stopCapturingCameraImages
{
    dispatch_async([self sessionQueue], ^{
        [[self session] stopRunning];

        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]];
        [[NSNotificationCenter defaultCenter] removeObserver:[self runtimeErrorHandlingObserver]];

        [self removeObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" context:SessionRunningAndDeviceAuthorizedContext];

        [self removeObserver:self forKeyPath:@"vid_Output.recording" context:RecordingContext];
    });

}

问题在于移除观察者,

[self removeObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" context:SessionRunningAndDeviceAuthorizedContext];

[self removeObserver:self forKeyPath:@"vid_Output.recording" context:RecordingContext];

程序在运行这两个 removeObservers 后崩溃了。 有什么问题吗?

编辑:

stopCapturingCameraImages is called from
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{

    @autoreleasepool {
         [_processing Searchobject_using_CPU_CascadeClassifier_for:img with_return_Rect:_rects];

dispatch_async(dispatch_get_main_queue(), ^{
                    for (int lc = 0; lc < 
                    if(_rects.count >0){
                        [ self stopCapturingCameraImages];
                    }

                });

}

编辑 1:

根据@SwiftArchitect的建议,我把if ([[self session] isRunning])。然后就可以了。 我实现为

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:YES];
    [self stopCapturingCameraImages];
}

- (void) stopCapturingCameraImages
{
    dispatch_async([self sessionQueue], ^{
        if ([[self session] isRunning]){
        [[self session] stopRunning];

        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]];
        [[NSNotificationCenter defaultCenter] removeObserver:[self runtimeErrorHandlingObserver]];

            [self removeObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" context:SessionRunningAndDeviceAuthorizedContext];

            [self removeObserver:self forKeyPath:@"vid_Output.recording" context:RecordingContext];
        }
    });

}

最佳答案

到时候:

dispatch_async([self sessionQueue], ^{
    // ...

    [self removeObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" context:SessionRunningAndDeviceAuthorizedContext];

    [self removeObserver:self forKeyPath:@"vid_Output.recording" context:RecordingContext];
});

被执行时,self(UIViewController)可能已经执行了它的 viewWillDisappear,并移除了观察者。

Whats的执行顺序在dispatch_get_main_queuesessionQueue中执行,并不一定是你所期望的,甚至是可预测的。


修复可能很简单,只需在执行 removeObserver 之前添加一个检查,例如 if [[self session] isRunning],而不是添加一个信号量。

关于ios - 在 iOS 相机中停止相机捕获 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34281487/

相关文章:

ios - iPhone 7+,ios 11.2 : Depth data delivery is not supported in the current configuration

ios - 同时调用 ViewController 的多个实例导致应用程序在 AppDelegate 中崩溃

ios - 在 Swift 的导航栏中设置图像

ios - UISearchBar 提示出现在栏上,而不是在栏的顶部

ios - GANTracker for iPhone SDK 自定义变量给出错误 195946409

swift - AVCaptureVideoPreviewLayer预览位置开始于导航栏(toolbar)下

ios - dealloc AVCaptureVideoPreviewLayer 期间罕见崩溃

ios - AVCaptureDevice.requestAccessForMediaType 是否仅适用于未确定的授权状态?

ios - 检查 bool 是否为真 x 秒 - Swift

objective-c - 两部相同的 iPhone,一部在 AVCaptureDeviceTypeBuiltInDualCamera 上返回 exc_bad_access