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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

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. Fixes #444.

Minor breaking change
Calling setScreenShare(enabled: true) 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.

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"?


Task { [weak self] in
guard let self else { return }
let shouldPublish = await self.broadcastStarted?() ?? true
Copy link
Contributor

Choose a reason for hiding this comment

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

i think that method could have a clearer name, such as shouldPublishBroadcast, to make it more obvious how it fits in

although, thinking more about it, perhaps it would be clearest if the method were simply called onBroadcastStart. if the developer implements it, then they're given complete control over what to do next. otherwise the default implementation is to call try await self.setScreenShare(enabled: true) if they're connected and to cancel the broadcast if they're not.

that way we don't need to let this task hang indefinitely if the developer has more complex needs (such as maybe the user is running broadcast in a "preview" mode not meant to be published right away)

return
}
do {
try await self.setScreenShare(enabled: true)
Copy link
Contributor

Choose a reason for hiding this comment

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

what happens if you're not already actively connected to a room?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setScreenShare(enabled:) calls down to set(source:enabled:captureOptions:publishOptions:) which in turn calls requireRoom(), performing a check to ensure the room isn't nil. I will need to perform some more examination to see if additional checks are necessary.

///
public var broadcastStarted: (() async -> Bool)?

private var isBroadcasting = false {
Copy link
Contributor

Choose a reason for hiding this comment

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

i think this or something like it needs to be publicly gettable, because developers may have UI that hinges on this state, in the case that they aren't starting publishing right away

Copy link
Contributor Author

@ladvoc ladvoc Jan 21, 2025

Choose a reason for hiding this comment

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

Is it okay if we expose a Combine AnyPublisher<Bool, Never> in the public API? If so, I could see making the isBroadcasting property a CurrentValueSubject which is type erased into AnyPublisher. This would allow both getting the current value and listening for changes. We could then replace the broadcastStarted closure with a Boolean publishOnBroadcast property which allows the developer to disable the default behavior to manually control track publication as per your suggestion. The public interface would then look something like this:

public var publishOnBroadcast: Bool
public var isBroadcasting: AnyPublisher<Bool, Never>

Copy link
Contributor

Choose a reason for hiding this comment

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

it's an interesting idea and i'd defer to @hiroshihorie but my instinct is that we should consider introducing new patterns carefully and deliberately. i'm not sure it's worth using something novel here that is unlike other things in our SDK, especially because I'm not sure there's a really strong reason we'd need to besides that it might be a little nicer.

we use a delegate pattern everywhere else. maybe good to make a new BroadcastDelegate that has this and the onStarted method in it

Copy link
Member

@hiroshihorie hiroshihorie Jan 22, 2025

Choose a reason for hiding this comment

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

I think @bcherry is right, I'm open to adding a AnyPublisher in addition to current design of reading value synchronously and notifying with delegate perhaps.

We eventually need to modernize the SDK and I think we can start here incrementally.

Copy link
Contributor Author

@ladvoc ladvoc Jan 22, 2025

Choose a reason for hiding this comment

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

@hiroshihorie, just to make sure I’m following you, are you thinking the public interface would look something like this then?

public var isBroadcasting: Bool
public var isBroadcastingPublisher: AnyPublisher<Bool, Never>

This would be in addition to the delegate methods.

@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

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