import VideoToolbox
import AVFoundation
// Function to check hardware decode and encode support for a given codec
func checkHardwareCodecSupport() {
// List of codec types to check (based on M4 Max Media Engine + VP9)
let codecTypes: [CMVideoCodecType] = [
kCMVideoCodecType_H264,
kCMVideoCodecType_HEVC,
kCMVideoCodecType_AppleProRes422,
kCMVideoCodecType_AppleProRes422HQ,
kCMVideoCodecType_AppleProRes422LT,
kCMVideoCodecType_AppleProRes422Proxy,
kCMVideoCodecType_AppleProRes4444,
kCMVideoCodecType_AppleProRes4444XQ,
kCMVideoCodecType_AppleProResRAW,
kCMVideoCodecType_AV1,
kCMVideoCodecType_VP9 // Added VP9
]
// Dictionary to map codec types to human-readable names
let codecNames: [CMVideoCodecType: String] = [
kCMVideoCodecType_H264: "H.264",
kCMVideoCodecType_HEVC: "HEVC (H.265)",
kCMVideoCodecType_AppleProRes422: "ProRes 422",
kCMVideoCodecType_AppleProRes422HQ: "ProRes 422 HQ",
kCMVideoCodecType_AppleProRes422LT: "ProRes 422 LT",
kCMVideoCodecType_AppleProRes422Proxy: "ProRes 422 Proxy",
kCMVideoCodecType_AppleProRes4444: "ProRes 4444",
kCMVideoCodecType_AppleProRes4444XQ: "ProRes 4444 XQ",
kCMVideoCodecType_AppleProResRAW: "ProRes RAW",
kCMVideoCodecType_AV1: "AV1",
kCMVideoCodecType_VP9: "VP9",
kCMVideoCodecType_JPEG: "JPEG",
0x64657068: "Depth (deph)", // 'deph' - Depth data
0x64697368: "Disparity (dish)", // 'dish' - Disparity data
0x6d757861: "Mux Audio (muxa)" // 'muxa' - Audio multiplexing
]
VTRegisterSupplementalVideoDecoderIfAvailable(kCMVideoCodecType_VP9)
print("Checking Hardware Decode & Encode Capabilities on Mac...\n")
// First, let's list all available hardware encoders with detailed info
print("=== Detailed Hardware Encoder Analysis ===")
var encoderListOut: CFArray?
let status = VTCopyVideoEncoderList(nil, &encoderListOut)
if status == noErr, let encoderList = encoderListOut {
let encoders = encoderList as! [CFDictionary]
var hardwareEncoderCount = 0
for (index, encoder) in encoders.enumerated() {
if let codecType = CFDictionaryGetValue(encoder, Unmanaged.passUnretained(kVTVideoEncoderList_CodecType).toOpaque()) {
let encoderCodec = Unmanaged<CFNumber>.fromOpaque(codecType).takeUnretainedValue()
var codecValue: CMVideoCodecType = 0
CFNumberGetValue(encoderCodec, CFNumberType.sInt32Type, &codecValue)
if let isHardwareAccelerated = CFDictionaryGetValue(encoder, Unmanaged.passUnretained(kVTVideoEncoderList_IsHardwareAccelerated).toOpaque()) {
let accelerated = Unmanaged<CFBoolean>.fromOpaque(isHardwareAccelerated).takeUnretainedValue()
if CFBooleanGetValue(accelerated) {
hardwareEncoderCount += 1
let codecName = codecNames[codecValue] ?? "Unknown (\(String(codecValue, radix: 16)))"
print("🔧 Hardware Encoder #\(hardwareEncoderCount): \(codecName)")
print(" Codec ID: 0x\(String(codecValue, radix: 16)) ('\(fourCCToString(codecValue))')")
// Extract all available properties
let keys = CFDictionaryGetKeysAndValues(encoder, nil, nil)
let count = CFDictionaryGetCount(encoder)
var keyPointers = [UnsafeRawPointer?](repeating: nil, count: count)
var valuePointers = [UnsafeRawPointer?](repeating: nil, count: count)
CFDictionaryGetKeysAndValues(encoder, &keyPointers, &valuePointers)
for i in 0..<count {
if let keyPtr = keyPointers[i], let valuePtr = valuePointers[i] {
let key = Unmanaged<CFString>.fromOpaque(keyPtr).takeUnretainedValue() as String
// Try to extract the value based on common types
if key.contains("CodecName") || key.contains("EncoderName") || key.contains("DisplayName") {
if let stringValue = Unmanaged<CFString>.fromOpaque(valuePtr).takeUnretainedValue() as String? {
print(" \(key): \(stringValue)")
}
} else if key.contains("GPURegistryID") || key.contains("InstanceID") || key.contains("Rating") || key.contains("CodecType") {
if let numberValue = Unmanaged<CFNumber>.fromOpaque(valuePtr).takeUnretainedValue() as CFNumber? {
var intValue: Int64 = 0
CFNumberGetValue(numberValue, CFNumberType.sInt64Type, &intValue)
print(" \(key): \(intValue)")
}
} else if key.contains("IsHardwareAccelerated") {
if let boolValue = Unmanaged<CFBoolean>.fromOpaque(valuePtr).takeUnretainedValue() as CFBoolean? {
print(" \(key): \(CFBooleanGetValue(boolValue) ? "Yes" : "No")")
}
} else if key.contains("EncoderID") {
if let stringValue = Unmanaged<CFString>.fromOpaque(valuePtr).takeUnretainedValue() as String? {
print(" \(key): \(stringValue)")
}
} else {
// Safe generic value extraction - just show the description
let cfValue = Unmanaged<AnyObject>.fromOpaque(valuePtr).takeUnretainedValue()
let description = String(describing: cfValue)
if description.count < 100 { // Avoid extremely long output
print(" \(key): \(description)")
}
}
}
}
print()
}
}
}
}
print("Total Hardware Encoders Found: \(hardwareEncoderCount)")
}
print()
for codec in codecTypes {
guard let codecName = codecNames[codec] else { continue }
// Check if hardware decode is supported
let isHardwareDecodeSupported = VTIsHardwareDecodeSupported(codec)
// Check if hardware encode is supported by trying to copy encoder specifications
var encodeSupported = false
if status == noErr, let encoderList = encoderListOut {
let encoders = encoderList as! [CFDictionary]
for encoder in encoders {
if let codecType = CFDictionaryGetValue(encoder, Unmanaged.passUnretained(kVTVideoEncoderList_CodecType).toOpaque()) {
let encoderCodec = Unmanaged<CFNumber>.fromOpaque(codecType).takeUnretainedValue()
var codecValue: CMVideoCodecType = 0
CFNumberGetValue(encoderCodec, CFNumberType.sInt32Type, &codecValue)
if codecValue == codec {
if let isHardwareAccelerated = CFDictionaryGetValue(encoder, Unmanaged.passUnretained(kVTVideoEncoderList_IsHardwareAccelerated).toOpaque()) {
let accelerated = Unmanaged<CFBoolean>.fromOpaque(isHardwareAccelerated).takeUnretainedValue()
if CFBooleanGetValue(accelerated) {
encodeSupported = true
break
}
}
}
}
}
}
print("Codec: \(codecName)")
print("Hardware Decode Supported: \(isHardwareDecodeSupported ? "Yes" : "No")")
print("Hardware Encode Supported: \(encodeSupported ? "Yes" : "No")")
print("---")
}
}
// Helper function to convert FourCC codes to readable strings
func fourCCToString(_ fourCC: CMVideoCodecType) -> String {
let bytes = [
UInt8((fourCC >> 24) & 0xff),
UInt8((fourCC >> 16) & 0xff),
UInt8((fourCC >> 8) & 0xff),
UInt8(fourCC & 0xff)
]
return String(bytes: bytes, encoding: .ascii) ?? "????"
}
// Run the check
checkHardwareCodecSupport()