Repository navigation
Refresh subscribed codecs after publisher answer #1037
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| "client-sdk-android": patch | ||
| --- | ||
|
|
||
| Keep dynacast-paused simulcast layers paused across publisher renegotiations. Previously a paused layer started to upload again after any renegotiation of the publisher connection. It stayed on until the next subscribed-quality change. Publishing a track, unpublishing one, a codec change, and an ICE restart all caused this. | ||
|
|
||
| The server's answer to a publisher offer no longer carries the RFC 8853 pause markers. Applying that answer re-enables every layer the client paused. If the subscribed set does not change, the server sends no subscribed-codec update, so the pause was never restated. Directly after it applies a publisher answer, the SDK now re-applies the last known subscribed codecs. | ||
|
|
||
| One frame per layer can still escape, because the encoders restart during the apply. This matches the JavaScript and Rust SDKs. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -430,6 +430,18 @@ constructor( | |
| return newCodecs | ||
| } | ||
|
|
||
| /** | ||
| * Re-applies the most recently received subscribed codecs. | ||
| * | ||
| * Applying a publisher answer re-enables simulcast layers that dynacast had paused, and the | ||
| * server only sends a subscribed codec update when the set changes, so the pause is restated | ||
| * here instead. | ||
| */ | ||
| internal fun refreshSubscribedCodecs() { | ||
| val codecs = subscribedCodecs ?: return | ||
| setPublishingCodecs(codecs) | ||
| } | ||
|
Comment on lines
+440
to
+443
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Quality-only pauses vanish after renegotiation After a quality-only update, Learn moreA subscribed-quality update can contain a flat list of qualities without any subscribed codecs. handleSubscribedQualityUpdate applies that list directly with Example: A server sends an update for camera A with Recommended fix: Store the last flat quality update as well as the codec update and replay whichever form most recently controlled the primary sender. Clear both on unpublish or republish, and cover quality-only updates in the renegotiation test. Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| internal fun addSimulcastTrack(codec: VideoCodec, encodings: List<RtpParameters.Encoding>): SimulcastTrackInfo? { | ||
| if (this.simulcastCodecs.containsKey(codec)) { | ||
| LKLog.w { "$codec already added, skipping." } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Sender refresh bypasses the RTC thread
On a publisher answer,
onRequestSubscribedCodecRefreshaccesses WebRTC sender parameters from the engine's I/O coroutine. This can race with RTC-thread negotiation and leave paused layers active.Learn more
The publisher answer is applied by setRemoteDescription on the RTC thread, but the suspended caller resumes on the engine's I/O dispatcher. The new synchronous callback reaches setPublishingLayersForSender, which reads and writes
RtpSender.parameters. Sender operations belong on the RTC thread according to the repository's WebRTC threading rule; otherwise they can overlap further negotiation work.Example: An answer for an ICE restart arrives while another publisher negotiation starts. The refresh accesses the sender on the I/O worker while the other negotiation uses that sender on the RTC executor.
Recommended fix: Schedule the refresh's WebRTC sender operations through
launchBlockingOnRTCThreadorexecuteBlockingOnRTCThreadwith the room's RTC token. Keep the cached codec state synchronized with signaling updates if the callback is dispatched asynchronously.Was this helpful? React with 👍 or 👎 to provide feedback.