1

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
Ben Jones
  • 919
  • 1
  • 8
  • 22

0 Answers0