From ee6f64c2573f7871848ed03194b8d29025eed07b Mon Sep 17 00:00:00 2001 From: ninglinLiu Date: Fri, 30 Oct 2026 18:39:41 +0800 Subject: [PATCH 1/2] feat(rtc-node): expose RemoteTrackPublication.setVideoQuality (Fixes #670) Mirror setSubscribed by dispatching SetRemoteTrackPublicationQuality through FfiClient so server-side subscribers can request a simulcast layer without reaching into internal FFI handles. --- .../feat-remote-track-set-video-quality.md | 5 ++ packages/livekit-rtc/src/index.ts | 1 + .../livekit-rtc/src/track_publication.test.ts | 52 +++++++++++++++++++ packages/livekit-rtc/src/track_publication.ts | 28 +++++++++- 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 .changeset/feat-remote-track-set-video-quality.md create mode 100644 packages/livekit-rtc/src/track_publication.test.ts diff --git a/.changeset/feat-remote-track-set-video-quality.md b/.changeset/feat-remote-track-set-video-quality.md new file mode 100644 index 00000000..3c86deec --- /dev/null +++ b/.changeset/feat-remote-track-set-video-quality.md @@ -0,0 +1,5 @@ +--- +'@livekit/rtc-node': minor +--- + +Expose RemoteTrackPublication.setVideoQuality for requesting a simulcast subscribed quality diff --git a/packages/livekit-rtc/src/index.ts b/packages/livekit-rtc/src/index.ts index 4a3020b2..8a861f97 100644 --- a/packages/livekit-rtc/src/index.ts +++ b/packages/livekit-rtc/src/index.ts @@ -68,6 +68,7 @@ export { LocalTrackPublication, RemoteTrackPublication, TrackPublication, + VideoQuality, } from './track_publication.js'; export type { Transcription, TranscriptionSegment } from './transcription.js'; export type { ChatMessage } from './types.js'; diff --git a/packages/livekit-rtc/src/track_publication.test.ts b/packages/livekit-rtc/src/track_publication.test.ts new file mode 100644 index 00000000..c453a9ac --- /dev/null +++ b/packages/livekit-rtc/src/track_publication.test.ts @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2026 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { + SetRemoteTrackPublicationQualityRequest, + VideoQuality, +} from '@livekit/rtc-ffi-bindings'; +import { describe, expect, it, vi } from 'vitest'; +import { FfiClient } from './ffi_client.js'; +import { RemoteTrackPublication } from './track_publication.js'; + +// Synthetic FFI handles must not schedule a native drop (invalid handle) at GC. +vi.mock('@livekit/rtc-ffi-bindings', async () => { + const actual = await vi.importActual( + '@livekit/rtc-ffi-bindings', + ); + class FakeFfiHandle { + private _handle: bigint; + constructor(handle: bigint) { + this._handle = handle; + } + dispose(): void {} + get handle(): bigint { + return this._handle; + } + } + return { ...actual, FfiHandle: FakeFfiHandle }; +}); + +describe('RemoteTrackPublication.setVideoQuality', () => { + it('dispatches SetRemoteTrackPublicationQuality with the publication handle', () => { + const handleId = BigInt(42); + const publication = new RemoteTrackPublication({ + handle: { id: handleId }, + info: {}, + } as never); + + const requestSpy = vi.spyOn(FfiClient.instance, 'request').mockReturnValue({} as never); + try { + publication.setVideoQuality(VideoQuality.HIGH); + expect(requestSpy).toHaveBeenCalledTimes(1); + const arg = requestSpy.mock.calls[0]![0] as { + message: { case: string; value: SetRemoteTrackPublicationQualityRequest }; + }; + expect(arg.message.case).toBe('setRemoteTrackPublicationQuality'); + expect(arg.message.value.trackPublicationHandle).toBe(handleId); + expect(arg.message.value.quality).toBe(VideoQuality.HIGH); + } finally { + requestSpy.mockRestore(); + } + }); +}); diff --git a/packages/livekit-rtc/src/track_publication.ts b/packages/livekit-rtc/src/track_publication.ts index 75480688..6827342c 100644 --- a/packages/livekit-rtc/src/track_publication.ts +++ b/packages/livekit-rtc/src/track_publication.ts @@ -2,8 +2,15 @@ // // SPDX-License-Identifier: Apache-2.0 import type { EncryptionType } from '@livekit/rtc-ffi-bindings'; -import type { SetSubscribedResponse } from '@livekit/rtc-ffi-bindings'; -import { SetSubscribedRequest } from '@livekit/rtc-ffi-bindings'; +import type { + SetRemoteTrackPublicationQualityResponse, + SetSubscribedResponse, +} from '@livekit/rtc-ffi-bindings'; +import { + SetRemoteTrackPublicationQualityRequest, + SetSubscribedRequest, + VideoQuality, +} from '@livekit/rtc-ffi-bindings'; import type { OwnedTrackPublication, TrackKind, @@ -121,4 +128,21 @@ export class RemoteTrackPublication extends TrackPublication { message: { case: 'setSubscribed', value: req }, }); } + + /** + * For simulcast remote video publications, request a specific subscribed quality. + * Dispatches through the same FFI client path as {@link setSubscribed}. + */ + setVideoQuality(quality: VideoQuality) { + const req = new SetRemoteTrackPublicationQualityRequest({ + trackPublicationHandle: this.ffiHandle.handle, + quality, + }); + + FfiClient.instance.request({ + message: { case: 'setRemoteTrackPublicationQuality', value: req }, + }); + } } + +export { VideoQuality }; From e3952bce2188c966f1f7511a9466349a722e3bb3 Mon Sep 17 00:00:00 2001 From: ninglinLiu Date: Fri, 30 Oct 2026 19:17:01 +0800 Subject: [PATCH 2/2] style(rtc-node): prettier-format setVideoQuality test imports --- packages/livekit-rtc/src/track_publication.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/livekit-rtc/src/track_publication.test.ts b/packages/livekit-rtc/src/track_publication.test.ts index c453a9ac..56590ed5 100644 --- a/packages/livekit-rtc/src/track_publication.test.ts +++ b/packages/livekit-rtc/src/track_publication.test.ts @@ -1,10 +1,7 @@ // SPDX-FileCopyrightText: 2026 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 -import { - SetRemoteTrackPublicationQualityRequest, - VideoQuality, -} from '@livekit/rtc-ffi-bindings'; +import { SetRemoteTrackPublicationQualityRequest, VideoQuality } from '@livekit/rtc-ffi-bindings'; import { describe, expect, it, vi } from 'vitest'; import { FfiClient } from './ffi_client.js'; import { RemoteTrackPublication } from './track_publication.js';