从通过AVCapturePhotoCaptureDelegate获取的AVCapturePhoto中检索CVSampleBuffer

时间:2017-12-06 16:19:15

标签: ios avfoundation

在标题中,我试图从方法的输出中检索捕获的照片的CVPixelBuffer:

AVCapturePhotoCaptureDelegate.photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)

委托方法调用中的photo参数pixelBuffernil,我希望将其用于某些低级图像操作。

我主要关注的示例代码可以在以下网址找到:

https://developer.apple.com/library/content/samplecode/AVCam/Introduction/Intro.html

和AVFoundation文档。

由于AVFoundation会话配置有点冗长并且可能提供一些答案,我只是粘贴处理它的整个对象,它应包含所有相关代码:

protocol CameraServiceDelegate: class {
    func cameraServiceDidCapturePhoto(withBuffer buffer: CVPixelBuffer)
    func cameraServiceEncounteredError(_ error: Error?)
}

final class CameraService: NSObject {

    struct BufferRetrievalFailure: Error {}

    weak var delegate: CameraServiceDelegate?

    private let session = AVCaptureSession()
    private var discoverySession = AVCaptureDevice.DiscoverySession(
        deviceTypes: [.builtInDualCamera, .builtInWideAngleCamera],
        mediaType: .video,
        position: .back
    )
    private var deviceInput: AVCaptureDeviceInput!
    private let photoOutput = AVCapturePhotoOutput()

    private let sessionQueue = DispatchQueue(label: "av-capture-session.serial.queue")

    private var captureDevice: AVCaptureDevice? {
        return .default(.builtInDualCamera, for: .video, position: .back)
            ?? .default(.builtInWideAngleCamera, for: .video, position: .back)
            ?? .default(.builtInWideAngleCamera, for: .video, position: .front)
    }

    func setup(with layer: AVCaptureVideoPreviewLayer) {
        layer.session = session

        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            break
        case .notDetermined:
            requestVideoAuthorization()
        default:
            assertionFailure("Just enable video, this is not a real app.")
        }

        sessionQueue.async { [weak self] in
            self?.setupAVSession(with: layer)
        }
    }

    func resume() {
        sessionQueue.async { [weak session] in
            session?.startRunning()
        }
    }

    func suspend() {
        sessionQueue.async { [weak session] in
            session?.stopRunning()
        }
    }

    func capturePhoto() {
        sessionQueue.async { [weak self] in
            guard let strongSelf = self else {
                return
            }

            strongSelf.photoOutput.capturePhoto(with: strongSelf.capturePhotoSettings(), delegate: strongSelf)
        }
    }

    private func requestVideoAuthorization() {
        sessionQueue.suspend()

        AVCaptureDevice.requestAccess(for: .video) { [weak sessionQueue] isAuthorized in
            guard isAuthorized else {
                assertionFailure("Just enable video, this is not a real app.")
                return
            }

            sessionQueue?.resume()
        }
    }

    private func setupAVSession(with layer: AVCaptureVideoPreviewLayer) {
        session.beginConfiguration()

        session.sessionPreset = .photo

        setupVideoInput()
        setupVideoPreviewViewLayer(with: layer)
        setupPhotoOutput()

        session.commitConfiguration()
    }

    private func setupVideoInput() {
        guard let videoDevice = captureDevice,
              let deviceInput = try? AVCaptureDeviceInput(device: videoDevice),
              session.canAddInput(deviceInput) else {
            fatalError("Could not retrieve suitable capture device or configure video device input.")
        }

        self.deviceInput = deviceInput
        session.addInput(deviceInput)
    }

    private func setupVideoPreviewViewLayer(with layer: AVCaptureVideoPreviewLayer) {
        DispatchQueue.main.async {
            let statusBarOrientation = UIApplication.shared.statusBarOrientation

            layer.connection?.videoOrientation =
                statusBarOrientation != .unknown
                    ? AVCaptureVideoOrientation(rawValue: statusBarOrientation.rawValue)!
                    : .portrait
        }
    }

    private func setupPhotoOutput() {
        guard session.canAddOutput(photoOutput) else {
            fatalError("Could not configure photo output.")
        }

        session.addOutput(photoOutput)

        photoOutput.isHighResolutionCaptureEnabled = true
        photoOutput.isLivePhotoCaptureEnabled = false
        photoOutput.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported
    }

    private func capturePhotoSettings() -> AVCapturePhotoSettings {
        let settings: AVCapturePhotoSettings

        if photoOutput.availablePhotoCodecTypes.contains(.hevc) {
            settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
        } else {
            settings = AVCapturePhotoSettings()
        }

        settings.isHighResolutionPhotoEnabled = true
        settings.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliveryEnabled

        return settings
    }
}

extension CameraService: AVCapturePhotoCaptureDelegate {

    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        guard error == nil else {
            delegate?.cameraServiceEncounteredError(error)
            return
        }

        guard let buffer = photo.pixelBuffer else {
            delegate?.cameraServiceEncounteredError(BufferRetrievalFailure())
            return
        }

        delegate?.cameraServiceDidCapturePhoto(withBuffer: buffer)
    }
}

1 个答案:

答案 0 :(得分:1)

我没有代码示例,因为我正在Xamarin中工作,但是您需要在创建捕获时在使用的AVCapturePhotoSettings对象上设置previewPhotoFormat。我的一个例子found online

var settings = AVCapturePhotoSettings()
let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
let previewFormat = [
    kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
    kCVPixelBufferWidthKey as String: self.capturedButton.frame.width,
    kCVPixelBufferHeightKey as String: self.capturedButton.frame.height
] as [String : Any]
settings.previewPhotoFormat = previewFormat

我个人检查availablePreviewPhotoPixelFormatTypes,以查看分析所需的格式(kCVPixelFormatType_32BGRA)是否均匀存在。到目前为止,我还没有遇到没有它的设备。