AVFoundation Error Codes

2.4k views Asked by At

I am getting following two error messages when taking RAW and also JPEG photos. I have no way to diagnose these:

Error capturing photo: Error Domain=AVFoundationError- Domain Code=-11800 "The operation could not be completed" Userlnfo={NSLocalizedFailur- eReason=An unknown erroroccurred (42686), NSLocalizedDescription:The operation could not be completed, NSUnderlyingEr- ror=0x1c804bfa0 {Error Domain=NSOSStatusError- Domain Code=-12686 "(null)"}}

also...

Error capturing photo: Error Domain=AVFoundationError-Domain Code=-11800 The operation could not be completed" UserInfo.{NSLocalized-FailureReason=An unknown error occurred (-16802), NSLocalizedDescription=The operation could not be completed, NSUnderlyingEr-ror=0x1c4243f30 {Error Domain=NSOSStatusError-Domain Code=-16802 "(null)"}}

This seems to happen right after this function in AVCapturePhotoCaptureDelegate:

optional func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)  

These seem to happen randomly upon repeated photo capturing.

UPDATE

RAW Setup

       func updatePhotoSettings () {
self.photoOutput.setPreparedPhotoSettingsArray([])

let newPhotoSettings = AVCapturePhotoSettings(rawPixelFormatType: OSType(self.photoOutput.availableRawPhotoPixelFormatTypes.first!),
                                                          processedFormat: [AVVideoCodecKey : AVVideoCodecJPEG])
            newPhotoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: self.photoOutput.availablePhotoPixelFormatTypes[0]]
            if #available(iOS 11.0, *) {
                newPhotoSettings.embeddedThumbnailPhotoFormat =  [AVVideoCodecKey: self.photoOutput.availablePhotoCodecTypes[0]]
            }
            newPhotoSettings.isHighResolutionPhotoEnabled = true
            newPhotoSettings.flashMode = self.flashMode
            newPhotoSettings.isAutoStillImageStabilizationEnabled = false
            newPhotoSettings.livePhotoMovieFileURL = nil
            self.photoOutput.photoSettingsForSceneMonitoring = newPhotoSettings

            self.photoOutput.setPreparedPhotoSettingsArray([newPhotoSettings])

}

Capture Call:

final func snapImage () {

            let photoCaptureDelegate = PhotoCaptureDelegate(with: self.photoOutput.preparedPhotoSettingsArray.first!,
                                       willCapturePhotoAnimation: {
                //Shutter animation
                DispatchQueue.main.async { [unowned self] in
                    self.previewView.videoPreviewLayer.opacity = 0
                    UIView.animate(withDuration: 0.1, animations: {
                        self.previewView.videoPreviewLayer.opacity = 1
                    })
                }

            }, didCapturePhoto: {

                //Photo Saved animation

            }, completed:  { [unowned self] photoCaptureDelegate in
                self.inProgressPhotoCaptureDelegates[photoCaptureDelegate.requestedPhotoSettings.uniqueID] = nil
            })


            self.inProgressPhotoCaptureDelegates[photoCaptureDelegate.requestedPhotoSettings.uniqueID] = photoCaptureDelegate

            self.photoOutput.capturePhoto(with: self.photoOutput.preparedPhotoSettingsArray.first!, delegate: photoCaptureDelegate)

            DispatchQueue.main.async {
                //Goes to update photo settings for next shot
                self.updatePhotoSettings()
            }
        }
    }

Photo Capture Delegate

import AVFoundation
import Photos
import MobileCoreServices

class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {


         private(set) var requestedPhotoSettings     : AVCapturePhotoSettings

         private let willCapturePhotoAnimation       : () -> ()
         private let didCapturePhoto                 : () -> ()
         private let completed                       : (PhotoCaptureDelegate) -> ()

         private var jpegPhotoData                   : Data?

         private var dngPhotoData                    : Data?


    // MARK: - Initialization
    init(with requestedPhotoSettings: AVCapturePhotoSettings,
        willCapturePhotoAnimation: @escaping () -> (),
        didCapturePhoto: @escaping () -> (),
        completed: @escaping (PhotoCaptureDelegate) -> ())
    {
        self.requestedPhotoSettings = requestedPhotoSettings
        self.willCapturePhotoAnimation = willCapturePhotoAnimation
        self.didCapturePhoto = didCapturePhoto
        self.completed = completed
    }


    private final func didFinish() {
        self.completed(self)
    }

    // MARK: - Will Capture
    final func capture(_ captureOutput: AVCapturePhotoOutput, willCapturePhotoForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings) {
        willCapturePhotoAnimation()
    }

    final func capture(_ output: AVCapturePhotoOutput, willBeginCaptureForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings) {

    }


    // MARK: - iOS 11 Did Finish Photo
    @available(iOS 11.0, *)
    final func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

            if let error = error {

                PhotoCaptureDelegate.showError(sentMessage: "Error processing photo: \(error)")
                self.didCapturePhoto()

            } else {

                if photo.isRawPhoto {
                    self.dngPhotoData = photo.fileDataRepresentation()
                    return

                } else {
                    if self.jpegPhotoData == nil {
                        self.jpegPhotoData = photo.fileDataRepresentation()
                        self.didCapturePhoto()
                        return
                    }
                }
            }
    }


    // MARK: - Did Finish Capture
    final func capture(_ captureOutput: AVCapturePhotoOutput, didFinishCaptureForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {

        if let error = error {
            PhotoCaptureDelegate.showError(sentMessage: "Error capturing photo: \(error)")
            didFinish()
            return


        if PHPhotoLibrary.authorizationStatus() == .authorized {

                var temporaryDNGFileURL : URL? = nil
                self.dngPhotoData != nil {
                        temporaryDNGFileURL = URL(fileURLWithPath: NSTemporaryDirectory().appending("\(resolvedSettings.uniqueID).dng"))
                        do {
                            try self.dngPhotoData!.write(to: temporaryDNGFileURL!, options: [.atomic])
                        } catch let error as NSError {
                            PhotoCaptureDelegate.showError(sentMessage: "Could not write DNG Data: \(error)")
                            return
                        }
                }

                PHPhotoLibrary.shared().performChanges({
                    let creationRequest = PHAssetCreationRequest.forAsset()
                    if (self.jpegPhotoData != nil) {

                        creationRequest.addResource(with: .photo, data: self.jpegPhotoData!, options: nil)

                        if let temporaryDNGFileURL = temporaryDNGFileURL {

                            let companionDNGResourceOptions = PHAssetResourceCreationOptions()
                            companionDNGResourceOptions.shouldMoveFile = true
                            creationRequest.addResource(with: .alternatePhoto, fileURL: temporaryDNGFileURL, options: companionDNGResourceOptions)

                        }
                    }
                }, completionHandler: { [unowned self] success, error in

                    if let error = error {
                        PhotoCaptureDelegate.showError(sentMessage: "Error occurered while saving photo to photo library: \(error)")
                    }
                    if (temporaryDNGFileURL != nil) {
                        if FileManager.default.fileExists(atPath: temporaryDNGFileURL!.path) {
                            do {
                                try FileManager.default.removeItem(at: temporaryDNGFileURL!)
                            } catch let error as NSError {
                                PhotoCaptureDelegate.showError(sentMessage: "Could not remove DNG File: \(error)")
                            }
                        }
                    }

                })


            }

        else {
            PhotoCaptureDelegate.showError(sentMessage: "Not authorized to save photo")
            self.didFinish()
            return
        }

}



    private static func showError (sentMessage: String) {
        let alertController = UIAlertController(title: nil, message: sentMessage, preferredStyle: UIAlertControllerStyle.alert)
        let cancelAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert button title."), style: UIAlertActionStyle.cancel, handler: nil)
        alertController.addAction(cancelAction)
        let appDelegate  = UIApplication.shared.delegate as! AppDelegate
        appDelegate.window!.rootViewController!.present(alertController, animated: true, completion: nil)
    }

}

NOTES

  1. Apple's AVCam Sample code was closely followed.
  2. This crash happens ONLY after one of more successful photo captures. Produced RAW file is perfectly fine.
1

There are 1 answers

8
Gordon Childs On BEST ANSWER

UPDATE

You're calling updatePhotoSettings() before the image capture has finished which is causing errors. Try doing it from the completion handler or before you snap an image.

p.s. Really AVFoundation should've made this error harder to make or at least been clearer that this wouldn't work.

previous, incorrect guesses

You don't show how you configure the AVCaptureSession which is important, but

  1. if you enable AVCapturePhotoSettings isHighResolutionPhotoEnabled, you need to also enable isHighResolutionCaptureEnabled (might be highResolutionCaptureEnabled for you - what version of swift are you using?):

    self.photoOutput.isHighResolutionCaptureEnabled = true
    
  2. your PhotoCaptureDelegate looks like it's going out of scope, which won't help. Assign it to a member variable to prolong its life.

  3. set a .photo preset on your AVCaptureSession