diff --git a/.changes/screen-share-survives-reconnect b/.changes/screen-share-survives-reconnect new file mode 100644 index 000000000..fcd245c7b --- /dev/null +++ b/.changes/screen-share-survives-reconnect @@ -0,0 +1 @@ +patch type="fixed" "Screen sharing no longer fails to resume after a full reconnect: the capture source (iOS broadcast extension IPC, ReplayKit) is kept alive while the track is reattached to the new publisher" diff --git a/Sources/LiveKit/Participant/LocalParticipant.swift b/Sources/LiveKit/Participant/LocalParticipant.swift index 146fda51b..06d17971d 100644 --- a/Sources/LiveKit/Participant/LocalParticipant.swift +++ b/Sources/LiveKit/Participant/LocalParticipant.swift @@ -304,6 +304,21 @@ extension LocalParticipant { func republishAllTracks() async throws { let mediaTracks = _state.trackPublications.values.map { $0.track as? LocalTrack }.compactMap(\.self) + // Detach screen share tracks without stopping their capturers — the + // underlying sources (iOS broadcast extension IPC, ReplayKit) cannot + // be restarted programmatically. + let screenShareTracks = mediaTracks.filter { $0.source == .screenShareVideo } + for track in screenShareTracks { + let sidsToRemove = _state.trackPublications.filter { $0.value.track === track }.map(\.key) + _state.mutate { + for sid in sidsToRemove { + $0.trackPublications.removeValue(forKey: sid) + } + } + await track.set(transport: nil, rtpSender: nil) + track._state.mutate { $0.rtpSenderForCodec.removeAll() } + } + await unpublishAll() for mediaTrack in mediaTracks {