Skip to content

Commit acde14a

Browse files
Actually stop the track when the broadcast extension socket is closed (#520)
This closes #446 Two fixes: ~1. It seems that stopping the broadcast via tapping on the system indicator and choosing "stop broadcast" calls the `broadcastPaused` method instead of the `broadcastFinished` method on the SampleHandler. The only way I could trigger `broadcastFinished` in testing was by locking my device. I could not find a way to trigger `broadcastResumed` so I just went ahead and treated "pause" as final, and close the socket (I think maybe this is vestigial from older iterations of ReplayKit? not sure, it's not an area I'm super familiar with)~ 2. When the socket closes the frame reader stops but never notified the BroadcastScreenCapturer that owns it, so the track would stay published even as it stopped being updated. This fix adds a new callback method to pass the closure up the stack and stop the track. Not sure what happened in original testing but actually broadcastFinished _is_ called as expected, and broadcastPaused is called when you open the "stop capture" dialog, not when you finish it. I also fixed it to properly unpublish the track, not sure why initial testing showed simply ending capture to be enough. --------- Co-authored-by: hiroshihorie <[email protected]>
1 parent 970e609 commit acde14a

File tree

3 files changed

+24
-0
lines changed

3 files changed

+24
-0
lines changed

Sources/LiveKit/Broadcast/BroadcastScreenCapturer.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ class BroadcastScreenCapturer: BufferCapturer {
6262
frameReader.didCapture = { pixelBuffer, rotation in
6363
self.capture(pixelBuffer, rotation: rotation.toLKType())
6464
}
65+
frameReader.didEnd = { [weak self] in
66+
guard let self else { return }
67+
Task {
68+
try await self.stopCapture()
69+
}
70+
}
6571
frameReader.startCapture(with: socketConnection)
6672
self.frameReader = frameReader
6773

Sources/LiveKit/Broadcast/SocketConnectionFrameReader.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class SocketConnectionFrameReader: NSObject {
137137

138138
private var message: Message?
139139
var didCapture: ((CVPixelBuffer, RTCVideoRotation) -> Void)?
140+
var didEnd: (() -> Void)?
140141

141142
override init() {}
142143

@@ -227,6 +228,7 @@ extension SocketConnectionFrameReader: StreamDelegate {
227228
case .endEncountered:
228229
logger.log(level: .debug, "server stream end encountered")
229230
stopCapture()
231+
didEnd?()
230232
case .errorOccurred:
231233
logger.log(level: .debug, "server stream error encountered: \(aStream.streamError?.localizedDescription ?? "")")
232234
default:

Sources/LiveKit/TrackPublications/LocalTrackPublication.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ extension LocalTrackPublication: VideoCapturerDelegate {
105105
}
106106
}
107107
}
108+
109+
public func capturer(_ capturer: VideoCapturer, didUpdate state: VideoCapturer.CapturerState) {
110+
// Broadcasts can always be stopped from system UI that bypasses our normal disable & unpublish methods.
111+
// This check ensures that when this happens the track gets unpublished as well.
112+
#if os(iOS)
113+
if state == .stopped, capturer is BroadcastScreenCapturer {
114+
Task {
115+
guard let participant = try await self.requireParticipant() as? LocalParticipant else {
116+
return
117+
}
118+
119+
try await participant.unpublish(publication: self)
120+
}
121+
}
122+
#endif
123+
}
108124
}
109125

110126
extension LocalTrackPublication {

0 commit comments

Comments
 (0)