ios - AVCaptureSession stopRunning 方法造成可怕的挂起

标签 ios objective-c avfoundation avcapturesession

使用 Ray Wenderlich's QRCode reader from Chapter 22 of iOS7 Tutorials ,我正在成功读取我当前应用程序的二维码。我现在扩展它,在成功读取 QRCode 后,我想存储已读取的 AVMetadataMachineReadableCodeObjectstringValue,转到新 View ,并在新 View ,或多或少准确地说明了大多数 QRCode 阅读器应用程序(如 RedLaser 等...)如何处理条形码和 QRCode。

但是,我调用了 [captureSession stopRunning](这样它就不再读取任何 QRCode 并触发额外的 segues)并且有 10 多秒的挂起。我已尝试根据 this SO question 实现 async 调用,然而无济于事。我也看过these SO Questions他们似乎不适合这个目的。

有谁知道如何去除这个悬挂物?

代码如下:

#import "BMQRCodeReaderViewController.h"
#import "NSString+containsString.h"
#import "BMManualExperimentDataEntryViewController.h"
@import AVFoundation;

@interface BMQRCodeReaderViewController ()
    <AVCaptureMetadataOutputObjectsDelegate>
@end

@implementation BMQRCodeReaderViewController {
    AVCaptureSession *_captureSession;
    AVCaptureDevice *_videoDevice;
    AVCaptureDeviceInput *_videoInput;
    AVCaptureVideoPreviewLayer *_previewLayer;
    BOOL _running;
    AVCaptureMetadataOutput *_metadataOutput;
}

- (void)setupCaptureSession { // 1
    if (_captureSession) return;
    // 2
    _videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    if (!_videoDevice) {
        NSLog(@"No video camera on this device!"); return;
    }
    // 3
    _captureSession = [[AVCaptureSession alloc] init];
    // 4
    _videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:nil];
    // 5
    if ([_captureSession canAddInput:_videoInput]) { [_captureSession addInput:_videoInput];
    }
    // 6
    _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    _metadataOutput = [[AVCaptureMetadataOutput alloc] init];
    dispatch_queue_t metadataQueue = dispatch_queue_create("com.razeware.ColloQR.metadata", 0);

    [_metadataOutput setMetadataObjectsDelegate:self queue:metadataQueue];
    if ([_captureSession canAddOutput:_metadataOutput]) { [_captureSession addOutput:_metadataOutput];
    }
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
       fromConnection:(AVCaptureConnection *)connection {

    // This fancy BOOL is just helping me fire the segue when the correct string is found
     __block NSNumber *didFind = [NSNumber numberWithBool:NO];

    [metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject *obj, NSUInteger idx, BOOL *stop) {

        AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)obj;

        NSLog(@"Metadata: %@", readableObject);

        // [ containsString is a category I extended for NSString, just FYI
        if ([readableObject.stringValue containsString:@"CorrectString"]) {

            didFind = [NSNumber numberWithBool:YES];
             NSLog(@"Found it");
             _testName = @"NameOfTest";
             *stop = YES;
             return;
         }
    }];

    if ([didFind boolValue]) {
        NSLog(@"Confirming we found it");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self stopRunning];
        });
        _labelTestName.text = _testName;
        [self performSegueWithIdentifier:@"segueFromFoundQRCode" sender:self];
    }
    else {
        NSLog(@"Did not find it");
    }
}

- (void)startRunning {
    if (_running)
        return;

    [_captureSession startRunning];
    _metadataOutput.metadataObjectTypes = _metadataOutput.availableMetadataObjectTypes;
    _running = YES;
}
- (void)stopRunning {
    if (!_running) return;

    [_captureSession stopRunning];
    _running = NO;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    [self setupCaptureSession];
    [self setupNavBar];
    [self startRunning];
    _previewLayer.frame = _previewView.bounds;
    [_previewView.layer addSublayer:_previewLayer];
}

最佳答案

我已经陆续解决了这个问题。问题是委托(delegate)方法调用

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
       fromConnection:(AVCaptureConnection *)connection

在后台运行。这是通过 [NSThread isMainThread] 调用确定的,但它失败了。

解决方案是从 QRCode 中找到正确的字符串值,在后台停止 captureSession,然后在主线程上调用您的 segue。解决方案看起来是这样的:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
       fromConnection:(AVCaptureConnection *)connection {

    // This fancy BOOL is just helping me fire the segue when the correct string is found
     __block NSNumber *didFind = [NSNumber numberWithBool:NO];

    [metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject *obj, NSUInteger idx, BOOL *stop) {

        AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)obj;

        NSLog(@"Metadata: %@", readableObject);
        if ([NSThread isMainThread]) {
            NSLog(@"Yes Main Thread");
        }
        else {
            NSLog(@"Not main thread");
        }
        // [ containsString is a category I extended for NSString, just FYI
        if ([readableObject.stringValue containsString:@"Biomeme"]) {
            //NSLog(@"this is a test: %@", getTestName);
            didFind = [NSNumber numberWithBool:YES];
             NSLog(@"Found it");
             _testName = readableObject.stringValue;
             *stop = YES;
             return;
         }
    }];


    if ([didFind boolValue]) {
        NSLog(@"Confirming we found it");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSDate *start = [NSDate date];

            [self stopRunning];
            NSLog(@"time took: %f", -[start timeIntervalSinceNow]);

            // *** Here is the key, make your segue on the main thread
            dispatch_async(dispatch_get_main_queue(), ^{
                [self performSegueWithIdentifier:@"segueFromFoundQRCode" sender:self];
                _labelTestName.text = _testName;
            });

        });



    }
    else {
        NSLog(@"Did not find it");
    }
}

关于ios - AVCaptureSession stopRunning 方法造成可怕的挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19525132/

相关文章:

ios - 将 CADisplayLink.timestamp CFTimeInterval 转换为主机时间 (uint64)

android - React native null 不是 Android 上的对象(评估 '_scrollView.scrollTo' )

ios - 以编程方式激活 UISearchBar 阻止用户与其交互

objective-c - 数学逻辑替代 if 语句

objective-c - AVPlayer 缓冲百分比

iphone - iOS - 自动调整 CVPixelBufferRef 的大小

ios - UITableView 在点击屏幕顶部的状态栏时滚动到顶部

ios - UIImageView 在以编程方式创建时显示离散图像

ios - iOS5上奇怪的UIButton行为

iphone - 如何在我的 iPhone 应用程序中使用 NSError?