【问题标题】:CIContext initialization crashCIContext 初始化崩溃
【发布时间】:2017-09-04 23:28:56
【问题描述】:

背景: 我正在使用以下两个选项运行 swift 2 应用程序。

选项 A: 用户可以输入数字登录。在这种情况下,他/她的图片显示在 UIImageView 中。

选项 B: 用户可以使用 NFC 标签登录。在这种情况下,UIImageView 被替换为显示实时摄像头流的摄像头层,并使用 CIContext 在按钮按下时捕获图像。

问题: 我面临的问题是,有时,当我选择选项 A(不使用相机层)时,应用程序崩溃。由于我无法确定地重现崩溃,因此我陷入了死胡同,无法理解应用程序崩溃的原因。

编辑:相机层在两个选项中都使用,但隐藏在选项 A 中。

Crashlytics 生成以下崩溃日志:

0   libswiftCore.dylib specialized _fatalErrorMessage(StaticString, StaticString, StaticString, UInt) -> () + 44
1   CameraLayerView.swift line 20 CameraLayerView.init(coder : NSCoder) -> CameraLayerView?
2   CameraLayerView.swift line 0 @objc CameraLayerView.init(coder : NSCoder) -> CameraLayerView?
3   UIKit -[UIClassSwapper initWithCoder:] + 248
32  UIKit UIApplicationMain + 208
33  AppDelegate.swift line 17 main
34  libdispatch.dylib (Missing)

我在 CameraLayerView 中检查了第 20 行,但这只是一个初始化语句

private let ciContext = CIContext(EAGLContext: EAGLContext(API: .OpenGLES2))

下面提到的是 CameraLayerView 文件。任何帮助将不胜感激

var captureSession = AVCaptureSession()
var sessionOutput = AVCaptureVideoDataOutput()
var previewLayer = AVCaptureVideoPreviewLayer()

private var pixelBuffer : CVImageBuffer!
private var attachments : CFDictionary!
private var ciImage : CIImage!
private let ciContext = CIContext(EAGLContext: EAGLContext(API: .OpenGLES2))
private var imageOptions : [String : AnyObject]!

var faceFound = false
var image : UIImage!

override func layoutSubviews() {
    previewLayer.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
    previewLayer.bounds = self.frame
    self.layer.borderWidth = 2.0
    self.layer.borderColor = UIColor.redColor().CGColor
}

func loadCamera() {
    let camera = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
    for device in camera {
        if device.position == .Front {
            do{
                for input in captureSession.inputs {
                    captureSession.removeInput(input as! AVCaptureInput)
                }
                for output in captureSession.outputs {
                    captureSession.removeOutput(output as! AVCaptureOutput)
                }
                previewLayer.removeFromSuperlayer()
                previewLayer.session = nil
                let input = try AVCaptureDeviceInput(device: device as! AVCaptureDevice)
                if captureSession.canAddInput(input) {
                    captureSession.addInput(input)
                    sessionOutput.videoSettings = [String(kCVPixelBufferPixelFormatTypeKey) : Int(kCVPixelFormatType_32BGRA)]
                    sessionOutput.setSampleBufferDelegate(self, queue: dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.rawValue), 0))
                    sessionOutput.alwaysDiscardsLateVideoFrames = true

                    if captureSession.canAddOutput(sessionOutput) {
                        captureSession.addOutput(sessionOutput)
                        captureSession.sessionPreset = AVCaptureSessionPresetPhoto
                        captureSession.startRunning()

                        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                        previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
                        switch UIDevice.currentDevice().orientation.rawValue {
                        case 1:
                            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.Portrait
                            break
                        case 2:
                            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.PortraitUpsideDown
                            break
                        case 3:
                            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
                            break
                        case 4:
                            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft
                            break
                        default:
                            break
                        }
                        self.layer.addSublayer(previewLayer)
                    }
                }

            } catch {
                print("Error")
            }
        }
    }
}

func takePicture() -> UIImage {
    self.previewLayer.removeFromSuperlayer()
    self.captureSession.stopRunning()
    return image
}

func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
    pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate)
    ciImage = CIImage(CVPixelBuffer: pixelBuffer!, options: attachments as? [String : AnyObject])
    if UIDevice.currentDevice().orientation == .PortraitUpsideDown {
        imageOptions = [CIDetectorImageOrientation : 8]
    } else if UIDevice.currentDevice().orientation == .LandscapeLeft {
        imageOptions = [CIDetectorImageOrientation : 3]
    } else if UIDevice.currentDevice().orientation == .LandscapeRight {
        imageOptions = [CIDetectorImageOrientation : 1]
    } else {
        imageOptions = [CIDetectorImageOrientation : 6]
    }
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: ciContext, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
    let features = faceDetector.featuresInImage(ciImage, options: imageOptions)
    if features.count == 0 {
        if faceFound == true {
            faceFound = false
            dispatch_async(dispatch_get_main_queue()) {
                self.layer.borderColor = UIColor.redColor().CGColor
            }
        }
    } else {
        if UIDevice.currentDevice().orientation == .PortraitUpsideDown {
            image = UIImage(CGImage: ciContext.createCGImage(ciImage, fromRect: ciImage.extent), scale: 1.0, orientation: UIImageOrientation.Left)
        } else if UIDevice.currentDevice().orientation == .LandscapeLeft {
            image = UIImage(CGImage: ciContext.createCGImage(ciImage, fromRect: ciImage.extent), scale: 1.0, orientation: UIImageOrientation.Down)
        } else if UIDevice.currentDevice().orientation == .LandscapeRight {
            image = UIImage(CGImage: ciContext.createCGImage(ciImage, fromRect: ciImage.extent), scale: 1.0, orientation: UIImageOrientation.Up)
        } else {
            image = UIImage(CGImage: ciContext.createCGImage(ciImage, fromRect: ciImage.extent), scale: 1.0, orientation: UIImageOrientation.Right)
        }
        if faceFound == false {
            faceFound = true
            for feature in features {
                if feature.isKindOfClass(CIFaceFeature) {
                    dispatch_async(dispatch_get_main_queue()) {
                        self.layer.borderColor = UIColor.greenColor().CGColor
                    }
                }
            }
        }
    }
}

【问题讨论】:

  • 您能否在本地(在 Xcode 中)或仅在用户的设备上重现崩溃?如果是这样,控制台日志说明了什么?
  • 我自己无法重现该错误。我也无法找到导致这次崩溃的事件链的模式。这种情况不会经常发生,但当它确实发生时,它会强制应用重新启动(这本身就是一种奇怪的行为,因为应用崩溃通常只会让应用崩溃而不是重新启动)
  • 我从未听说过 iOS 应用程序“崩溃并自动重启”(系统“应用程序”跳板除外)。
  • 这是一种奇怪的行为。通常,只有在您更改隐私设置时才会发生这种情况(在这种情况下,操作系统会强制重新启动应用程序以使新设置生效)。无论如何,感谢您的关注。希望有人能有一个想法。
  • 对不起,我帮不上忙;多年来没有使用过Core Image,那时也只有这么多。修复无法在本地重现的崩溃非常具有挑战性;祝你好运!

标签: ios image camera swift2


【解决方案1】:

我测试了一个理论,它奏效了。由于 ciContext 是使用视图初始化进行初始化的,因此应用程序似乎由于竞争条件而崩溃。我将 ciContext 的初始化移动到我的 loadCamera 方法中,此后它没有崩溃。

更新

我注意到的另一件事是,在互联网上的各种教程和博客文章中,声明 let ciContext = CIContext(EAGLContext: EAGLContext(API: .OpenGLES2)) 在两个单独的声明中声明,因此它变成了

let eaglContext = EAGLContext(API: .OpenGLES2)
let ciContext = CIContext(EAGLContext: eaglContext)

我仍然不知道究竟是什么导致应用程序崩溃,但这两个更改似乎已经解决了问题

正确答案

终于找到了罪魁祸首。在我使用 ciContext 的 viewController 中,我有一个没有失效的计时器,因此保持对 viewController 的强引用。在随后的每次访问中,它都会创建一个新的 viewController,而前一个从未从内存中释放。这导致内存超时。一旦超过某个阈值,ciContext 初始化程序将返回 nil,因为内存不足会导致应用程序崩溃。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多