Conversation
createSimulcastTransceiverSender passed the TrackType enum's toString, 'tracktype.video', as the media kind for the capability lookup. The desktop plugin refuses it and the darwin plugin reads it as audio. The transceiver is always video, so pass 'video', the same as the primary publish does. The transceiver was also added to the publisher peer connection before the server was told about it, and nothing removed it when the publish failed afterwards, so the next offer carried a media section the server could not match and it bound the track's frames to whichever video track was pending. Remove the sender and drop the simulcast entry when anything fails before the add track request succeeds.
ecstra
requested review from
changt,
cloudwebrtc,
hiroshihorie and
xianshijing-lk
as code owners
September 29, 2026 06:08
A republish that raced the failure owns the codec key by then, so a stale attempt must not erase the newer entry (Devin's review).
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two fixes to the backup codec publish (
LocalParticipant.publishAdditionalCodecForPublication) on native platforms.Engine.createSimulcastTransceiverSenderpassedtrack.kind.toString().toLowerCase()tosetPreferredCodec, which istracktype.video(theTrackTypeenum'stoString), notvideo. The desktop plugin (common/cpp, Windows and Linux) refuses that kind ingetRtpReceiverCapabilitieswithPlatformException(getRtpSenderCapabilities, getRtpSenderCapabilities() kind is null or empty), and the darwin plugin'smediaTypeFromStringfalls back to audio, so macOS and iOS built the backup's codec preferences from audio capabilities. The transceiver is always video here, so the literalvideois passed, the same as the primary publish already does inlocal.dart.The transceiver is added to the publisher peer connection before the server is told about it, and nothing removed it when the publish failed afterwards. The next offer then carried a media section the server had no signalled track for, and the server (
getPendingTrack, "if no match on client id, find first one matching type") bound it to whichever video track was pending. In the field that was a screen share being published at the same moment: the camera's frames arrived under the share's publication. BothcreateSimulcastTransceiverSenderandpublishAdditionalCodecForPublicationnow remove the sender and drop the simulcast entry when anything fails before the add track request succeeds, and rethrow.How it was found
Flutter desktop clients (Windows and macOS, flutter_webrtc 1.6.0, livekit_client 2.11.0, livekit-server v1.13.6): every camera and share publish advertising a backup codec threw the exception above on Windows the moment the server asked for the backup, and a share published under the earlier share's WebRTC track id with "duplicate layer" warnings in the server log. With the kind passed as
videothe backup publish completes and signals its add track request under the publication's sid.Tests
No unit test is attached:
MockPeerConnection.addTransceiverthrowsUnimplementedError, so the path is not reachable from the mock harness without extending it. Happy to add one if you would like the mock to grow a transceiver.