HaishinKit.swift/Sources/Extension/VTCompressionSession+Extens...

233 lines
8.3 KiB
Swift

import Foundation
import VideoToolbox
enum VTCompressionSessionPropertyKey: VTSessionPropertyKey {
// Bitstream Configuration
case depth
case profileLevel
case H264EntropyMode
// Buffers
case numberOfPendingFrames
case pixelBufferPoolIsShared
case videoEncoderPixelBufferAttributes
// Clean Aperture and Pixel Aspect Ratio
case aspectRatio16x9
case cleanAperture
case fieldCount
case fieldDetail
case pixelAspectRatio
case progressiveScan
// Color
case colorPrimaries
case transferFunction
case YCbCrMatrix
case ICCProfile
// Expected Values
case expectedDuration
case expectedFrameRate
case sourceFrameCount
// Frame Dependency
case allowFrameReordering
case allowTemporalCompression
case maxKeyFrameInterval
case maxKeyFrameIntervalDuration
#if os(macOS)
// Hardware Acceleration
case usingHardwareAcceleratedVideoEncoder
case requireHardwareAcceleratedVideoEncoder
case enableHardwareAcceleratedVideoEncoder
#endif
// Multipass Storage
case multiPassStorage
// Per-Frame Configuration
case forceKeyFrame
// Precompression Processing
case pixelTransferProperties
// Rate Control
case averageBitRate
case dataRateLimits
case moreFramesAfterEnd
case moreFramesBeforeStart
case quality
// Runtime Restriction
case realTime
case maxH264SliceBytes
case maxFrameDelayCount
// Others
case encoderUsage
var CFString: CFString {
switch self {
case .depth:
return kVTCompressionPropertyKey_Depth
case .profileLevel:
return kVTCompressionPropertyKey_ProfileLevel
case .H264EntropyMode:
return kVTCompressionPropertyKey_H264EntropyMode
case .numberOfPendingFrames:
return kVTCompressionPropertyKey_NumberOfPendingFrames
case .pixelBufferPoolIsShared:
return kVTCompressionPropertyKey_PixelBufferPoolIsShared
case .videoEncoderPixelBufferAttributes:
return kVTCompressionPropertyKey_VideoEncoderPixelBufferAttributes
case .aspectRatio16x9:
return kVTCompressionPropertyKey_AspectRatio16x9
case .cleanAperture:
return kVTCompressionPropertyKey_CleanAperture
case .fieldCount:
return kVTCompressionPropertyKey_FieldCount
case .fieldDetail:
return kVTCompressionPropertyKey_FieldDetail
case .pixelAspectRatio:
return kVTCompressionPropertyKey_PixelAspectRatio
case .progressiveScan:
return kVTCompressionPropertyKey_ProgressiveScan
case .colorPrimaries:
return kVTCompressionPropertyKey_ColorPrimaries
case .transferFunction:
return kVTCompressionPropertyKey_TransferFunction
case .YCbCrMatrix:
return kVTCompressionPropertyKey_YCbCrMatrix
case .ICCProfile:
return kVTCompressionPropertyKey_ICCProfile
case .expectedDuration:
return kVTCompressionPropertyKey_ExpectedDuration
case .expectedFrameRate:
return kVTCompressionPropertyKey_ExpectedFrameRate
case .sourceFrameCount:
return kVTCompressionPropertyKey_SourceFrameCount
case .allowFrameReordering:
return kVTCompressionPropertyKey_AllowFrameReordering
case .allowTemporalCompression:
return kVTCompressionPropertyKey_AllowTemporalCompression
case .maxKeyFrameInterval:
return kVTCompressionPropertyKey_MaxKeyFrameInterval
case .maxKeyFrameIntervalDuration:
return kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration
#if os(macOS)
case .usingHardwareAcceleratedVideoEncoder:
return kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder
case .requireHardwareAcceleratedVideoEncoder:
return kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder
case .enableHardwareAcceleratedVideoEncoder:
return kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder
#endif
case .multiPassStorage:
return kVTCompressionPropertyKey_MultiPassStorage
case .forceKeyFrame:
return kVTEncodeFrameOptionKey_ForceKeyFrame
case .pixelTransferProperties:
return kVTCompressionPropertyKey_PixelTransferProperties
case .averageBitRate:
return kVTCompressionPropertyKey_AverageBitRate
case .dataRateLimits:
return kVTCompressionPropertyKey_DataRateLimits
case .moreFramesAfterEnd:
return kVTCompressionPropertyKey_MoreFramesAfterEnd
case .moreFramesBeforeStart:
return kVTCompressionPropertyKey_MoreFramesBeforeStart
case .quality:
return kVTCompressionPropertyKey_Quality
case .realTime:
return kVTCompressionPropertyKey_RealTime
case .maxH264SliceBytes:
return kVTCompressionPropertyKey_MaxH264SliceBytes
case .maxFrameDelayCount:
return kVTCompressionPropertyKey_MaxFrameDelayCount
case .encoderUsage:
return "EncoderUsage" as CFString
}
}
}
extension VTCompressionSession {
func encodeFrame(_ imageBuffer: CVImageBuffer, presentaionTimeStamp: CMTime, duration: CMTime, frameProperties: CFDictionary? = nil, sourceFrameRefcon: UnsafeMutableRawPointer? = nil, infoFlagsOut: UnsafeMutablePointer<VTEncodeInfoFlags>? = nil) throws {
let status = VTCompressionSessionEncodeFrame(
self,
imageBuffer: imageBuffer,
presentationTimeStamp: presentaionTimeStamp,
duration: duration,
frameProperties: frameProperties,
sourceFrameRefcon: sourceFrameRefcon,
infoFlagsOut: infoFlagsOut
)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
}
func timeRangeForNextPass() throws -> CMTimeRange {
var itemCount: CMItemCount = 0
var timeRange: UnsafePointer<CMTimeRange>?
let status = VTCompressionSessionGetTimeRangesForNextPass(self, timeRangeCountOut: &itemCount, timeRangeArrayOut: &timeRange)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
return timeRange?.pointee ?? .invalid
}
func setProperty(_ key: VTCompressionSessionPropertyKey, value: CFTypeRef?) throws {
let status = VTSessionSetProperty(self, key: key.CFString, value: value)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
}
func setProperties(_ propertyDictionary: [NSString: NSObject]) throws {
let status = VTSessionSetProperties(self, propertyDictionary: propertyDictionary as CFDictionary)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
}
func prepareToEncodeFrame() throws {
let status = VTCompressionSessionPrepareToEncodeFrames(self)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
}
func beginPass(_ flags: VTCompressionSessionOptionFlags = .init(rawValue: 0)) throws {
let status = VTCompressionSessionBeginPass(self, flags: flags, nil)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
}
func endPass() throws -> DarwinBoolean {
var furtherPassesRequestedOut: DarwinBoolean = false
let status = VTCompressionSessionEndPass(self, furtherPassesRequestedOut: &furtherPassesRequestedOut, nil)
guard status == noErr else {
throw OSError.invoke(function: #function, status: status)
}
return furtherPassesRequestedOut
}
func invalidate() {
VTCompressionSessionInvalidate(self)
}
func copySupportedPropertyDictionary() -> [AnyHashable: Any] {
var support: CFDictionary?
guard VTSessionCopySupportedPropertyDictionary(self, supportedPropertyDictionaryOut: &support) == noErr else {
return [:]
}
guard let result = support as? [AnyHashable: Any] else {
return [:]
}
return result
}
}