如何避免在使用AVFoundation时通过iPhone相机阻塞用户界面?

11

我正在尝试在我的iPhone应用程序中嵌入一个简单的视图以快速拍照。一切都很好,但我遇到了相机启动时间的一些问题。在Apple的示例项目中,AVCaptureSession的-startRunning没有在主线程上执行,这似乎是必要的。我在视图初始化期间设置捕获会话,并在单独的线程中启动它。现在我在-didMoveToSuperview中添加了AVCaptureVideoPreviewLayer。没有多线程时一切正常(UI被阻止约1秒),但是使用GCD时,UI有时可以工作,有时需要等待很长时间才能“解冻”或显示预览。

如何可靠地处理相机启动延迟,而不会阻塞主线程(延迟本身不是问题)?

希望你们理解我的问题:D

提前感谢!

顺便说一句:这是我的概念验证项目(没有GCD),我现在正在重用它来制作另一个应用程序:http://github.com/dariolass/QuickShotView

2个回答

10

所以我自己想出了解决方法。这个代码对我有用,而且不会导致用户界面冻结得太厉害:

- (void)willMoveToSuperview:(UIView *)newSuperview {
    //capture session setup
    AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.rearCamera error:nil];
    AVCaptureStillImageOutput *newStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
                            AVVideoCodecJPEG, AVVideoCodecKey,
                            nil];
    [newStillImageOutput setOutputSettings:outputSettings];

    AVCaptureSession *newCaptureSession = [[AVCaptureSession alloc] init];

    if ([newCaptureSession canAddInput:newVideoInput]) {
        [newCaptureSession addInput:newVideoInput];
    }

    if ([newCaptureSession canAddOutput:newStillImageOutput]) {
        [newCaptureSession addOutput:newStillImageOutput];
        self.stillImageOutput = newStillImageOutput;
        self.captureSession = newCaptureSession;
    }
    // -startRunning will only return when the session started (-> the camera is then ready)
    dispatch_queue_t layerQ = dispatch_queue_create("layerQ", NULL);
    dispatch_async(layerQ, ^{
        [self.captureSession startRunning];
        AVCaptureVideoPreviewLayer *prevLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
            prevLayer.frame = self.previewLayerFrame;
            prevLayer.masksToBounds = YES;
            prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
            prevLayer.cornerRadius = PREVIEW_LAYER_EDGE_RADIUS;
        //to make sure were not modifying the UI on a thread other than the main thread, use dispatch_async w/ dispatch_get_main_queue
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.layer insertSublayer:prevLayer atIndex:0];
        });
    });
}

谢谢!对我来说关键是在后台线程上执行AVCaptureSessionstartRunningstopRunning - Tamás Sengel
@TamásSengel 怎么做? - Mudlabs
2
@Mudlabs 只需将代码嵌入到 DispatchQueue.global(qos: .background).async 块中即可。 - Tamás Sengel

-1

我认为避免的另一种方法是将你的“启动相机”代码放在viewDidAppear中,而不是放在viewWillAppear中。


这并没有解决在主线程上执行昂贵函数的问题。你仍然可以在早期执行它(例如,在viewWillAppear中),而不会阻塞主线程。 - Doug Mead

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接