VPCamera3/tdvideo/tdvideo/FrameProcessor.swift
2024-03-05 11:44:34 +08:00

234 lines
9.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// FrameProcessor.swift
// SpatialVideoGist
//
//
// Created by Bryan on 12/15/23.
//
import AVFoundation
import CoreImage
///
final class FrameProcessor {
// MARK: - Properties
// MARK: Public
///
var isPrepared = false
// MARK: Private
/// CIContext
private var ciContext: CIContext?
///
private var outputColorSpace: CGColorSpace?
/// ' CVPixelBufferPool '
private var outputPixelBufferPool: CVPixelBufferPool?
///' CMFormatDescription '
private(set) var inputFormatDescription: CMFormatDescription?
///' CMFormatDescription '
private(set) var outputFormatDescription: CMFormatDescription?
///GPU
private let metalDevice = MTLCreateSystemDefaultDevice()!
///' CVMetalTexture '
private var textureCache: CVMetalTextureCache!
// MARK: - Methods
///
/// -:
/// - formatDescription:' CMFormatDescription '
/// - outputRetainedBufferCountHint:
func prepare(
with formatDescription: CMFormatDescription,
outputRetainedBufferCountHint: Int
) {
reset()
(outputPixelBufferPool,
outputColorSpace,
outputFormatDescription) = allocateOutputBufferPool(
with: formatDescription,
outputRetainedBufferCountHint: outputRetainedBufferCountHint
)
if outputPixelBufferPool == nil {
return
}
inputFormatDescription = formatDescription
ciContext = CIContext()
var metalTextureCache: CVMetalTextureCache?
if CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, metalDevice, nil, &metalTextureCache) != kCVReturnSuccess {
assertionFailure("Unable to allocate texture cache")
} else {
textureCache = metalTextureCache
}
isPrepared = true
}
///' CIImage '' CGRect '' CVPixelBuffer '
/// -:
/// - pixelBufferImage:' CIImage '
/// - targetRect:CGRect
/// -:' CVPixelBuffer '
func cropPixelBuffer(
pixelBufferImage: CIImage,
targetRect: CGRect
) -> CVPixelBuffer? {
guard let ciContext = ciContext,
isPrepared
else {
isPrepared = false
return nil
}
var croppedImage = pixelBufferImage.cropped(to: targetRect)
let originTransform = CGAffineTransform(
translationX: -croppedImage.extent.origin.x,
y: -croppedImage.extent.origin.y
)
croppedImage = croppedImage.transformed(by: originTransform)
var pbuf: CVPixelBuffer?
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, outputPixelBufferPool!, &pbuf)
guard let outputPixelBuffer = pbuf else {
print("Allocation failure")
return nil
}
//(CIContext)
ciContext.render(
croppedImage,
to: outputPixelBuffer,
bounds: croppedImage.extent,
colorSpace: outputColorSpace
)
return outputPixelBuffer
}
// MARK: - Private
///
private func reset() {
ciContext = nil
outputColorSpace = nil
outputPixelBufferPool = nil
outputFormatDescription = nil
inputFormatDescription = nil
textureCache = nil
isPrepared = false
}
}
///Helper
private extension FrameProcessor {
///
/// -:
/// - inputFormatDescription:' CMFormatDescription '
/// - outputRetainedBufferCountHint:
/// -:
private func allocateOutputBufferPool(
with inputFormatDescription: CMFormatDescription,
outputRetainedBufferCountHint: Int
) ->(
outputBufferPool: CVPixelBufferPool?,
outputColorSpace: CGColorSpace?,
outputFormatDescription: CMFormatDescription?) {
let inputDimensions = CMVideoFormatDescriptionGetDimensions(inputFormatDescription)
var pixelBufferAttributes: [String: Any] = [
kCVPixelBufferPixelFormatTypeKey as String: UInt(kCVPixelFormatType_32BGRA),
kCVPixelBufferWidthKey as String: Int(inputDimensions.width / 2),
kCVPixelBufferHeightKey as String: Int(inputDimensions.height),
kCVPixelBufferIOSurfacePropertiesKey as String: [:]
]
//
var cgColorSpace = CGColorSpaceCreateDeviceRGB()
if let inputFormatDescriptionExtension = CMFormatDescriptionGetExtensions(inputFormatDescription) as Dictionary? {
let colorPrimaries = inputFormatDescriptionExtension[kCVImageBufferColorPrimariesKey]
if let colorPrimaries = colorPrimaries {
var colorSpaceProperties: [String: AnyObject] = [kCVImageBufferColorPrimariesKey as String: colorPrimaries]
if let yCbCrMatrix = inputFormatDescriptionExtension[kCVImageBufferYCbCrMatrixKey] {
colorSpaceProperties[kCVImageBufferYCbCrMatrixKey as String] = yCbCrMatrix
}
if let transferFunction = inputFormatDescriptionExtension[kCVImageBufferTransferFunctionKey] {
colorSpaceProperties[kCVImageBufferTransferFunctionKey as String] = transferFunction
}
pixelBufferAttributes[kCVBufferPropagatedAttachmentsKey as String] = colorSpaceProperties
}
if let cvColorspace = inputFormatDescriptionExtension[kCVImageBufferCGColorSpaceKey] {
cgColorSpace = cvColorspace as! CGColorSpace
} else if (colorPrimaries as? String) == (kCVImageBufferColorPrimaries_P3_D65 as String) {
cgColorSpace = CGColorSpace(name: CGColorSpace.displayP3)!
}
}
//
let poolAttributes = [kCVPixelBufferPoolMinimumBufferCountKey as String: outputRetainedBufferCountHint]
var cvPixelBufferPool: CVPixelBufferPool?
CVPixelBufferPoolCreate(kCFAllocatorDefault, poolAttributes as NSDictionary?, pixelBufferAttributes as NSDictionary?, &cvPixelBufferPool)
guard let pixelBufferPool = cvPixelBufferPool else {
assertionFailure("Allocation failure: Could not allocate pixel buffer pool.")
return (nil, nil, nil)
}
preallocateBuffers(pool: pixelBufferPool, allocationThreshold: outputRetainedBufferCountHint)
//
var pixelBuffer: CVPixelBuffer?
var outputFormatDescription: CMFormatDescription?
let auxAttributes = [kCVPixelBufferPoolAllocationThresholdKey as String: outputRetainedBufferCountHint] as NSDictionary
CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pixelBufferPool, auxAttributes, &pixelBuffer)
if let pixelBuffer = pixelBuffer {
CMVideoFormatDescriptionCreateForImageBuffer(allocator: kCFAllocatorDefault,
imageBuffer: pixelBuffer,
formatDescriptionOut: &outputFormatDescription)
}
pixelBuffer = nil
return (pixelBufferPool, cgColorSpace, outputFormatDescription)
}
///
/// -:
/// -:' CVPixelBufferPool '
/// - allocationThreshold:
private func preallocateBuffers(
pool: CVPixelBufferPool,
allocationThreshold: Int
) {
var pixelBuffers = [CVPixelBuffer]()
var error: CVReturn = kCVReturnSuccess
let auxAttributes = [kCVPixelBufferPoolAllocationThresholdKey as String: allocationThreshold] as NSDictionary
var pixelBuffer: CVPixelBuffer?
while error == kCVReturnSuccess {
error = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(kCFAllocatorDefault, pool, auxAttributes, &pixelBuffer)
if let pixelBuffer = pixelBuffer {
pixelBuffers.append(pixelBuffer)
}
pixelBuffer = nil
}
pixelBuffers.removeAll()
}
}