@@ -32,16 +32,19 @@ public class MacOSScreenCapturer: VideoCapturer {
3232 // TODO: Make it possible to change dynamically
3333 public let captureSource : MacOSScreenCaptureSource ?
3434
35- // SCStream
36- private var _scStream : SCStream ?
37-
38- // cached frame for resending to maintain minimum of 1 fps
39- private var _lastFrame : LKRTCVideoFrame ?
40- private var _resendTimer : Task < Void , Error > ?
41-
4235 /// The ``ScreenShareCaptureOptions`` used for this capturer.
4336 public let options : ScreenShareCaptureOptions
4437
38+ struct State {
39+ // SCStream
40+ var scStream : SCStream ?
41+ // Cached frame for resending to maintain minimum of 1 fps
42+ var lastFrame : LKRTCVideoFrame ?
43+ var resendTimer : Task < Void , Error > ?
44+ }
45+
46+ private var _screenCapturerState = StateSync ( State ( ) )
47+
4548 init ( delegate: LKRTCVideoCapturerDelegate , captureSource: MacOSScreenCaptureSource , options: ScreenShareCaptureOptions ) {
4649 self . captureSource = captureSource
4750 self . options = options
@@ -96,7 +99,7 @@ public class MacOSScreenCapturer: VideoCapturer {
9699 try stream. addStreamOutput ( self , type: . screen, sampleHandlerQueue: nil )
97100 try await stream. startCapture ( )
98101
99- _scStream = stream
102+ _screenCapturerState . mutate { $0 . scStream = stream }
100103
101104 return true
102105 }
@@ -107,17 +110,22 @@ public class MacOSScreenCapturer: VideoCapturer {
107110 // Already stopped
108111 guard didStop else { return false }
109112
110- guard let stream = _scStream else {
113+ guard let stream = _screenCapturerState . read ( { $0 . scStream } ) else {
111114 throw LiveKitError ( . invalidState, message: " SCStream is nil " )
112115 }
113116
114117 // Stop resending paused frames
115- _resendTimer? . cancel ( )
116- _resendTimer = nil
118+ _screenCapturerState. mutate {
119+ $0. resendTimer? . cancel ( )
120+ $0. resendTimer = nil
121+ }
117122
118123 try await stream. stopCapture ( )
119124 try stream. removeStreamOutput ( self , type: . screen)
120- _scStream = nil
125+
126+ _screenCapturerState. mutate {
127+ $0. scStream = nil
128+ }
121129
122130 return true
123131 }
@@ -149,10 +157,12 @@ public class MacOSScreenCapturer: VideoCapturer {
149157 rotation: . _0,
150158 timeStampNs: timeStampNs)
151159
152- capture ( frame: rtcFrame, capturer: capturer, options: options)
160+ // Cache last frame
161+ _screenCapturerState. mutate {
162+ $0. lastFrame = rtcFrame
163+ }
153164
154- // cache last frame
155- _lastFrame = rtcFrame
165+ capture ( frame: rtcFrame, capturer: capturer, options: options)
156166 }
157167}
158168
@@ -167,9 +177,9 @@ extension MacOSScreenCapturer {
167177 return
168178 }
169179
170- log ( " No movement detected, resending frame... " )
180+ log ( " No movement detected, resending frame... " , . trace )
171181
172- guard let frame = _lastFrame else { return }
182+ guard let frame = _screenCapturerState . read ( { $0 . lastFrame } ) else { return }
173183
174184 // create a new frame with new time stamp
175185 let newFrame = LKRTCVideoFrame ( buffer: frame. buffer,
@@ -215,17 +225,22 @@ extension MacOSScreenCapturer: SCStreamOutput {
215225 // let contentScale = attachments[.contentScale] as? CGFloat,
216226 let scaleFactor = attachments [ . scaleFactor] as? CGFloat else { return }
217227
218- capture ( sampleBuffer, contentRect: contentRect, scaleFactor: scaleFactor)
219-
220- _resendTimer? . cancel ( )
221- _resendTimer = Task . detached ( priority: . utility) { [ weak self] in
228+ // Schedule resend timer
229+ let newTimer = Task . detached ( priority: . utility) { [ weak self] in
222230 while true {
223231 try ? await Task . sleep ( nanoseconds: UInt64 ( 1 * 1_000_000_000 ) )
224232 if Task . isCancelled { break }
225233 guard let self else { break }
226234 try await self . _capturePreviousFrame ( )
227235 }
228236 }
237+
238+ _screenCapturerState. mutate {
239+ $0. resendTimer? . cancel ( )
240+ $0. resendTimer = newTimer
241+ }
242+
243+ capture ( sampleBuffer, contentRect: contentRect, scaleFactor: scaleFactor)
229244 }
230245}
231246
0 commit comments