我正在尝试使用 AVCapturePhotoOutput 从连接的 iPad 截取屏幕截图,但是当 capturePhoto 调用我的委托didFinishingProcessingPhoto
方法时,它会给出未知错误。我想我遵循了 AVFoundation 文档中关于如何捕获照片的说明,但似乎没有一个描述屏幕捕获,而且大部分都集中在 iOS 上。任何想法如何避免这个错误(甚至它可能意味着什么?)
下面的相关代码和输出。
//
// 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)
}
}
}
输出:
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