AVCaptureVideoPreviewLayer 在屏幕截图中不可见
AVCaptureVideoPreviewLayer is not visible on the screenshot
我有一个应用程序可以添加一些实时动画和图像以在 AV Foundation 相机中预览视图。我可以 "hardware screenshot"(按住侧边按钮和提高音量按钮),没问题。但是,我需要一个制作屏幕截图的按钮。
所有像UIGraphicsGetImageFromCurrentImageContext
(或view.drawHierarchy()
)这样的截图方法都会导致视频预览所在的黑屏。除了 AVCaptureVideoPreviewLayer
.
之外,所有其他元素都在屏幕截图上并且图像是可见的
请帮帮我。我可以"hardware screenshot"吗?是否存在该问题的另一种解决方案?
我处于相同的位置,并研究了解决这个问题的两个不同的解决方案。
将 ViewController 设置为 AVCaptureVideoDataOutputSampleBufferDelegate 并对视频输出进行采样以截取屏幕截图。
将 ViewController 设置为 AVCapturePhotoCaptureDelegate 并捕获照片。
这个问题中描述了设置前者的机制,例如:
我同时执行了这两个操作以检查图像质量是否存在差异(没有)。
如果您只需要相机快照,就可以了。但听起来你需要在上面画一个额外的动画。为此,我创建了一个与快照大小相同的容器 UIView,用快照向其中添加了一个 UIImageView,然后在上面绘制了动画。之后,您可以在容器上使用 UIGraphicsGetImageFromCurrentImageContext。
至于使用解决方案(1)和(2)中的哪一个,如果您不需要在应用程序中支持不同的相机方向,那可能并不重要。但是,如果你需要在前后摄像头之间切换并支持不同的摄像头方向,那么你需要知道快照方向才能在正确的位置应用动画,而要做到这一点,结果证明这是一个完全失败的方法(1 ).
我使用的解决方案:
UIViewController 扩展 AVCapturePhotoCaptureDelegate
将照片输出添加到 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
}
- 捕获快照
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)
- 在进行其余操作之前先旋转或翻转快照
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 策略检测一次缓冲区的大小。
我有一个应用程序可以添加一些实时动画和图像以在 AV Foundation 相机中预览视图。我可以 "hardware screenshot"(按住侧边按钮和提高音量按钮),没问题。但是,我需要一个制作屏幕截图的按钮。
所有像UIGraphicsGetImageFromCurrentImageContext
(或view.drawHierarchy()
)这样的截图方法都会导致视频预览所在的黑屏。除了 AVCaptureVideoPreviewLayer
.
请帮帮我。我可以"hardware screenshot"吗?是否存在该问题的另一种解决方案?
我处于相同的位置,并研究了解决这个问题的两个不同的解决方案。
将 ViewController 设置为 AVCaptureVideoDataOutputSampleBufferDelegate 并对视频输出进行采样以截取屏幕截图。
将 ViewController 设置为 AVCapturePhotoCaptureDelegate 并捕获照片。
这个问题中描述了设置前者的机制,例如:
我同时执行了这两个操作以检查图像质量是否存在差异(没有)。
如果您只需要相机快照,就可以了。但听起来你需要在上面画一个额外的动画。为此,我创建了一个与快照大小相同的容器 UIView,用快照向其中添加了一个 UIImageView,然后在上面绘制了动画。之后,您可以在容器上使用 UIGraphicsGetImageFromCurrentImageContext。
至于使用解决方案(1)和(2)中的哪一个,如果您不需要在应用程序中支持不同的相机方向,那可能并不重要。但是,如果你需要在前后摄像头之间切换并支持不同的摄像头方向,那么你需要知道快照方向才能在正确的位置应用动画,而要做到这一点,结果证明这是一个完全失败的方法(1 ).
我使用的解决方案:
UIViewController 扩展 AVCapturePhotoCaptureDelegate
将照片输出添加到 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
}
- 捕获快照
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)
- 在进行其余操作之前先旋转或翻转快照
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 策略检测一次缓冲区的大小。