AVCaptureVideoPreviewLayer在屏幕截图上不可见

时间:2019-07-11 11:28:51

标签: swift avfoundation screenshot avcapture

我有一个应用程序,可以添加一些实时动画和图像以在AV Foundation相机中预览视图。我可以进行“硬件屏幕截图”(按住“侧面”按钮和“调高音量”按钮),并且可以。但是,我需要一个用于截屏的按钮。

所有获取屏幕截图的方法,例如UIGraphicsGetImageFromCurrentImageContext(或view.drawHierarchy())都会在黑屏的情况下显示视频预览。屏幕截图上的所有其他元素均可见,除AVCaptureVideoPreviewLayer以外,其他图像均可见。

请帮助我。我可以做“硬件截图”吗?是否存在解决该问题的另一种方法?

1 个答案:

答案 0 :(得分:2)

我处于同一位置,并且研究了针对此问题的两种单独的解决方案。

  1. 将ViewController设置为AVCaptureVideoDataOutputSampleBufferDelegate并对视频输出进行采样以截取屏幕截图。

  2. 将ViewController设置为AVCapturePhotoCaptureDelegate并捕获照片。

在此问题中描述了设置前者的机制,例如:How to take UIImage of AVCaptureVideoPreviewLayer instead of AVCapturePhotoOutput capture

我都实施了这两种方法,以检查图像质量是否存在差异(没有)。

如果只需要摄影机快照,就可以了。但是听起来您需要在顶部绘制一个附加的动画。为此,我创建了一个与快照大小相同的容器UIView,向其中添加了带有快照的UIImageView,然后在顶部绘制了动画。之后,您可以在容器上使用UIGraphicsGetImageFromCurrentImageContext。

关于使用哪种解决方案(1)和(2),如果您不需要在应用程序中支持不同的相机方向,则可能无关紧要。但是,如果您需要在前后摄像头之间切换并支持不同的摄像头方向,则需要知道快照的方向,以便在正确的位置应用动画,而正确地使用方法(1 )。

我使用的解决方案:

  1. UIViewController扩展了AVCapturePhotoCaptureDelegate

  2. 将照片输出添加到AVCaptureSession

    private let session = AVCaptureSession()
    private let photoOutput = AVCapturePhotoOutput()

    ....

    // When configuring the session
    if self.session.canAddOutput(self.photoOutput) {
        self.session.addOutput(self.photoOutput)
        self.photoOutput.isHighResolutionCaptureEnabled = true
    } 
  1. 捕获快照
    let settings = AVCapturePhotoSettings()
    let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
    let previewFormat = [
        kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
        kCVPixelBufferWidthKey as String: 160,
        kCVPixelBufferHeightKey as String: 160
    ]
    settings.previewPhotoFormat = previewFormat
    photoOutput.capturePhoto(with: settings, delegate: self)
  1. 旋转或翻转快照,然后再进行其余操作
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

        guard error == nil else {
            // Do something
            // return
        }

        if let dataImage = photo.fileDataRepresentation() {
            print(UIImage(data: dataImage)?.size as Any)

            let dataProvider = CGDataProvider(data: dataImage as CFData)
            let cgImageRef: CGImage! = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
            //https://developer.apple.com/documentation/uikit/uiimageorientation?language=objc
            let orientation = UIApplication.shared.statusBarOrientation
            var imageOrientation = UIImage.Orientation.right
            switch orientation {
            case .portrait:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.right : UIImage.Orientation.leftMirrored
            case .landscapeRight:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.up : UIImage.Orientation.downMirrored
            case .portraitUpsideDown:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.left : UIImage.Orientation.rightMirrored
            case .landscapeLeft:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.down : UIImage.Orientation.upMirrored
            case .unknown:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.right : UIImage.Orientation.leftMirrored
            @unknown default:
                imageOrientation = self.cameraPosition == .back ? UIImage.Orientation.right : UIImage.Orientation.leftMirrored
            }
            let image = UIImage.init(cgImage: cgImageRef, scale: 1.0, orientation: imageOrientation)

            // Do whatever you need to do with the image

        } else {
            // Handle error
        }
     }

如果您需要知道图像的大小以放置动画,则可以使用AVCaptureVideoDataOutputSampleBufferDelegate策略来一次检测缓冲区的大小。