mirror of
https://github.com/yattee/yattee.git
synced 2026-02-19 17:29:45 +00:00
162 lines
6.2 KiB
Swift
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)
|
|
}
|
|
}
|