Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions Sources/LiveKit/Support/Video/DeviceManager+Public.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI

// MARK: - Computed Helpers

public extension DeviceManager {
@MainActor
var defaultAudioInput: AudioDevice? {
audioInputDevices.first(where: \.isDefault)
}

@MainActor
var defaultAudioOutput: AudioDevice? {
audioOutputDevices.first(where: \.isDefault)
}

@MainActor
var canSwitchCameraPosition: Bool {
videoCaptureDevices.contains(where: { $0.position == .front }) &&
videoCaptureDevices.contains(where: { $0.position == .back })
}
}

// MARK: - Selection (imperative)

public extension DeviceManager {
@MainActor
func select(audioInput: AudioDevice) {
selectedAudioInput = audioInput
AudioManager.shared.inputDevice = audioInput
}

@MainActor
func select(audioOutput: AudioDevice) {
selectedAudioOutput = audioOutput
AudioManager.shared.outputDevice = audioOutput
}

@MainActor
func select(videoCapture: VideoCaptureDevice) {
selectedVideoCapture = videoCapture
}

@MainActor
func selectCamera(position: CameraPosition) {
selectedVideoCapture = videoCaptureDevices.first(where: { $0.position == position })
}

@MainActor
func switchCameraPosition() {
let current = selectedVideoCapture?.position ?? .front
selectCamera(position: current == .front ? .back : .front)
}
}

// MARK: - Selection by ID (for persistence/restoration)

public extension DeviceManager {
@MainActor
@discardableResult
func selectAudioInput(byId deviceId: String) -> Bool {
guard let device = audioInputDevices.first(where: { $0.deviceId == deviceId }) else {
return false
}
select(audioInput: device)
return true
}

@MainActor
@discardableResult
func selectAudioOutput(byId deviceId: String) -> Bool {
guard let device = audioOutputDevices.first(where: { $0.deviceId == deviceId }) else {
return false
}
select(audioOutput: device)
return true
}

@MainActor
@discardableResult
func selectVideoCapture(byId deviceId: String) -> Bool {
guard let device = videoCaptureDevices.first(where: { $0.deviceId == deviceId }) else {
return false
}
select(videoCapture: device)
return true
}
}

// MARK: - SwiftUI Bindings

public extension DeviceManager {
@MainActor
var audioInputBinding: Binding<AudioDevice?> {
Binding(
get: { self.selectedAudioInput },
set: { newDevice in
if let newDevice {
self.select(audioInput: newDevice)
} else {
self.selectedAudioInput = nil
}
}
)
}

@MainActor
var audioOutputBinding: Binding<AudioDevice?> {
Binding(
get: { self.selectedAudioOutput },
set: { newDevice in
if let newDevice {
self.select(audioOutput: newDevice)
} else {
self.selectedAudioOutput = nil
}
}
)
}

@MainActor
var videoCaptureBinding: Binding<VideoCaptureDevice?> {
Binding(
get: { self.selectedVideoCapture },
set: { newDevice in
if let newDevice {
self.select(videoCapture: newDevice)
} else {
self.selectedVideoCapture = nil
}
}
)
}

@MainActor
var cameraPositionBinding: Binding<CameraPosition> {
Binding(
get: { self.selectedVideoCapture?.position ?? .unspecified },
set: { newPosition in
self.selectCamera(position: newPosition)
}
)
}
}
107 changes: 102 additions & 5 deletions Sources/LiveKit/Support/Video/DeviceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,33 @@
*/

@preconcurrency import AVFoundation
import Combine

// Internal-only for now
class DeviceManager: @unchecked Sendable, Loggable {
public class DeviceManager: ObservableObject, @unchecked Sendable, Loggable {
// MARK: - Public

static let shared = DeviceManager()
public static let shared = DeviceManager()

static func prepare() {
public static func prepare() {
// Instantiate shared instance
_ = shared
}

// Async version, waits until inital device fetch is complete
// MARK: - Published Properties

@Published public internal(set) var videoCaptureDevices: [VideoCaptureDevice] = []
@Published public internal(set) var audioInputDevices: [AudioDevice] = []
@Published public internal(set) var audioOutputDevices: [AudioDevice] = []

@Published public internal(set) var selectedVideoCapture: VideoCaptureDevice?
@Published public internal(set) var selectedAudioInput: AudioDevice?
@Published public internal(set) var selectedAudioOutput: AudioDevice?

@Published public internal(set) var error: (any Error)?

// MARK: - Internal (video discovery)

// Async version, waits until initial device fetch is complete
func devices() async throws -> [AVCaptureDevice] {
try await _devicesCompleter.wait()
}
Expand Down Expand Up @@ -134,6 +148,89 @@
}
}
#endif

// Wire video device observation
_state.onDidMutate = { [weak self] newState, _ in
let videoDevices = newState.devices.map { VideoCaptureDevice(avCaptureDevice: $0) }
Task { @MainActor [weak self] in
guard let self else { return }
videoCaptureDevices = videoDevices

Check failure on line 157 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'videoCaptureDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 157 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'videoCaptureDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 157 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'videoCaptureDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 157 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'videoCaptureDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 157 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'videoCaptureDevices' in closure requires explicit use of 'self' to make capture semantics explicit
reconcileVideoSelection()

Check failure on line 158 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 158 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 158 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 158 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 158 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit
}
}

// Wire audio device observation
observeAudioDevices()
}

// MARK: - Audio Observation

private func observeAudioDevices() {
let existingCallback = AudioManager.shared.onDeviceUpdate

AudioManager.shared.onDeviceUpdate = { [weak self] audioManager in
let inputDevices = audioManager.inputDevices
let outputDevices = audioManager.outputDevices
let inputDevice = audioManager.inputDevice
let outputDevice = audioManager.outputDevice

Task { @MainActor [weak self] in
guard let self else { return }
audioInputDevices = inputDevices

Check failure on line 179 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'audioInputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 179 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'audioInputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 179 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'audioInputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 179 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'audioInputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 179 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'audioInputDevices' in closure requires explicit use of 'self' to make capture semantics explicit
audioOutputDevices = outputDevices

Check failure on line 180 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'audioOutputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 180 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'audioOutputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 180 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'audioOutputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 180 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'audioOutputDevices' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 180 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'audioOutputDevices' in closure requires explicit use of 'self' to make capture semantics explicit
// Update selected if not yet set
if selectedAudioInput == nil {

Check failure on line 182 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 182 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 182 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 182 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 182 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit
selectedAudioInput = inputDevice

Check failure on line 183 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 183 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 183 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 183 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 183 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'selectedAudioInput' in closure requires explicit use of 'self' to make capture semantics explicit
}
if selectedAudioOutput == nil {

Check failure on line 185 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 185 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 185 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 185 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 185 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit
selectedAudioOutput = outputDevice

Check failure on line 186 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 186 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 186 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 186 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit

Check failure on line 186 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

reference to property 'selectedAudioOutput' in closure requires explicit use of 'self' to make capture semantics explicit
}
reconcileAudioSelection()

Check failure on line 188 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 188 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, iOS Simulator,name=iPhone 16 Pro,OS=18.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 188 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, tvOS Simulator,name=Apple TV,OS=18.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 188 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, visionOS Simulator,name=Apple Vision Pro,OS=2.5)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Check failure on line 188 in Sources/LiveKit/Support/Video/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build & Test (macos-15, 16.4, macOS,variant=Mac Catalyst)

implicit use of 'self' in closure; use 'self.' to make capture semantics explicit
}

existingCallback?(audioManager)
}

// Initial population
Task { @MainActor [weak self] in
guard let self else { return }
audioInputDevices = AudioManager.shared.inputDevices
audioOutputDevices = AudioManager.shared.outputDevices
selectedAudioInput = AudioManager.shared.inputDevice
selectedAudioOutput = AudioManager.shared.outputDevice
}
}

// MARK: - Reconciliation

@MainActor
func reconcileVideoSelection() {
if let selected = selectedVideoCapture,
!videoCaptureDevices.contains(where: { $0.deviceId == selected.deviceId })
{
selectedVideoCapture = videoCaptureDevices.first
}
}

@MainActor
func reconcileAudioSelection() {
if let selected = selectedAudioInput,
!audioInputDevices.contains(where: { $0.deviceId == selected.deviceId })
{
selectedAudioInput = audioInputDevices.first(where: \.isDefault)
}
if let selected = selectedAudioOutput,
!audioOutputDevices.contains(where: { $0.deviceId == selected.deviceId })
{
selectedAudioOutput = audioOutputDevices.first(where: \.isDefault)
}
}

// MARK: - Error

@MainActor
public func dismissError() {
error = nil
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/LiveKit/Types/AudioDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ public class AudioDevice: NSObject, MediaDevice {
extension AudioDevice: Identifiable {
public var id: String { deviceId }
}

extension AudioDevice: DeviceProtocol {}

extension AudioDevice: @unchecked Sendable {}
73 changes: 73 additions & 0 deletions Sources/LiveKit/Types/DeviceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@preconcurrency import AVFoundation

/// A device that can capture or output media.
public protocol DeviceProtocol: Identifiable, Hashable, Sendable {
var deviceId: String { get }
var name: String { get }
}

public extension DeviceProtocol {
var id: String { deviceId }
}

// MARK: - CameraPosition

/// The facing position of a camera device.
public enum CameraPosition: String, Sendable, CaseIterable {
case front
case back
case unspecified

init(from position: AVCaptureDevice.Position) {
switch position {
case .front: self = .front
case .back: self = .back
default: self = .unspecified
}
}
}

// MARK: - VideoCaptureDevice

/// A video capture device (camera).
public struct VideoCaptureDevice: DeviceProtocol {
public let deviceId: String
public let name: String
public let position: CameraPosition

/// Internal — used to bridge to CameraCaptureOptions.
let _avCaptureDevice: AVCaptureDevice

init(avCaptureDevice: AVCaptureDevice) {
deviceId = avCaptureDevice.uniqueID
name = avCaptureDevice.localizedName
position = CameraPosition(from: avCaptureDevice.facingPosition)
_avCaptureDevice = avCaptureDevice
}

// MARK: - Hashable

public func hash(into hasher: inout Hasher) {
hasher.combine(deviceId)
}

public static func == (lhs: VideoCaptureDevice, rhs: VideoCaptureDevice) -> Bool {
lhs.deviceId == rhs.deviceId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

public extension CameraCaptureOptions {
convenience init(
device: VideoCaptureDevice,
dimensions: Dimensions = .h720_169,
fps: Int = 30
) {
self.init(device: device._avCaptureDevice, dimensions: dimensions, fps: fps)
}
}
Loading