Camera giving duplicate images in iOS 14 and above

Viewed 74
class CaptureController: NSObject, AVCapturePhotoCaptureDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {

    static let shared = CaptureController()

    weak var delegate: CaptureControllerDelegate?

    private let session = AVCaptureSession()
    private var cameraInput: AVCaptureDeviceInput?
    private let photoOutput = AVCapturePhotoOutput()
    private let previewVideoOutput = AVCaptureVideoDataOutput()

    private let previewRectangleDetectionController = RectangleDetectionController()

    var flashMode = AVCaptureDevice.FlashMode.off
    
    override init() {
        super.init()
        previewVideoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "video output buffer queue", qos: .userInteractive))
    }

    func startCaptureSession() {
        self.session.sessionPreset = AVCaptureSession.Preset.photo

        guard let backCameraDevice = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .back) else {
            assertionFailure("Could not find a back facing camera")
            return
        }

        do {
            try cameraInput = AVCaptureDeviceInput(device: backCameraDevice)
            guard let cameraInput = cameraInput else { return }
            if self.session.canAddInput(cameraInput) {
                self.session.addInput(cameraInput)
            }
        } catch {
            print("AVCaptureDeviceInput error: \(error)")
        }

        if self.session.canAddOutput(self.photoOutput) {
            if UIDevice.supportsJPGCaptureFormatOnly() {
                self.photoOutput.isHighResolutionCaptureEnabled = false
                self.photoOutput.setPreparedPhotoSettingsArray(
                    [AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil
                )
            }
            self.session.addOutput(self.photoOutput)
        }

        if self.session.canAddOutput(self.previewVideoOutput) {
            self.session.addOutput(self.previewVideoOutput)
            if let connection = self.previewVideoOutput.connection(with: .video) {
                connection.videoOrientation = .portrait
            }
        }

        self.session.startRunning()
    }

    func endCaptureSession() {
        self.session.outputs.forEach { output in
            self.session.removeOutput(output)
        }

        self.session.inputs.forEach { input in
            self.session.removeInput(input)
        }

        self.session.stopRunning()
    }

    func takePhoto() {
        if UIDevice.supportsJPGCaptureFormatOnly() {
            let settings = AVCapturePhotoSettings()
            if UIImagePickerController.isFlashAvailable(for: .rear) {
                settings.flashMode = flashMode
            }
            self.photoOutput.capturePhoto(with: settings, delegate: self)
        } else {
            guard let rawFormatFileType = self.photoOutput.availableRawPhotoFileTypes.first,
                  let rawFormatPixelType = self.photoOutput.supportedRawPhotoPixelFormatTypes(for: rawFormatFileType).first
            else {
                print("This device does not support RAW image capture format.")
                return
            }
            
            guard let processFormat = self.photoOutput.availablePhotoCodecTypes.first else {
                print("No supported codecs")
                return
            }
            let settings = AVCapturePhotoSettings(rawPixelFormatType: rawFormatPixelType.uint32Value,
                                                  processedFormat: [AVVideoCodecKey: processFormat])
            settings.flashMode = flashMode
            self.photoOutput.capturePhoto(with: settings, delegate: self)
        }
    }

    // MARK: - AVCapturePhotoCaptureDelegate

    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingRawPhoto rawSampleBuffer: CMSampleBuffer?,
                     previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
                     resolvedSettings: AVCaptureResolvedPhotoSettings,
                     bracketSettings: AVCaptureBracketedStillImageSettings?,
                     error: Error?) {
        
        if let error = error {
            print("Capture failed: \(error)")
        }
    }

    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingPhoto photo: AVCapturePhoto,
                     error: Error?) {
        if let error = error {
            print("Capture failed: \(error)")
            self.delegate?.captureDidFail()
        } else {
            DispatchQueue.global(qos: .userInitiated).async {
                guard let scannedImage = self.processImage(from: photo, withDetectedRectangle: self.previewRectangleDetectionController.bestRectangle) else {
                    return
                }
                self.delegate?.didCaptureImage(scannedImage)
            }
        }
    }

    private func processImage(from photo: AVCapturePhoto, withDetectedRectangle detectedRect: DetectedRectangle?) -> ScannedImage? {
        guard let imageData = photo.fileDataRepresentation() else { return nil }
        guard let image = CIImage(data: imageData) else { return nil }

        // the image data always comes landscape oriented from the AVCapturePhoto, but the CIImage doesn't know.
        // We have to flip it "right" here to match the expected portrait orientation ("up" would be the typical
        // portrait orientation).
        let targetOrientation: CGImagePropertyOrientation
        if let orientationRawValue = photo.metadata[kCGImagePropertyOrientation as String] as? UInt32,
            let photoOrientation = CGImagePropertyOrientation(rawValue: orientationRawValue) {
            targetOrientation = photoOrientation
        } else {
            targetOrientation = .right
        }

        let rotatedImage = image.oriented(targetOrientation)
        guard let flattenedImage = ImageManipulationController.shared.flatten(rotatedImage) else { return nil }

        var editingCorners: Quadrilateral?
        if let detectedRect = detectedRect {
            // match scale
            let scaleTransform = ResizingHelpers.transform(from: detectedRect.imageSize, to: flattenedImage.extent.size, destinationContentMode: .scaleToFill)
            let scaledRect = detectedRect.rectangle.applying(scaleTransform)

            // inset
            let insetAmount: CGFloat = 0.97
            let insetScale = CGAffineTransform(scaleX: insetAmount, y: insetAmount)
            let insetScaledRect = scaledRect.applying(insetScale)
            let distanceToCenter = scaledRect.center - insetScaledRect.center
            let insetTranslation = CGAffineTransform(translationX: distanceToCenter.x, y: distanceToCenter.y)
            let finalInsettingTransform = insetScale.concatenating(insetTranslation)

            editingCorners = scaledRect.applying(finalInsettingTransform)
        }

        return ScannedImage(fullResolutionImage: flattenedImage, editingCorners: editingCorners)
    }

    // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate

    func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        let ciImage = CIImage(cvImageBuffer: imageBuffer)

        self.previewRectangleDetectionController.processRectangles(in: ciImage)
        let bestRect = self.previewRectangleDetectionController.bestRectangle
        self.delegate?.didUpdatePreviewImage(ciImage, withDetectedRectangle: bestRect)
    }
    
}

Above code are working fine in iOS 13 and below. But in iOS 14 and above producing duplicate Image. I tried in internet but no hope to find why it's coming duplicates.

Note: While pressing camera button , its appearing in two images

0 Answers
Related