11 KiB
Vendored livekit-client
Vendored from https://github.com/livekit/client-sdk-js at tag v2.22.3 (commit 53dd840) under the Apache-2.0 licence. Re-vendored from v2.17.2 on 2026-09-17.
Source transforms
Every file went through the same mechanical steps, so a fresh upstream tree can be brought into this shape before merging.
- Test files,
src/testand snapshots are dropped. Files that are not reachable fromsrc/index.tsorsrc/e2ee/worker/e2ee.worker.tsare dropped too. - Comments are stripped.
/// <reference>lines,@ts-expect-errordirectives that are still needed andbiome-ignorelines stay. - Relative imports get explicit
.tsextensions, and directory imports point atindex.ts. - Each file starts with the LiveKit SPDX header.
biome check --write --unsaferuns with the repo config. The unsafe fixes are reviewed by hand, becauseuseOptionalChainturnedcallback && callback(x)into a call onfalseinroom/debounce.ts, andnoUnusedPrivateClassMembersdeleted a field that was still written inutils/dataPacketBuffer.ts.- The tree is made to pass
tsgo --noEmitunderfluxer_app/tsconfig.json. That meansoverridemodifiers, explicitreturn undefined, typedletdeclarations, no async promise executors, literal enum members and no unused@ts-expect-errordirectives. @livekit/throws-transformer/throwsimports point atsrc/utils/throws.ts, which copies its type definitions, so the dev-only package is not a dependency.
Fluxer modifications
-
AV1 end-to-end encryption (
src/e2ee/worker/av1Utils.ts,FrameCryptor.ts,src/room/track/utils.ts)OBU-level AV1 encryption and decryption with an E2EE metadata OBU.
video/AV1Xmaps toav1. -
Cryptor context for reused senders (
src/e2ee/E2eeManager.ts,src/e2ee/worker/FrameCryptor.ts)Upstream 2.22 tracks the id of a reused receiver in
E2EE_TRACK_IDand sendsupdateCodecwithpreviousTrackId. Fluxer applies the same to senders, passes the codec and track id fromLocalSenderCreated, and resets codec detection whenever a cryptor moves to another track id or codec. This replaces the oldupdateTrackContextmessage andget/setE2EETransformState. -
E2EE frame guards (
src/e2ee/worker/FrameCryptor.ts,naluUtils.ts)Trailer, IV, tag and clear-prefix bounds are checked before any view is built, and malformed frames are dropped without ending the transform. NALU clear bytes are clamped to the frame length. H.265 slices keep 3 clear bytes where upstream keeps 2. The SIF trailer check is bounds safe.
-
No plaintext before the encryption state is known (
src/e2ee/worker/FrameCryptor.ts)The encoder drops frames while the participant's encryption state is unknown. Upstream passes them through. Upstream 2.22 already drops them on decode.
-
Screen share scalability mode (
src/room/participant/LocalParticipant.ts)A caller-supplied
scalabilityModeis kept for screen shares. Upstream forcesL1T3andcontentHint = 'motion'.L3T3_KEYis only defaulted for cameras, and SVC layers are only sent to the server when an encoding has a scalability mode. -
Encrypted backup codec publishing (
src/room/participant/LocalParticipant.ts,src/e2ee/E2eeManager.ts)Backup codecs are advertised and published while E2EE is on. The simulcast sender emits
LocalSenderCreatedwith its codec and track id so it gets its own transform. A failed backup codec publish stops the cloned track and is logged instead of thrown. -
Publisher codec preferences (
src/room/RTCEngine.ts)setCodecPreferences()is applied to publisher transceivers so the browser's SDP follows the selected primary or backup codec. H.264 profiles rank Baseline42001ffirst, then Constrained Baseline42e01f, then everything else. Main, High and Constrained High rank last on purpose.livekit-server registers H.264 High
640032on the publisher peer connection but filters it off the subscriber peer connection, and itsCodecParametersFuzzySearchfalls back to a mime-only match. A High publication therefore reaches subscribers under their42e01fpayload type and decodes to nothing on a Constrained-Baseline-only decoder such as Firefox's OpenH264 GMP.42001fis the one profile Chromium's accelerated encoder factory advertises that such a decoder can still handle, because Chromium's VAAPI encoder and OpenH264 both write a Constrained Baseline SPS forH264PROFILE_BASELINE.The trade is that livekit-server does not register
42001feither, so Windows and macOS negotiate42e01f, which Chromium's accelerated encoder factory does not advertise there (kPlatformH264CbpEncodingis off by default on Windows, andIsH264ConstrainedBaselineProfileAvailableForAcceleratedEncoderreturns false on Apple). Those publishers fall back to software H.264, which is what an unpatched browser does anyway. Linux, ChromeOS and Android keep hardware encoding. -
Media publishing defaults (
src/room/defaults.ts,src/room/utils.ts,src/room/track/options.ts)Codec fallback follows actual sender capabilities in the order H.264, VP9, VP8, AV1, HEVC through
supportsVideoCodec()andselectPreferredVideoCodec(). HEVC is never reported on Firefox. Advanced codecs get an H.264 backup simulcast, dynacast is on, DTX is off, degradation preference ismaintain-resolution, and screen shares default to theoriginalpreset at 20 Mbps and 60 fps.musicHighQualityStereois 510 kbps. AV1 and HEVC come last because both are opt-in in Fluxer, so a fallback insidepublishTrackmust not land on a codec the user did not enable. -
High-fidelity Opus SDP munging (
src/room/PCTransport.ts,src/room/participant/LocalParticipant.ts)Local offers and remote answers force Opus RED and FEC, 10 ms packet time, no DTX and a 510 kbps maximum average bitrate.
stereo=1andsprop-stereo=1are only added for publications whoseTrackBitrateInfo.stereois set and for the subscriber mids the server advertised as stereo. Audio bitrates are registered on every browser, not only Firefox. The initial offer sent with the join request is not munged, as upstream does not munge it either. -
Remote audio volume held at zero (
src/room/track/RemoteAudioTrack.ts)attach(),connectWebAudio()andgetVolume()testelementVolume !== undefined, so a track held at0stays silent after re-attach. Remote gains above1.0rely on the Web AudiogainNodebranch, sowebAudioMixmust stay on. -
Processor teardown before source stop (
src/room/track/LocalTrack.ts)stop()detaches and destroys the processor beforesuper.stop()ends the source track. -
Transactional source and processor swaps (
src/room/track/LocalTrack.ts,LocalVideoTrack.ts,LocalAudioTrack.ts)setMediaStreamTrack()takesSetMediaStreamTrackOptions(force,deferEndedListener,preservePreviousTrack,isUnmuting) and restores the previous source, constraints,enabledstate, listeners, processor and sender when a swap fails.stageTrackReplacement()andcommitStagedTrackReplacement()give a two-phase swap.runWithTrackChangeLock()andstopProcessorIfCurrent()are public for the app. Secondary simulcast senders get the processed track, and processor install and teardown roll back every sender. Upstream'sonSenderTrackSwapped()hook runs after a successfulreplaceTrack,stageTrackReplacement,setProcessor,stopProcessorandstopProcessorIfCurrent. -
Track bitrate entries are replaced (
src/room/PCTransport.ts)setTrackCodecBitrate()replaces the entry for the same cid or transceiver instead of appending, becausetrackBitratesis never cleared. Start bitrates for every video codec are upstream since 2.22 throughapplyVideoStartBitrate(). -
Subscriber video decoder exclusions (
src/options.ts,src/room/RTCEngine.ts,src/room/PCTransportManager.ts,src/room/PCTransport.ts)RoomOptions.subscriberVideoCodecExclusionsremoves codecs from receive transceivers withsetCodecPreferences()before every offer and answer, including the initial offer sent with the join request. Firefox and Safari are skipped. -
ICE diagnostics (
src/room/PCTransportManager.ts)ICE candidate errors are logged once per transport, code, URL and address until the next ICE restart. The transport connect timeout names each transport's connection, ICE and signalling state.
-
Screen share capture options (
src/room/track/options.ts,src/room/track/utils.ts)ScreenShareCaptureOptionshaswindowAudio,monitorTypeSurfacesandrestrictOwnAudio.restrictOwnAudioandsuppressLocalAudioPlaybackare passed as audio constraints. -
Stats and monitors (
src/room/stats.ts,src/room/track/Track.ts,LocalVideoTrack.ts,RemoteVideoTrack.ts,RemoteTrack.ts,LocalAudioTrack.ts)Sender stats have
encoderImplementationandpowerEfficientEncoder, receiver stats havepowerEfficientDecoder.runMonitor()skips a stats poll while the previous one is still running. Adaptive stream tracks that are not visible skip receiver stats.setPublishingLayersForSenderwritesmaxFrameratewhere upstream writes a misspeltmaxFrameRate, andvideoLayersFromEncodingshandles an SVC codec without a scalability mode. -
Smaller fixes (
src/api/SignalClient.ts,src/api/utils.ts,src/room/Room.ts,src/room/timers.ts,src/room/utils.ts,src/e2ee/worker/e2ee.worker.ts)The abort path passes its reason to
SignalClient.close()as the reason instead ofupdateState.handleOnCloseis public sosimulateScenarioneeds no suppression. Queue bypass checks no longer log every request. Abort reasons that are numbers, booleans, bigints or symbols become strings.Roomconnect rejections are alwaysErrorinstances.CriticalTimersbinds the native timers when the module loads.Futurerejects when its executor throws. The e2ee worker listens withaddEventListener, and the outgoing data stream sink no longer logs aborts toconsole.
Removed upstream code
connectionHelper, createLocalScreenTracks, getStereoAudioStreamTrack, isLocalPub, the frame metadata worker, room/token-source/test-tokens.ts, utils/subscribeToEvents.ts, and the DataTrackPacket, data track packet extension and LocalTrackRecorder exports. createV0RtcUrl, truncateBytes, videoQualityForRid and STOP_REFETCH_DELAY_MS are no longer exported.
Updating from upstream
- Read the upstream changelog for the target version.
- Run the source transforms on the old tag and on the new tag. Keep the old result as the merge base and the new result as the other side.
- Strip blank lines from the base, this tree and the new tree, run
git merge-file --diff3 --diff-algorithm=histogramper file, and put blank lines back from the new tree. Blank-line drift between the trees otherwise turns into conflicts. - Resolve conflicts with the modification list above. Where upstream rewrote a function that carries a Fluxer change, start from the upstream function and reapply the change.
- Diff the result against the transformed new tag after compiling both to JavaScript without types. Every hunk should belong to a modification listed here.
- Run
tsgo --noEmitunder the app tsconfig,biome check,knipandpnpm --filter livekit-client test, then update the version inpackage.jsonand this file.