我正在OS X上编写应用程序,它将从相机中捕获帧。
是否可以使用AVCaptureDevice.activeFormat
属性设置捕获设置?我试过这个,但它没有用(会话预设会覆盖它)。
我发现在IOS上可以将AVCaptureSession
中的SessionPreset设置为AVCaptureSessionPresetInputPriority
。
主要目的是选择比预设更详细的视频分辨率。
答案 0 :(得分:9)
在macOS中(与iOS不同),捕获会话可以在您进行更改后自动配置捕获格式。要防止自动更改捕获格式,请使用lockForConfiguration()
方法。然后调用beginConfiguration()
方法,设置属性(从十几个中选择一个预设,例如 AVCaptureSessionPresetiFrame960x540
),然后调用commitConfiguration()
方法。最后,您需要在更改设备属性后放置unlockForConfiguration()
。
或者按照以下步骤操作:
调用lockForConfiguration()
获取对设备配置属性的独占访问权。
更改设备的activeFormat
属性(如上文及下文所述)。
使用会话的startRunning()
方法开始捕获。
使用unlockForConfiguration()
解锁设备。
startRunning()
和stopRunning()
方法来启动和停止数据从输入到输出的流动。
在调用
lockForConfiguration()
方法AVCaptureSession
之前,您还必须致电startRunning()
,否则会话的预设将覆盖捕获设备上选定的有效格式。
但是,如果您要求设备属性保持不变,则可以保持锁定而不释放该锁定。
以下是开发人员文档lockForConfiguration()中的详细信息。
如果您尝试将活动格式设置为可访问格式中不存在的格式,则会抛出invalidArgumentException
。
此外,还有一个如何更改属性的说明:macOS AVFoundation Video Capture
在AVCaptureDevice中有两个属性。格式和activeFormat。 format将返回
AVCaptureDeviceFormat
的NSArrary,其中包含cam公开的所有格式。您从此列表中选择任何一种格式并将其设置为activeFormat。通过调用AVCaptureDevicelockForConfigration
,确保在收到对devlce的独占访问权限后设置格式。设置格式后,使用AVCaptureDeviceunlockForConfigration
释放锁定。然后启动AVCaptureSession,它将为您提供所设置格式的视频帧。AVCaptureFormat是CMFormatDescription的包装器。 CMVideoFotmatDescription是CMFormatDescription的concreete子类。使用
CMVideoFormatDescriptionGetDimentions()
以设置格式获取宽度和高度。使用CMFormatDescriptionGetMediaSubType()
获取视频编解码器。对于原始照片,视频编解码器主要是yuvs或vuy2。对于压缩格式,它的h264,dmb1(mjpeg)等等。
这是用Swift 4.1编写的macOS示例:
import Cocoa
import AVFoundation
class ViewController: NSViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupCameraSession()
}
override func viewDidAppear() {
super.viewDidAppear()
view.layer?.addSublayer(previewLayer)
cameraSession.startRunning()
}
lazy var cameraSession: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = AVCaptureSession.Preset.iFrame960x540
return session
}()
lazy var previewLayer: AVCaptureVideoPreviewLayer = {
let preview = AVCaptureVideoPreviewLayer(session: self.cameraSession)
preview.bounds = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
preview.position = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
preview.videoGravity = AVLayerVideoGravity.resize
return preview
}()
func setupCameraSession() {
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
do {
let deviceInput = try AVCaptureDeviceInput(device: captureDevice!)
guard let camera = AVCaptureDevice.default(for: .video) else {
return
}
// acquire exclusive access to the device’s properties
try camera.lockForConfiguration()
cameraSession.beginConfiguration()
camera.focusMode = .continuousAutoFocus
camera.flashMode = .on
camera.whiteBalanceMode = .continuousAutoWhiteBalance
if (cameraSession.canAddInput(deviceInput) == true) {
cameraSession.addInput(deviceInput)
}
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange as UInt32)] as [String : Any]
dataOutput.alwaysDiscardsLateVideoFrames = true
if (cameraSession.canAddOutput(dataOutput) == true) {
cameraSession.addOutput(dataOutput)
}
cameraSession.commitConfiguration()
camera.unlockForConfiguration()
let queue = DispatchQueue(label: "com.blah-blah.yourLabel")
dataOutput.setSampleBufferDelegate(self, queue: queue)
} catch let error as NSError {
NSLog("\(error), \(error.localizedDescription)")
}
}
func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Here you collect each frame and process it
}
func captureOutput(_ captureOutput: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Here you can count how many frames are dropped
}
}