Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly handle broadcast capture state #551

Merged
merged 19 commits into from
Feb 3, 2025
Merged

Conversation

ladvoc
Copy link
Contributor

@ladvoc ladvoc commented Jan 18, 2025

When using the broadcast capturer on iOS, the broadcast extension now drives whether or not screen sharing is enabled, publishing a screen sharing track when the extension begins broadcasting upon user approval. Additionally, the new BroadcastManager class gives developers control over the broadcast state and track publication.

Fixes #444 and fixes #472.

Minor breaking changes

  • Calling setScreenShare(enabled: true) or set(source:enabled:captureOptions:publishOptions:) on LocalParticipant currently returns a LocalTrackPublication representing the newly published screen share track. After this change, when using the broadcast capturer on iOS, this method will return nil, as the track is published asynchronously pending user approval. Developers should treat enabling screen share as a request that might not be fulfilled and should not interpret a nil return value from this method as an error.
  • Since track publication is now asynchronous, capture options must be set as room defaults rather than being passed to set(source:enabled:captureOptions:publishOptions:) when enabling screen sharing.

When using the broadcast capturer on iOS, the broadcast extension now drives whether or not screen sharing is enabled, only publishing a screen sharing track when the extension actually begins broadcasting. Fixes livekit#444
Adds the ability to get a publisher for receiving notifications.
Copy link
Member

@hiroshihorie hiroshihorie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@bcherry bcherry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks pretty good and seems to be the right approach.

One design question besides what's in the review comments is how does a developer "stop" a broadcast if they don't want it? Right now, their control is only over whether it is published or not. there needs to be a way to stop it, too...

Another question is is this a breaking change? it looks like maybe it's actually not a breaking change and it will "just work"?

Sources/LiveKit/Participant/LocalParticipant.swift Outdated Show resolved Hide resolved
Sources/LiveKit/Participant/LocalParticipant.swift Outdated Show resolved Hide resolved
@ladvoc
Copy link
Contributor Author

ladvoc commented Jan 20, 2025

I think this looks pretty good and seems to be the right approach.

One design question besides what's in the review comments is how does a developer "stop" a broadcast if they don't want it? Right now, their control is only over whether it is published or not. there needs to be a way to stop it, too...

Another question is is this a breaking change? it looks like maybe it's actually not a breaking change and it will "just work"?

Currently, there is no method to stop a broadcast other than by unpublishing the screen share track. To address your other comments, I am adding a method to stop the broadcast even if a track has not been published.

Regarding whether this constitutes a breaking change, there are a few points to consider. At present, when calling setScreenShare(enabled: true), a LocalTrackPublication? is returned, representing the newly published screen share track. After this change, however, when using the broadcast capturer on iOS, the result of this call will be nil, as the track is published asynchronously. Since the return type is already optional, some developers may rely on this return value to determine whether the screen share was successful—even though in this case, nil does not indicate an error.

Please let me know your thoughts on this.

@bcherry
Copy link
Contributor

bcherry commented Jan 21, 2025

Currently, there is no method to stop a broadcast other than by unpublishing the screen share track. To address your other comments, I am adding a method to stop the broadcast even if a track has not been published.

yeah it makes sense to add something new since broadcast is no longer explicitly tied to publication.

Regarding whether this constitutes a breaking change, there are a few points to consider. At present, when calling setScreenShare(enabled: true), a LocalTrackPublication? is returned, representing the newly published screen share track. After this change, however, when using the broadcast capturer on iOS, the result of this call will be nil, as the track is published asynchronously. Since the return type is already optional, some developers may rely on this return value to determine whether the screen share was successful—even though in this case, nil does not indicate an error.

that does sound like a minor breaking change so we'll be sure to update the sdk minor version and include a clear note about it in the release notes. since the default "detailed changelog" is just the git history, can you ensure the description on this PR includes notes about how this would affect existing apps and what they need to change to adapt? that will then go into the sqaushed commit as a description and be linked in the history as well

@bcherry
Copy link
Contributor

bcherry commented Jan 21, 2025

looks like this will also close #472

@ladvoc
Copy link
Contributor Author

ladvoc commented Jan 31, 2025

I have implemented the requested changes and added some tests. Additionally, I refactored the public-facing API into a dedicated class, BroadcastManager. The public interface for this class is as follows:

/// Manages the broadcast state and track publication for screen sharing on iOS.
public final class BroadcastManager : Sendable {

    public static let shared: BroadcastManager

    /// A delegate for handling broadcast state changes.
    public var delegate: BroadcastManagerDelegate? { get set }

    /// Indicates whether a broadcast is currently in progress.
    public var isBroadcasting: Bool { get }

    /// A publisher that emits the current broadcast state as a Boolean value.
    public var isBroadcastingPublisher: AnyPublisher<Bool, Never> { get }

    /// Determines whether a screen share track should be automatically published when broadcasting starts.
    ///
    /// Set this to `false` to manually manage track publication when the broadcast starts.
    ///
    public var shouldPublishTrack: Bool { get set }

    /// Displays the system broadcast picker, allowing the user to start the broadcast.
    ///
    /// - Note: This is merely a request and does not guarantee the user will choose to start the broadcast.
    ///
    public func requestActivation()

    /// Requests to stop the broadcast.
    ///
    /// If a screen share track is published, it will also be unpublished once the broadcast ends.
    /// This method has no effect if no broadcast is currently in progress.
    ///
    public func requestStop()
}

/// A delegate protocol for receiving updates about the broadcast state.
@objc public protocol BroadcastManagerDelegate {

    func broadcastManager(didChangeState isBroadcasting: Bool)
}

Let me know if you have any further questions or suggestions.

Track published asynchronously now explicitly uses `useBroadcastExtension` set to true
Encapsulates settings related to broadcast extension.
If a broadcast extension is properly configured, the `useBroadcastExtension` property on `ScreenShareCaptureOptions` will default to `true`.
Copy link
Contributor

@bcherry bcherry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah looks very close!

Sources/LiveKit/Types/Options/RoomOptions.swift Outdated Show resolved Hide resolved
Sources/LiveKit/Participant/LocalParticipant.swift Outdated Show resolved Hide resolved
Copy link
Contributor

@bcherry bcherry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just tested, this works great. last thing you should do before we merge it is can you go also include any updates to the doc you wrote that are needed? It seems like a very minimum it needs to be clear that you should prefer to set screenshare options on the room instead of in the call.

@ladvoc
Copy link
Contributor Author

ladvoc commented Feb 3, 2025

just tested, this works great. last thing you should do before we merge it is can you go also include any updates to the doc you wrote that are needed? It seems like a very minimum it needs to be clear that you should prefer to set screenshare options on the room instead of in the call.

That is a good idea. I am also documenting the use of BroadcastManager.

@bcherry bcherry merged commit fbec343 into livekit:main Feb 3, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants