screencapturekit 2.1.0

Safe Rust bindings for Apple's ScreenCaptureKit framework - screen and audio capture on macOS
Documentation
// Audio device enumeration using AVFoundation

import AVFoundation
import Foundation

/// Represents an audio input device (microphone)
public struct AudioInputDevice {
    public let id: String
    public let name: String
    public let isDefault: Bool
}

// MARK: - FFI Functions

/// Get the count of available audio input devices
@_cdecl("sc_audio_get_input_device_count")
public func getInputDeviceCount() -> Int {
    let discoverySession = AVCaptureDevice.DiscoverySession(
        deviceTypes: [.builtInMicrophone, .externalUnknown],
        mediaType: .audio,
        position: .unspecified
    )
    return discoverySession.devices.count
}

/// Get audio input device ID at index into a buffer
@_cdecl("sc_audio_get_input_device_id")
public func getInputDeviceId(index: Int, buffer: UnsafeMutablePointer<CChar>?, bufferSize: Int) -> Bool {
    guard let buffer, bufferSize > 0 else { return false }

    let discoverySession = AVCaptureDevice.DiscoverySession(
        deviceTypes: [.builtInMicrophone, .externalUnknown],
        mediaType: .audio,
        position: .unspecified
    )
    let devices = discoverySession.devices
    guard index >= 0, index < devices.count else { return false }

    let deviceId = devices[index].uniqueID
    return deviceId.withCString { src in
        let length = strlen(src)
        guard length < bufferSize else { return false }
        strcpy(buffer, src)
        return true
    }
}

/// Get audio input device name at index into a buffer
@_cdecl("sc_audio_get_input_device_name")
public func getInputDeviceName(index: Int, buffer: UnsafeMutablePointer<CChar>?, bufferSize: Int) -> Bool {
    guard let buffer, bufferSize > 0 else { return false }

    let discoverySession = AVCaptureDevice.DiscoverySession(
        deviceTypes: [.builtInMicrophone, .externalUnknown],
        mediaType: .audio,
        position: .unspecified
    )
    let devices = discoverySession.devices
    guard index >= 0, index < devices.count else { return false }

    let deviceName = devices[index].localizedName
    return deviceName.withCString { src in
        let length = strlen(src)
        guard length < bufferSize else { return false }
        strcpy(buffer, src)
        return true
    }
}

/// Check if the device at index is the default audio input device
@_cdecl("sc_audio_is_default_input_device")
public func isDefaultInputDevice(index: Int) -> Bool {
    let discoverySession = AVCaptureDevice.DiscoverySession(
        deviceTypes: [.builtInMicrophone, .externalUnknown],
        mediaType: .audio,
        position: .unspecified
    )
    let devices = discoverySession.devices
    guard index >= 0, index < devices.count else { return false }

    // The default device is typically the one returned by default()
    if let defaultDevice = AVCaptureDevice.default(for: .audio) {
        return devices[index].uniqueID == defaultDevice.uniqueID
    }
    return false
}

/// Get the default audio input device ID into a buffer
@_cdecl("sc_audio_get_default_input_device_id")
public func getDefaultInputDeviceId(buffer: UnsafeMutablePointer<CChar>?, bufferSize: Int) -> Bool {
    guard let buffer, bufferSize > 0 else { return false }
    guard let device = AVCaptureDevice.default(for: .audio) else { return false }

    return device.uniqueID.withCString { src in
        let length = strlen(src)
        guard length < bufferSize else { return false }
        strcpy(buffer, src)
        return true
    }
}

/// Get the default audio input device name into a buffer
@_cdecl("sc_audio_get_default_input_device_name")
public func getDefaultInputDeviceName(buffer: UnsafeMutablePointer<CChar>?, bufferSize: Int) -> Bool {
    guard let buffer, bufferSize > 0 else { return false }
    guard let device = AVCaptureDevice.default(for: .audio) else { return false }

    return device.localizedName.withCString { src in
        let length = strlen(src)
        guard length < bufferSize else { return false }
        strcpy(buffer, src)
        return true
    }
}