Skip to content

Commit

Permalink
Actually stop the track when the broadcast extension socket is closed (
Browse files Browse the repository at this point in the history
…#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]>
  • Loading branch information
bcherry and hiroshihorie authored Dec 5, 2024
1 parent 970e609 commit acde14a
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Sources/LiveKit/Broadcast/BroadcastScreenCapturer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ class BroadcastScreenCapturer: BufferCapturer {
frameReader.didCapture = { pixelBuffer, rotation in
self.capture(pixelBuffer, rotation: rotation.toLKType())
}
frameReader.didEnd = { [weak self] in
guard let self else { return }
Task {
try await self.stopCapture()
}
}
frameReader.startCapture(with: socketConnection)
self.frameReader = frameReader

Expand Down
2 changes: 2 additions & 0 deletions Sources/LiveKit/Broadcast/SocketConnectionFrameReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class SocketConnectionFrameReader: NSObject {

private var message: Message?
var didCapture: ((CVPixelBuffer, RTCVideoRotation) -> Void)?
var didEnd: (() -> Void)?

override init() {}

Expand Down Expand Up @@ -227,6 +228,7 @@ extension SocketConnectionFrameReader: StreamDelegate {
case .endEncountered:
logger.log(level: .debug, "server stream end encountered")
stopCapture()
didEnd?()
case .errorOccurred:
logger.log(level: .debug, "server stream error encountered: \(aStream.streamError?.localizedDescription ?? "")")
default:
Expand Down
16 changes: 16 additions & 0 deletions Sources/LiveKit/TrackPublications/LocalTrackPublication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ extension LocalTrackPublication: VideoCapturerDelegate {
}
}
}

public func capturer(_ capturer: VideoCapturer, didUpdate state: VideoCapturer.CapturerState) {
// Broadcasts can always be stopped from system UI that bypasses our normal disable & unpublish methods.
// This check ensures that when this happens the track gets unpublished as well.
#if os(iOS)
if state == .stopped, capturer is BroadcastScreenCapturer {
Task {
guard let participant = try await self.requireParticipant() as? LocalParticipant else {
return
}

try await participant.unpublish(publication: self)
}
}
#endif
}
}

extension LocalTrackPublication {
Expand Down

0 comments on commit acde14a

Please sign in to comment.