Files
OpenStats/Packages/OpenStatsKit/Sources/Metrics/MediaUsageSampler.swift
T

82 lines
4.2 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.
import CoreAudio
import CoreMediaIO
import Foundation
/// 摄像头与麦克风是否正被某个进程使用。只读设备的“正在运行”属性,不打开设备,也不需要隐私授权
public struct MediaUsage: Sendable, Equatable {
public var camera = false
public var microphone = false
public init(camera: Bool = false, microphone: Bool = false) {
self.camera = camera
self.microphone = microphone
}
}
public enum MediaUsageSampler {
public static func sample() -> MediaUsage {
MediaUsage(camera: cameraInUse(), microphone: microphoneInUse())
}
// MARK: 麦克风(Core Audio)
private static func microphoneInUse() -> Bool {
audioDevices().contains { device in
hasInputStreams(device) && audioFlag(device, kAudioDevicePropertyDeviceIsRunningSomewhere)
}
}
private static func audioDevices() -> [AudioObjectID] {
var address = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDevices,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMain)
var size: UInt32 = 0
let system = AudioObjectID(kAudioObjectSystemObject)
guard AudioObjectGetPropertyDataSize(system, &address, 0, nil, &size) == noErr, size > 0 else { return [] }
var devices = [AudioObjectID](repeating: 0, count: Int(size) / MemoryLayout<AudioObjectID>.size)
guard AudioObjectGetPropertyData(system, &address, 0, nil, &size, &devices) == noErr else { return [] }
return devices
}
private static func hasInputStreams(_ device: AudioObjectID) -> Bool {
var address = AudioObjectPropertyAddress(mSelector: kAudioDevicePropertyStreams,
mScope: kAudioObjectPropertyScopeInput,
mElement: kAudioObjectPropertyElementMain)
var size: UInt32 = 0
return AudioObjectGetPropertyDataSize(device, &address, 0, nil, &size) == noErr && size > 0
}
private static func audioFlag(_ device: AudioObjectID, _ selector: AudioObjectPropertySelector) -> Bool {
var address = AudioObjectPropertyAddress(mSelector: selector,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMain)
var value: UInt32 = 0
var size = UInt32(MemoryLayout<UInt32>.size)
return AudioObjectGetPropertyData(device, &address, 0, nil, &size, &value) == noErr && value != 0
}
// MARK: 摄像头(Core Media IO)
private static func cameraInUse() -> Bool {
var address = CMIOObjectPropertyAddress(mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyDevices),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMain))
let system = CMIOObjectID(kCMIOObjectSystemObject)
var size: UInt32 = 0
guard CMIOObjectGetPropertyDataSize(system, &address, 0, nil, &size) == noErr, size > 0 else { return false }
var devices = [CMIOObjectID](repeating: 0, count: Int(size) / MemoryLayout<CMIOObjectID>.size)
var used: UInt32 = 0
guard CMIOObjectGetPropertyData(system, &address, 0, nil, size, &used, &devices) == noErr else { return false }
return devices.contains { device in
var running = CMIOObjectPropertyAddress(mSelector: CMIOObjectPropertySelector(kCMIODevicePropertyDeviceIsRunningSomewhere),
mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeWildcard),
mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementWildcard))
var value: UInt32 = 0
var valueUsed: UInt32 = 0
return CMIOObjectGetPropertyData(device, &running, 0, nil, UInt32(MemoryLayout<UInt32>.size), &valueUsed, &value) == noErr
&& value != 0
}
}
}