I'm trying to take a screenshot from a connected iPad using AVCapturePhotoOutput, but when capturePhoto calls my delegate's didFinishingProcessingPhoto
method, it gives an unknown error. I think I followed the instructions from the AVFoundation doc on how to capture photos, but none of it seems to describe screen capture, and most of it is focused on iOS. Any ideas how to avoid this error (or even what it might mean?)
Relevant code and output below.
//
// AVCapture.swift
// presenterMode
//
// Created by Ben Jones on 1/8/22.
//
import Foundation
import AVFoundation
import CoreMediaIO
import Combine
class AVDeviceManager : NSObject, ObservableObject {
@Published var avWrappers : [AVWrapper] = []
private var delegates : [DevicePhotoDelegate] = []
private let connectionPublisher = NotificationCenter.default
.publisher(for: NSNotification.Name.AVCaptureDeviceWasConnected)
private var subscriptionHandle : AnyCancellable? = nil
//let disconnectionPublisher = NotificationCenter.default
// .publisher(for: NSNotification.Name.AVCaptureDeviceWasDisconnected)
override init(){
super.init()
//without this ipads won't show up as capture dvices
//From https://stackoverflow.com/questions/48646470/ios-device-not-listed-by-avcapturedevice-devices-unless-quicktime-is-opened
var prop = CMIOObjectPropertyAddress(
mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyAllowScreenCaptureDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster))
var allow : UInt32 = 1
let dataSize : UInt32 = 4
let zero : UInt32 = 0
CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &prop, zero, nil, dataSize, &allow)
getCaptureDevices()
subscriptionHandle = connectionPublisher.sink { (message) in
print("got a message from the connection publisher")
let device : AVCaptureDevice = message.object as! AVCaptureDevice;
print(device.deviceType, " localized name: ", device.localizedName, " model id", device.modelID)
var session = AVCaptureSession();
let photoOutput = AVCapturePhotoOutput()
session.beginConfiguration()
guard session.canAddOutput(photoOutput) else { return }
session.sessionPreset = .photo
session.addOutput(photoOutput)
print("output added to session")
do {
try session.addInput(AVCaptureDeviceInput(device: device));
print("input added to session")
session.commitConfiguration();
session.startRunning();
print("session running")
let photoSettings = AVCapturePhotoSettings()
print("about to try to capture a photo with", device.localizedName)
let del = DevicePhotoDelegate(dev: device, man: self)
self.delegates.append(del)
photoOutput.capturePhoto(with: photoSettings, delegate: del)
} catch {
print("couldn't add capture device as input")
}
}
}
func getCaptureDevices() -> Void {
//not relevant for the ipad capture since the ipad doesn't show up in this list at startup
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes:
[.externalUnknown, .builtInWideAngleCamera], mediaType: .video, position: .unspecified)
self.avWrappers = discoverySession.devices.map({dev -> AVWrapper in
return AVWrapper(dev: dev, im: GlobalViewModel.staticImage)
})
print(self.avWrappers);
}
}
}
}
struct AVWrapper : Identifiable {
let device: AVCaptureDevice
let imagePreview :CGImage
let id: ObjectIdentifier
init(dev: AVCaptureDevice, im : CGImage){
device = dev
imagePreview = im
id = ObjectIdentifier(device)
}
}
class DevicePhotoDelegate : NSObject, AVCapturePhotoCaptureDelegate {
let device : AVCaptureDevice
let manager : AVDeviceManager
init(dev : AVCaptureDevice, man : AVDeviceManager){
device = dev
manager = man
}
@objc(captureOutput:didFinishProcessingPhoto:error:) func photoOutput(_ output: AVCapturePhotoOutput,
didFinishProcessingPhoto photo: AVCapturePhoto,
error: Error?){
print("got the ipad photo!")
if (error != nil) {
print("Error: ", error)
}
manager.avWrappers.append(AVWrapper(dev: device,
im: photo.cgImageRepresentation()!))
}
func photoOutput(_: AVCapturePhotoOutput, willBeginCaptureFor: AVCaptureResolvedPhotoSettings){
print("will begin capture")
}
func photoOutput(_: AVCapturePhotoOutput, willCapturePhotoFor: AVCaptureResolvedPhotoSettings){
print("will capture photo")
}
func photoOutput(_: AVCapturePhotoOutput, didFinishCaptureFor: AVCaptureResolvedPhotoSettings, error: Error?){
print("capture complete")
if (error != nil) {
print("Error: ", error)
}
}
}
Output:
got a message from the connection publisher
AVCaptureDeviceType(_rawValue: AVCaptureDeviceTypeExternalUnknown) localized name: Ben’s iPad model id iOS Device
output added to session
input added to session
2022-01-08 20:26:51.990119-0700 presenterMode[71468:6611851] [] CMIOHardware.cpp:379:CMIOObjectGetPropertyData Error: 2003332927, failed
2022-01-08 20:26:51.990198-0700 presenterMode[71468:6611851] [] CMIO_DALA_Object.cpp:518:GetPropertyData Error: 2003332927, got an error getting the property data mObjectID 39
2022-01-08 20:26:51.994027-0700 presenterMode[71468:6611851] [] CMIOHardware.cpp:420:CMIOObjectSetPropertyData property isn't settable pft glob
2022-01-08 20:26:51.994117-0700 presenterMode[71468:6611851] [] CMIOHardware.cpp:450:CMIOObjectSetPropertyData Error: 1852797029, failed
2022-01-08 20:26:51.995318-0700 presenterMode[71468:6611851] [] CMIOHardware.cpp:379:CMIOObjectGetPropertyData Error: 2003332927, failed
2022-01-08 20:26:51.995525-0700 presenterMode[71468:6611851] [] CMIOHardware.cpp:379:CMIOObjectGetPropertyData Error: 2003332927, failed
2022-01-08 20:26:51.995552-0700 presenterMode[71468:6611851] [] CMIO_DALA_Object.cpp:518:GetPropertyData Error: 2003332927, got an error getting the property data mObjectID 39
session running
about to try to capture a photo with Ben’s iPad
will begin capture
will capture photo
got the ipad photo!
Error: Optional(Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedDescription=The operation could not be completed, NSLocalizedFailureReason=An unknown error occurred (-11800)})
presenterMode/AVCapture.swift:123: Fatal error: Unexpectedly found nil while unwrapping an Optional value
2022-01-08 20:26:55.542549-0700 presenterMode[71468:6611851] presenterMode/AVCapture.swift:123: Fatal error: Unexpectedly found nil while unwrapping an Optional value