Describe the bug
What I'm expecting
setCameraEnabled(true, captureOptions) applies captureOptions to the camera it brings back.
What happens instead
captureOptions is silently ignored whenever a muted publication for that source already exists — which is the normal state after setCameraEnabled(false), since disabling a camera mutes the publication rather than unpublishing it (only screen share unpublishes).
LocalParticipant.setTrackEnabled:
let track = this.getTrackPublication(source);
if (enabled) {
if (track) {
await track.unmute(); // options never used
} else {
localTracks = await this.createTracks({ video: options ?? true });
}
For a camera source, LocalVideoTrack.unmute() calls restart(undefined, true), and LocalTrack.restart() falls back to this._constraints when called without constraints — so the track comes back with the resolution, frame rate and facing mode it was originally created with, not the ones just requested.
The parameter is accepted and dropped without a warning, which is the part that costs downstream debugging time: the call looks correct and the UI shows the new setting, while the publisher keeps sending the old one.
Reproduction
await room.localParticipant.setCameraEnabled(true, {
resolution: VideoPresets.h720.resolution,
});
await room.localParticipant.setCameraEnabled(false);
await room.localParticipant.setCameraEnabled(true, {
resolution: VideoPresets.h1080.resolution,
});
// getSettings() on the published track still reports 1280x720
Found while working on suitenumerique/meet, where the UI passes the user's chosen resolution to the camera toggle: a resolution picked while the camera is off is never applied when it is turned back on.
Logs
No warning is emitted on the dropped options; `restart` logs only
"restarting track with constraints" with the previous constraints.
System Info
livekit-client: 2.21.0
macOS 26.5, Chrome 152.0.7977.84
The branch above is identical in the published ESM builds of 2.19.0, 2.21.0 and 2.22.3, so this is not specific to one version or browser.
Severity
serious, but I can work around it
Suggested fix
On the unmute branch, apply options when they differ from the track's current constraints — or, at minimum, log that they were dropped.
The workarounds available to an application are both unattractive: a second getUserMedia right after unmute, which visibly blinks the camera, or unpublishing the track on camera-off instead of muting it, which forces a renegotiation on every toggle.
Describe the bug
What I'm expecting
setCameraEnabled(true, captureOptions)appliescaptureOptionsto the camera it brings back.What happens instead
captureOptionsis silently ignored whenever a muted publication for that source already exists — which is the normal state aftersetCameraEnabled(false), since disabling a camera mutes the publication rather than unpublishing it (only screen share unpublishes).LocalParticipant.setTrackEnabled:For a camera source,
LocalVideoTrack.unmute()callsrestart(undefined, true), andLocalTrack.restart()falls back tothis._constraintswhen called without constraints — so the track comes back with the resolution, frame rate and facing mode it was originally created with, not the ones just requested.The parameter is accepted and dropped without a warning, which is the part that costs downstream debugging time: the call looks correct and the UI shows the new setting, while the publisher keeps sending the old one.
Reproduction
Found while working on suitenumerique/meet, where the UI passes the user's chosen resolution to the camera toggle: a resolution picked while the camera is off is never applied when it is turned back on.
Logs
System Info
The branch above is identical in the published ESM builds of 2.19.0, 2.21.0 and 2.22.3, so this is not specific to one version or browser.
Severity
serious, but I can work around it
Suggested fix
On the unmute branch, apply
optionswhen they differ from the track's current constraints — or, at minimum, log that they were dropped.The workarounds available to an application are both unattractive: a second
getUserMediaright after unmute, which visibly blinks the camera, or unpublishing the track on camera-off instead of muting it, which forces a renegotiation on every toggle.