Skip to content

fix: Enable E2EE for VP9 codec - #953

Merged
cloudwebrtc merged 3 commits into
livekit:mainfrom
Linquas:enable-e2ee-for-svc-codec
Jan 7, 2026
Merged

cloudwebrtc merged 3 commits into
livekit:mainfrom
Linquas:enable-e2ee-for-svc-codec

Conversation

@Linquas

@Linquas Linquas commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

Problem

While using the Flutter SDK with JS SDK and with E2EE enabled, the video from Web can not be decrypted by the Flutter app.

Summary

Enables end-to-end encryption (E2EE) for VP9 codec. Previously, E2EE was skipped for all SVC codecs. This aligns the Flutter SDK with the JS SDK, where E2EE works with VP9 codec.

Changes

Removed isSVCCodec() and use isAV1Codec() to enable E2EE setup for VP9 codec in LocalTrackPublishedEvent handler
Removed isSVCCodec() and use isAV1Codec() to enable E2EE setup for VP9 codec in TrackSubscribedEvent handler

Is there any specific reason why E2EE is disabled for SVC codecs?
I tried VP9 with this patch and the VP9 video from Web can be decrypted by Flutter app.

@CLAassistant

CLAassistant commented Jan 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@cloudwebrtc cloudwebrtc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For reliability, please enable VP9 only in this PR since AV1 is marked as not yet supported in the JS SDK.
https://github.com/livekit/client-sdk-js/blob/main/src/e2ee/worker/FrameCryptor.ts#L669-L671

@Linquas Linquas changed the title fix: Enable E2EE for SVC codec fix: Enable E2EE for VP9 codec Jan 7, 2026
@Linquas

Linquas commented Jan 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the input. I have updated the PR to enable E2EE for VP9 only. Please check again.

@Linquas
Linquas requested a review from cloudwebrtc January 7, 2026 07:09
Comment thread lib/src/e2ee/e2ee_manager.dart Outdated
..on<TrackSubscribedEvent>((event) async {
final codec = event.publication.mimeType.split('/')[1];
if (event.publication.encryptionType == EncryptionType.kNone || isSVCCodec(codec)) {
if (event.publication.encryptionType == EncryptionType.kNone) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (event.publication.encryptionType == EncryptionType.kNone) {
if (event.publication.encryptionType == EncryptionType.kNone || codec.toLowerCase() == 'av1') {

Comment thread lib/src/e2ee/e2ee_manager.dart Outdated
..on<LocalTrackPublishedEvent>((event) async {
if (event.publication.encryptionType == EncryptionType.kNone ||
isSVCCodec(event.publication.track?.codec ?? '')) {
if (event.publication.encryptionType == EncryptionType.kNone) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (event.publication.encryptionType == EncryptionType.kNone) {
if (event.publication.encryptionType == EncryptionType.kNone ||
event.publication.track?.codec?.toLowerCase() == 'av1') {

@cloudwebrtc
cloudwebrtc merged commit b835995 into livekit:main Jan 7, 2026
13 checks passed
rokk4 added a commit to rokk4/client-sdk-flutter that referenced this pull request Jul 28, 2026
Brings 18 upstream commits, notably 42f6b14 "fix: allow selectAudioOutput on
Android" (livekit#1121) — without it Hardware.selectAudioOutput early-returned with
"only supported on Desktop", so the in-call output picker was inert on Android.
selectAudioOutput now refuses only on Web/iOS.

Conflict resolution:

- lib/src/options.dart — both sides added a RoomOptions field in the same place.
  Kept both: our pendingTrackQueueMaxSize and upstream's networkOptions.
- lib/src/core/room.dart — kept our prefixed pending_queue.PendingTrackQueue
  construction (metadataTimeout, not upstream's ttl) and added upstream's
  RpcClientManager/RpcServerManager init from the RPC v2 work (livekit#1087).

Dropped the VP9 patch (cffd8a7 "vp9 experiment"):

  web/e2ee.frame_cryptor.dart is restored to upstream. That patch made
  getUnencryptedBytes return 0 for VP9 — encrypt the whole frame, header
  included — while every other peer (native libwebrtc, the JS SDK) uses the
  standard key=10 / delta=3 prefix. Since the same function runs on both
  encrypt and decrypt, the mismatch means a VP9 frame from mobile cannot be
  decrypted on web and vice versa, and with failureTolerance: -1 it fails
  silently rather than surfacing an error.

  It is also redundant: upstream fixed VP9 E2EE properly in b835995 (livekit#953),
  which narrowed the frame-cryptor skip from isSVCCodec to isAV1Codec, and
  that fix is already in this branch. Our patch predates it by a month and
  targeted the same symptom via the wrong mechanism.

Verified: flutter analyze clean, all 70 tests in test/core pass, and the three
patches the fork exists for are intact (pending_track_queue rejoin hardening,
pendingTrackQueueMaxSize, iOS podspec 16.0).
rokk4 added a commit to rokk4/client-sdk-flutter that referenced this pull request Jul 29, 2026
Reverts the drop made during the upstream merge. getUnencryptedBytes
returns 0 for VP9 so the web worker matches the native FrameCryptor,
which encrypts the whole frame.

Without it web and mobile disagree on where the ciphertext starts and
cannot decrypt each other's VP9 video. Upstream livekit#953 enables VP9 E2EE
but does not align the unencrypted-byte boundary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants