Files
yattee/Yattee/Core/HardwareCapabilities.swift
2026-02-08 18:33:56 +01:00

162 lines
6.2 KiB
Swift

//
// HardwareCapabilities.swift
// Yattee
//
// Detects hardware video decoding capabilities using VideoToolbox.
//
import Foundation
import VideoToolbox
import CoreMedia
/// Detects and caches hardware video decoding capabilities for the current device.
@MainActor
final class HardwareCapabilities {
static let shared = HardwareCapabilities()
// MARK: - Cached Results
private var _supportsH264Hardware: Bool?
private var _supportsHEVCHardware: Bool?
private var _supportsHEVCAlphaHardware: Bool?
private var _supportsDolbyVisionHEVCHardware: Bool?
private var _supportsVP9Hardware: Bool?
private var _supportsAV1Hardware: Bool?
private var _supportsProResHardware: Bool?
// MARK: - Hardware Support Properties
/// Whether the device supports H.264/AVC hardware decoding.
var supportsH264Hardware: Bool {
if let cached = _supportsH264Hardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_H264)
_supportsH264Hardware = supported
return supported
}
/// Whether the device supports HEVC/H.265 hardware decoding.
var supportsHEVCHardware: Bool {
if let cached = _supportsHEVCHardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)
_supportsHEVCHardware = supported
return supported
}
/// Whether the device supports HEVC with Alpha hardware decoding.
var supportsHEVCAlphaHardware: Bool {
if let cached = _supportsHEVCAlphaHardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVCWithAlpha)
_supportsHEVCAlphaHardware = supported
return supported
}
/// Whether the device supports Dolby Vision HEVC hardware decoding.
var supportsDolbyVisionHEVCHardware: Bool {
if let cached = _supportsDolbyVisionHEVCHardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_DolbyVisionHEVC)
_supportsDolbyVisionHEVCHardware = supported
return supported
}
/// Whether the device supports VP9 hardware decoding.
var supportsVP9Hardware: Bool {
if let cached = _supportsVP9Hardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_VP9)
_supportsVP9Hardware = supported
return supported
}
/// Whether the device supports AV1 hardware decoding.
var supportsAV1Hardware: Bool {
if let cached = _supportsAV1Hardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1)
_supportsAV1Hardware = supported
return supported
}
/// Whether the device supports ProRes hardware decoding.
var supportsProResHardware: Bool {
if let cached = _supportsProResHardware { return cached }
let supported = VTIsHardwareDecodeSupported(kCMVideoCodecType_AppleProRes422)
_supportsProResHardware = supported
return supported
}
// MARK: - Codec Priority
/// Returns codec priority for stream selection (higher = better).
///
/// When hardware decode is available, the codec gets a higher priority
/// to prefer battery-efficient playback. When not available, codecs that
/// require software decode get priority 0 to prefer hardware-decodable
/// alternatives at the same or similar resolution.
///
/// Priority levels:
/// - 4: Best (AV1 with hardware)
/// - 3: Great (VP9 with hardware, HEVC with hardware)
/// - 2: Good (H.264 - always hardware supported)
/// - 1: Acceptable (HEVC software - rare)
/// - 0: Avoid (AV1/VP9 software - battery drain, potential performance issues)
func codecPriority(for codec: String?) -> Int {
guard let codec = codec?.lowercased() else { return 0 }
if codec.contains("av1") || codec.contains("av01") {
// AV1: Best compression but avoid without hardware (heavy CPU usage)
return supportsAV1Hardware ? 4 : 0
} else if codec.contains("vp9") || codec.contains("vp09") {
// VP9: Good compression but avoid without hardware (battery drain)
return supportsVP9Hardware ? 3 : 0
} else if codec.contains("avc") || codec.contains("h264") || codec.contains("h.264") {
// H.264: Universal hardware support - reliable choice
return 2
} else if codec.contains("hevc") || codec.contains("hev") || codec.contains("h265") || codec.contains("h.265") {
// HEVC: Good compression, most devices have hardware support
return supportsHEVCHardware ? 3 : 1
}
return 0
}
/// Returns an ordered list of preferred codecs based on hardware support.
var preferredCodecOrder: [String] {
var codecs: [(String, Int)] = []
if supportsAV1Hardware {
codecs.append(("AV1", 4))
}
if supportsVP9Hardware {
codecs.append(("VP9", 3))
}
// H.264 is always hardware supported
codecs.append(("H.264", 2))
if supportsHEVCHardware {
codecs.append(("HEVC", 2))
}
return codecs.sorted { $0.1 > $1.1 }.map { $0.0 }
}
// MARK: - All Capabilities
/// Returns all codec capabilities for display in Device Capabilities view.
var allCapabilities: [(name: String, supported: Bool)] {
[
("H.264/AVC", supportsH264Hardware),
("HEVC/H.265", supportsHEVCHardware),
("HEVC with Alpha", supportsHEVCAlphaHardware),
("Dolby Vision HEVC", supportsDolbyVisionHEVCHardware),
("VP9", supportsVP9Hardware),
("AV1", supportsAV1Hardware),
("ProRes", supportsProResHardware)
]
}
// MARK: - Logging
/// Logs all hardware capabilities for debugging.
func logCapabilities() {
let capabilities = allCapabilities.map { "\($0.name): \($0.supported ? "Yes" : "No")" }.joined(separator: ", ")
LoggingService.shared.info("Hardware decode capabilities: \(capabilities)", category: .general)
LoggingService.shared.info("Preferred codec order: \(preferredCodecOrder.joined(separator: " > "))", category: .general)
}
}