AVFoundation使用自定义分辨率

时间:2016-04-18 09:01:45

标签: swift macos camera avfoundation

我正在OS X上编写应用程序,它将从相机中捕获帧。 是否可以使用AVCaptureDevice.activeFormat属性设置捕获设置?我试过这个,但它没有用(会话预设会覆盖它)。

我发现在IOS上可以将AVCaptureSession中的SessionPreset设置为AVCaptureSessionPresetInputPriority

主要目的是选择比预设更详细的视频分辨率。

1 个答案:

答案 0 :(得分:9)

在macOS中(与iOS不同),捕获会话可以在您进行更改后自动配置捕获格式。要防止自动更改捕获格式,请使用lockForConfiguration()方法。然后调用beginConfiguration()方法,设置属性(从十几个中选择一个预设,例如 AVCaptureSessionPresetiFrame960x540 ),然后调用commitConfiguration()方法。最后,您需要在更改设备属性后放置unlockForConfiguration()

或者按照以下步骤操作:

  1. 调用lockForConfiguration()获取对设备配置属性的独占访问权。

  2. 更改设备的activeFormat属性(如上文及下文所述)。

  3. 使用会话的startRunning()方法开始捕获。

  4. 使用unlockForConfiguration()解锁设备。

  5. 必须分别调用

    startRunning()stopRunning()方法来启动和停止数据从输入到输出的流动。

      

    在调用lockForConfiguration()方法AVCaptureSession之前,您还必须致电startRunning(),否则会话的预设将覆盖捕获设备上选定的有效格式。

    但是,如果您要求设备属性保持不变,则可以保持锁定而不释放该锁定。

    以下是开发人员文档lockForConfiguration()中的详细信息。

    如果您尝试将活动格式设置为可访问格式中不存在的格式,则会抛出invalidArgumentException

    enter image description here

    此外,还有一个如何更改属性的说明:macOS AVFoundation Video Capture

      

    在AVCaptureDevice中有两个属性。格式和activeFormat。 format将返回AVCaptureDeviceFormat的NSArrary,其中包含cam公开的所有格式。您从此列表中选择任何一种格式并将其设置为activeFormat。通过调用AVCaptureDevice lockForConfigration,确保在收到对devlce的独占访问权限后设置格式。设置格式后,使用AVCaptureDevice unlockForConfigration释放锁定。然后启动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
        }
    }