Skip to content
Open
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
1 change: 1 addition & 0 deletions .changes/macos-26-camera-crash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
patch type="fixed" "Crash when starting the camera or microphone on macOS 26 due to the auto-bridged ObjC completion handler"
10 changes: 6 additions & 4 deletions Sources/LiveKit/Participant/LocalParticipant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ extension LocalParticipant {
// MARK: - Simplified API

public extension LocalParticipant {
@objc
// @nonobjc on these async methods: the @objcMembers-synthesized completion
// handler bridge SIGBUSes on macOS 26 (see #1022).
@nonobjc
@discardableResult
func setCamera(enabled: Bool,
captureOptions: CameraCaptureOptions? = nil,
Expand All @@ -329,7 +331,7 @@ public extension LocalParticipant {
publishOptions: publishOptions)
}

@objc
@nonobjc
@discardableResult
func setMicrophone(enabled: Bool,
captureOptions: AudioCaptureOptions? = nil,
Expand All @@ -350,13 +352,13 @@ public extension LocalParticipant {
/// to capture other screens and windows. See ``MacOSScreenCapturer`` for details.
///
/// For advanced usage, you can create a relevant ``LocalVideoTrack`` and call ``LocalParticipant/publishVideoTrack(track:publishOptions:)``.
@objc
@nonobjc
@discardableResult
func setScreenShare(enabled: Bool) async throws -> LocalTrackPublication? {
try await set(source: .screenShareVideo, enabled: enabled)
}

@objc
@nonobjc
@discardableResult
// swiftlint:disable:next cyclomatic_complexity function_body_length
func set(source: Track.Source,
Expand Down
19 changes: 17 additions & 2 deletions Sources/LiveKit/Track/Capturers/CameraCapturer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,17 @@ public class CameraCapturer: VideoCapturer, @unchecked Sendable {

log("starting camera capturer device: \(device), format: \(selectedFormat), fps: \(selectedFps)(\(fpsRange))", .info)

try await capturer.startCapture(with: device, format: selectedFormat.format, fps: selectedFps)
// Call the completion-handler form directly: the compiler-synthesized
// async overload SIGBUSes on macOS 26 (see #1016).
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
capturer.startCapture(with: device, format: selectedFormat.format, fps: selectedFps) { error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: ())
}
}
}

// Update internal vars
_cameraCapturerState.mutate {
Expand All @@ -305,7 +315,12 @@ public class CameraCapturer: VideoCapturer, @unchecked Sendable {
// Already stopped
guard didStop else { return false }

await capturer.stopCapture()
// See note in `startCapture()` above.
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
capturer.stopCapture {
continuation.resume()
}
}

// Update internal vars
set(dimensions: nil)
Expand Down
Loading