@@ -3,8 +3,24 @@ import LiveKit
33
44@MainActor
55final class DeviceSwitcher : ObservableObject {
6+ // MARK: Error
7+
8+ enum Error : LocalizedError {
9+ case mediaDevice( Swift . Error )
10+ }
11+
612 // MARK: Devices
713
14+ @Published private( set) var error : Error ?
15+
16+ @Published private( set) var localAudioTrack : ( any AudioTrack ) ?
17+ @Published private( set) var localCameraTrack : ( any VideoTrack ) ?
18+ @Published private( set) var localScreenShareTrack : ( any VideoTrack ) ?
19+
20+ var isMicrophoneEnabled : Bool { localAudioTrack != nil }
21+ var isCameraEnabled : Bool { localCameraTrack != nil }
22+ var isScreenShareEnabled : Bool { localScreenShareTrack != nil }
23+
824 @Published private( set) var audioDevices : [ AudioDevice ] = AudioManager . shared. inputDevices
925 @Published private( set) var selectedAudioDeviceID : String = AudioManager . shared. inputDevice. deviceId
1026
@@ -22,9 +38,23 @@ final class DeviceSwitcher: ObservableObject {
2238 init ( room: Room ) {
2339 self . room = room
2440
41+ observeRoom ( )
2542 observeDevices ( )
2643 }
2744
45+ private func observeRoom( ) {
46+ Task { [ weak self] in
47+ guard let changes = self ? . room. changes else { return }
48+ for await _ in changes {
49+ guard let self else { return }
50+
51+ localAudioTrack = room. localParticipant. firstAudioTrack
52+ localCameraTrack = room. localParticipant. firstCameraVideoTrack
53+ localScreenShareTrack = room. localParticipant. firstScreenShareVideoTrack
54+ }
55+ }
56+ }
57+
2858 private func observeDevices( ) {
2959 try ? AudioManager . shared. set ( microphoneMuteMode: . inputMixer) // don't play mute sound effect
3060 Task {
@@ -49,9 +79,46 @@ final class DeviceSwitcher: ObservableObject {
4979 AudioManager . shared. onDeviceUpdate = nil
5080 }
5181
52- // MARK: - Actions
82+ // MARK: - Toggle
83+
84+ func toggleMicrophone( ) async {
85+ do {
86+ try await room. localParticipant. setMicrophone ( enabled: !isMicrophoneEnabled)
87+ } catch {
88+ self . error = . mediaDevice( error)
89+ }
90+ }
91+
92+ func toggleCamera( ) async {
93+ let enable = !isCameraEnabled
94+ do {
95+ // One video track at a time
96+ if enable, isScreenShareEnabled {
97+ try await room. localParticipant. setScreenShare ( enabled: false )
98+ }
99+
100+ let device = try await CameraCapturer . captureDevices ( ) . first ( where: { $0. uniqueID == selectedVideoDeviceID } )
101+ try await room. localParticipant. setCamera ( enabled: enable, captureOptions: CameraCaptureOptions ( device: device) )
102+ } catch {
103+ self . error = . mediaDevice( error)
104+ }
105+ }
106+
107+ func toggleScreenShare( ) async {
108+ let enable = !isScreenShareEnabled
109+ do {
110+ // One video track at a time
111+ if enable, isCameraEnabled {
112+ try await room. localParticipant. setCamera ( enabled: false )
113+ }
114+ try await room. localParticipant. setScreenShare ( enabled: enable)
115+ } catch {
116+ self . error = . mediaDevice( error)
117+ }
118+ }
119+
120+ // MARK: - Select
53121
54- #if os(macOS)
55122 func select( audioDevice: AudioDevice ) {
56123 selectedAudioDeviceID = audioDevice. deviceId
57124
@@ -66,14 +133,14 @@ final class DeviceSwitcher: ObservableObject {
66133 let captureOptions = CameraCaptureOptions ( device: videoDevice)
67134 _ = try ? await cameraCapturer. set ( options: captureOptions)
68135 }
69- #endif
70136
71- // TODO: Move that to session?
72137 func switchCamera( ) async {
73138 guard let cameraCapturer = getCameraCapturer ( ) else { return }
74139 _ = try ? await cameraCapturer. switchCameraPosition ( )
75140 }
76141
142+ // MARK: - Private
143+
77144 private func getCameraCapturer( ) -> CameraCapturer ? {
78145 guard let cameraTrack = room. localParticipant. firstCameraVideoTrack as? LocalVideoTrack else { return nil }
79146 return cameraTrack. capturer as? CameraCapturer
0 commit comments