我正在尝试遵循本教程,并在视图控制器上设置相机。
我有相同的PreviewView.swift:
class PreviewView: UIView {
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
guard let layer = layer as? AVCaptureVideoPreviewLayer else {
fatalError("Expected `AVCaptureVideoPreviewLayer` type for layer. Check PreviewView.layerClass implementation.")
}
//layer.videoGravity = .resizeAspectFill
return layer
}
var session: AVCaptureSession? {
get {
return videoPreviewLayer.session
}
set {
videoPreviewLayer.session = newValue
//Should I add videoPreviewLayer to the PreviewView's sublayer here somehow?
}
}
// MARK: UIView
override class var layerClass: AnyClass {
return AVCaptureVideoPreviewLayer.self
}
}
然后在CameraViewController中:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Camera"
guard AVCaptureDevice.default(for: AVMediaType.video) != nil else {
fatalError("No video device found")
}
// Disable UI. Enable the UI later, if and only if the session starts running.
recordButton.isEnabled = false
// Set up the video preview view.
previewView.session = session
/*
Check video authorization status. Video access is required and audio
access is optional. If the user denies audio access, AVCam won't
record audio during movie recording.
*/
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
// The user has previously granted access to the camera.
break
case .notDetermined:
/*
The user has not yet been presented with the option to grant
video access. We suspend the session queue to delay session
setup until the access request has completed.
Note that audio access will be implicitly requested when we
create an AVCaptureDeviceInput for audio during session setup.
*/
sessionQueue.suspend()
AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
if !granted {
self.setupResult = .notAuthorized
}
self.sessionQueue.resume()
})
default:
// The user has previously denied access.
setupResult = .notAuthorized
}
/*
Setup the capture session.
In general, it is not safe to mutate an AVCaptureSession or any of its
inputs, outputs, or connections from multiple threads at the same time.
Don't perform these tasks on the main queue because
AVCaptureSession.startRunning() is a blocking call, which can
take a long time. We dispatch session setup to the sessionQueue, so
that the main queue isn't blocked, which keeps the UI responsive.
*/
sessionQueue.async {
self.configureSession()
}
}
private func configureSession() {
if setupResult != .success {
return
}
session.beginConfiguration()
/*
We do not create an AVCaptureMovieFileOutput when setting up the session because
Live Photo is not supported when AVCaptureMovieFileOutput is added to the session.
*/
session.sessionPreset = .high
// Add video input.
do {
var defaultVideoDevice: AVCaptureDevice?
// Choose the back dual camera if available, otherwise default to a wide angle camera.
if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) {
defaultVideoDevice = dualCameraDevice
} else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
// If a rear dual camera is not available, default to the rear wide angle camera.
defaultVideoDevice = backCameraDevice
}
guard let videoDevice = defaultVideoDevice else {
print("Default video device is unavailable.")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice)
if session.canAddInput(videoDeviceInput) {
session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput
DispatchQueue.main.async {
/*
Dispatch video streaming to the main queue because AVCaptureVideoPreviewLayer is the backing layer for PreviewView.
You can manipulate UIView only on the main thread.
Note: As an exception to the above rule, it is not necessary to serialize video orientation changes
on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
Use the status bar orientation as the initial video orientation. Subsequent orientation changes are
handled by CameraViewController.viewWillTransition(to:with:).
*/
//
// self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
// self.videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
// self.videoPreviewLayer?.frame = self.view.layer.bounds
// self.previewView.layer.addSublayer(self.videoPreviewLayer!)
let statusBarOrientation = UIApplication.shared.statusBarOrientation
var initialVideoOrientation: AVCaptureVideoOrientation = .portrait
if statusBarOrientation != .unknown {
if let videoOrientation = AVCaptureVideoOrientation(interfaceOrientation: statusBarOrientation) {
initialVideoOrientation = videoOrientation
}
}
self.previewView.videoPreviewLayer.connection?.videoOrientation = initialVideoOrientation
}
} else {
print("Couldn't add video device input to the session.")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
} catch {
print("Couldn't create video device input: \(error)")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
// Add audio input.
do {
let audioDevice = AVCaptureDevice.default(for: .audio)
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if session.canAddInput(audioDeviceInput) {
session.addInput(audioDeviceInput)
} else {
print("Could not add audio device input to the session")
}
} catch {
print("Could not create audio device input: \(error)")
}
session.commitConfiguration()
}
所以我的印象是,因为会话是在不同的线程上配置的,也许AVCaptureVideoPreviewLayer不会以某种方式添加?当我尝试将其直接添加到PreviewView.swift中时,出现此错误:
*由于未捕获的异常“ CALayerInvalid”而终止应用程序,原因:“图层是其图层树中循环的一部分” * 首先抛出调用堆栈:
我也不太了解在Apple教程中如何将PreviewLayer添加到视图中,但是它似乎可以在该应用程序中使用,所以我不确定发生了什么