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
- Apple's AVCam Sample code was closely followed.
- This crash happens ONLY after one of more successful photo captures. Produced RAW file is perfectly fine.
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, butif you enable
AVCapturePhotoSettings
isHighResolutionPhotoEnabled
, you need to also enableisHighResolutionCaptureEnabled
(might behighResolutionCaptureEnabled
for you - what version of swift are you using?):your
PhotoCaptureDelegate
looks like it's going out of scope, which won't help. Assign it to a member variable to prolong its life.set a
.photo
preset on yourAVCaptureSession