From c7b3fc026bd1bc83ecfb214eed84843eed84ab61 Mon Sep 17 00:00:00 2001 From: lukasIO Date: Wed, 3 May 2023 20:45:22 +0200 Subject: [PATCH] Add support for local priority in presets (#677) * wip * Add support for local priority in presets * remove default values on video presets * address comment * also set network priority * fix explicit type imports * fix tests * fix eslint * Create large-zebras-press.md --- .changeset/large-zebras-press.md | 5 ++++ jest.config.cjs | 2 +- package.json | 2 +- src/connectionHelper/ConnectionCheck.ts | 3 ++- src/index.ts | 14 +++++++---- src/room/PCTransport.ts | 12 ++++++--- src/room/RTCEngine.ts | 7 +++--- src/room/defaults.ts | 10 +++++--- src/room/participant/LocalParticipant.ts | 15 ++++++------ src/room/participant/RemoteParticipant.ts | 3 ++- src/room/participant/publishUtils.ts | 12 +++++---- src/room/track/LocalAudioTrack.ts | 3 ++- src/room/track/LocalVideoTrack.ts | 3 ++- src/room/track/RemoteAudioTrack.ts | 3 ++- src/room/track/RemoteVideoTrack.ts | 12 +++------ src/room/track/create.ts | 4 +-- src/room/track/options.ts | 30 +++++++++++++++++------ src/room/utils.ts | 5 ++++ tsconfig.eslint.json | 3 +-- tsconfig.json | 3 ++- yarn.lock | 8 +++--- 21 files changed, 100 insertions(+), 59 deletions(-) create mode 100644 .changeset/large-zebras-press.md diff --git a/.changeset/large-zebras-press.md b/.changeset/large-zebras-press.md new file mode 100644 index 0000000000..5167aba757 --- /dev/null +++ b/.changeset/large-zebras-press.md @@ -0,0 +1,5 @@ +--- +"livekit-client": patch +--- + +Add support for local priority in presets diff --git a/jest.config.cjs b/jest.config.cjs index 5419476854..12a5e59d5b 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -1,6 +1,6 @@ module.exports = { clearMocks: true, - modulePathIgnorePatterns: ['/dist/'], + modulePathIgnorePatterns: ['/dist/', '/example/'], preset: 'ts-jest', testEnvironment: 'node', }; diff --git a/package.json b/package.json index feb6ebdf3d..a0626071e3 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "ts-proto": "1.147.1", "typedoc": "0.24.6", "typedoc-plugin-no-inherit": "1.4.0", - "typescript": "4.9.5", + "typescript": "5.0.4", "vite": "4.3.4" }, "browserslist": [ diff --git a/src/connectionHelper/ConnectionCheck.ts b/src/connectionHelper/ConnectionCheck.ts index 29fe031736..5adc080883 100644 --- a/src/connectionHelper/ConnectionCheck.ts +++ b/src/connectionHelper/ConnectionCheck.ts @@ -1,6 +1,7 @@ import EventEmitter from 'events'; import type TypedEmitter from 'typed-emitter'; -import { CheckInfo, CheckStatus, Checker, InstantiableCheck } from './checks/Checker'; +import { CheckStatus, Checker } from './checks/Checker'; +import type { CheckInfo, InstantiableCheck } from './checks/Checker'; import { PublishAudioCheck } from './checks/publishAudio'; import { PublishVideoCheck } from './checks/publishVideo'; import { ReconnectCheck } from './checks/reconnect'; diff --git a/src/index.ts b/src/index.ts index ec2d698c0e..360a39e383 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,11 +14,11 @@ import LocalVideoTrack from './room/track/LocalVideoTrack'; import RemoteAudioTrack from './room/track/RemoteAudioTrack'; import RemoteTrack from './room/track/RemoteTrack'; import RemoteTrackPublication from './room/track/RemoteTrackPublication'; -import RemoteVideoTrack, { type ElementInfo } from './room/track/RemoteVideoTrack'; +import type { ElementInfo } from './room/track/RemoteVideoTrack'; +import RemoteVideoTrack from './room/track/RemoteVideoTrack'; import { TrackPublication } from './room/track/TrackPublication'; import type { LiveKitReactNativeInfo } from './room/types'; import { - type AudioAnalyserOptions, createAudioAnalyser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, @@ -27,6 +27,7 @@ import { supportsAdaptiveStream, supportsDynacast, } from './room/utils'; +import type { AudioAnalyserOptions } from './room/utils'; export * from './options'; export * from './room/errors'; @@ -48,7 +49,6 @@ export { supportsDynacast, supportsAV1, createAudioAnalyser, - AudioAnalyserOptions, LogLevel, Room, ConnectionState, @@ -66,12 +66,16 @@ export { RemoteAudioTrack, RemoteVideoTrack, RemoteTrackPublication, - ParticipantTrackPermission, TrackPublication, VideoQuality, ConnectionQuality, - ElementInfo, DefaultReconnectPolicy, CriticalTimers, +}; + +export type { + ElementInfo, + ParticipantTrackPermission, + AudioAnalyserOptions, LiveKitReactNativeInfo, }; diff --git a/src/room/PCTransport.ts b/src/room/PCTransport.ts index 304bd98421..9ddb2090d1 100644 --- a/src/room/PCTransport.ts +++ b/src/room/PCTransport.ts @@ -1,9 +1,10 @@ import EventEmitter from 'events'; -import { MediaDescription, parse, write } from 'sdp-transform'; +import { parse, write } from 'sdp-transform'; +import type { MediaDescription } from 'sdp-transform'; import { debounce } from 'ts-debounce'; import log from '../logger'; import { NegotiationError } from './errors'; -import { ddExtensionURI, isSVCCodec } from './utils'; +import { ddExtensionURI, isChromiumBased, isSVCCodec } from './utils'; /** @internal */ interface TrackBitrateInfo { @@ -35,9 +36,12 @@ export default class PCTransport extends EventEmitter { onOffer?: (offer: RTCSessionDescriptionInit) => void; - constructor(config?: RTCConfiguration) { + constructor(config?: RTCConfiguration, mediaConstraints: Record = {}) { super(); - this.pc = new RTCPeerConnection(config); + this.pc = isChromiumBased() + ? // @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor + new RTCPeerConnection(config, mediaConstraints) + : new RTCPeerConnection(config); } get isICEConnected(): boolean { diff --git a/src/room/RTCEngine.ts b/src/room/RTCEngine.ts index 56e9dc8e28..a14a8cc2ef 100644 --- a/src/room/RTCEngine.ts +++ b/src/room/RTCEngine.ts @@ -1,6 +1,7 @@ import { EventEmitter } from 'events'; import type TypedEventEmitter from 'typed-emitter'; -import { SignalClient, SignalOptions } from '../api/SignalClient'; +import { SignalClient } from '../api/SignalClient'; +import type { SignalOptions } from '../api/SignalClient'; import log from '../logger'; import type { InternalRoomOptions } from '../options'; import { @@ -307,8 +308,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit this.participantSid = joinResponse.participant?.sid; const rtcConfig = this.makeRTCConfiguration(joinResponse); - - this.publisher = new PCTransport(rtcConfig); + const googConstraints = { optional: [{ googDscp: true }] }; + this.publisher = new PCTransport(rtcConfig, googConstraints); this.subscriber = new PCTransport(rtcConfig); this.emit(EngineEvent.TransportsCreated, this.publisher, this.subscriber); diff --git a/src/room/defaults.ts b/src/room/defaults.ts index 059fda033e..6fb893f54e 100644 --- a/src/room/defaults.ts +++ b/src/room/defaults.ts @@ -1,16 +1,18 @@ import type { InternalRoomConnectOptions, InternalRoomOptions } from '../options'; import DefaultReconnectPolicy from './DefaultReconnectPolicy'; -import { +import { AudioPresets, ScreenSharePresets, VideoPresets } from './track/options'; +import type { AudioCaptureOptions, - AudioPresets, - ScreenSharePresets, TrackPublishDefaults, VideoCaptureOptions, - VideoPresets, } from './track/options'; export const publishDefaults: TrackPublishDefaults = { + /** + * @deprecated + */ audioBitrate: AudioPresets.music.maxBitrate, + audioPreset: AudioPresets.music, dtx: true, red: true, forceStereo: false, diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index d0f82fea02..b8bbc01765 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -18,22 +18,21 @@ import LocalTrack from '../track/LocalTrack'; import LocalTrackPublication from '../track/LocalTrackPublication'; import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTrack'; import { Track } from '../track/Track'; -import { +import { ScreenSharePresets, isBackupCodec, isCodecEqual } from '../track/options'; +import type { AudioCaptureOptions, BackupVideoCodec, CreateLocalTracksOptions, ScreenShareCaptureOptions, - ScreenSharePresets, TrackPublishOptions, VideoCaptureOptions, - isBackupCodec, - isCodecEqual, } from '../track/options'; import { constraintsForOptions, mergeDefaultOptions } from '../track/utils'; import type { DataPublishOptions } from '../types'; import { Future, isFireFox, isSVCCodec, isSafari, isWeb, supportsAV1, supportsVP9 } from '../utils'; import Participant from './Participant'; -import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission'; +import { trackPermissionToProto } from './ParticipantTrackPermission'; +import type { ParticipantTrackPermission } from './ParticipantTrackPermission'; import RemoteParticipant from './RemoteParticipant'; import { computeTrackBackupEncodings, @@ -651,10 +650,12 @@ export default class LocalParticipant extends Participant { opts, ); req.layers = videoLayersFromEncodings(req.width, req.height, simEncodings ?? encodings); - } else if (track.kind === Track.Kind.Audio && opts.audioBitrate) { + } else if (track.kind === Track.Kind.Audio) { encodings = [ { - maxBitrate: opts.audioBitrate, + maxBitrate: opts.audioPreset?.maxBitrate ?? opts.audioBitrate, + priority: opts.audioPreset?.priority ?? 'high', + networkPriority: opts.audioPreset?.priority ?? 'high', }, ]; } diff --git a/src/room/participant/RemoteParticipant.ts b/src/room/participant/RemoteParticipant.ts index 1f8f34661a..be668c2367 100644 --- a/src/room/participant/RemoteParticipant.ts +++ b/src/room/participant/RemoteParticipant.ts @@ -11,7 +11,8 @@ import { Track } from '../track/Track'; import type { TrackPublication } from '../track/TrackPublication'; import type { AudioOutputOptions } from '../track/options'; import type { AdaptiveStreamSettings } from '../track/types'; -import Participant, { ParticipantEventCallbacks } from './Participant'; +import Participant from './Participant'; +import type { ParticipantEventCallbacks } from './Participant'; export default class RemoteParticipant extends Participant { audioTracks: Map; diff --git a/src/room/participant/publishUtils.ts b/src/room/participant/publishUtils.ts index d67830b8a8..1415954acc 100644 --- a/src/room/participant/publishUtils.ts +++ b/src/room/participant/publishUtils.ts @@ -3,15 +3,12 @@ import { TrackInvalidError } from '../errors'; import LocalAudioTrack from '../track/LocalAudioTrack'; import LocalVideoTrack from '../track/LocalVideoTrack'; import { Track } from '../track/Track'; -import { +import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options'; +import type { BackupVideoCodec, - ScreenSharePresets, TrackPublishOptions, VideoCodec, VideoEncoding, - VideoPreset, - VideoPresets, - VideoPresets43, } from '../track/options'; import { isSVCCodec } from '../utils'; @@ -61,6 +58,7 @@ export const computeDefaultScreenShareSimulcastPresets = (fromPreset: VideoPrese ), ), t.fps, + fromPreset.encoding.priority, ), ); }; @@ -317,6 +315,10 @@ function encodingsFromPresets( if (preset.encoding.maxFramerate) { encoding.maxFramerate = preset.encoding.maxFramerate; } + if (preset.encoding.priority) { + encoding.priority = preset.encoding.priority; + encoding.networkPriority = preset.encoding.priority; + } encodings.push(encoding); }); return encodings; diff --git a/src/room/track/LocalAudioTrack.ts b/src/room/track/LocalAudioTrack.ts index edd3b8e0c0..6fe696c1ea 100644 --- a/src/room/track/LocalAudioTrack.ts +++ b/src/room/track/LocalAudioTrack.ts @@ -1,6 +1,7 @@ import log from '../../logger'; import { TrackEvent } from '../events'; -import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats'; +import { computeBitrate, monitorFrequency } from '../stats'; +import type { AudioSenderStats } from '../stats'; import { isWeb } from '../utils'; import LocalTrack from './LocalTrack'; import { Track } from './Track'; diff --git a/src/room/track/LocalVideoTrack.ts b/src/room/track/LocalVideoTrack.ts index 3ce28a396b..4ebd035880 100644 --- a/src/room/track/LocalVideoTrack.ts +++ b/src/room/track/LocalVideoTrack.ts @@ -2,7 +2,8 @@ import type { SignalClient } from '../../api/SignalClient'; import log from '../../logger'; import { VideoLayer, VideoQuality } from '../../proto/livekit_models'; import type { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc'; -import { VideoSenderStats, computeBitrate, monitorFrequency } from '../stats'; +import { computeBitrate, monitorFrequency } from '../stats'; +import type { VideoSenderStats } from '../stats'; import { Mutex, isFireFox, isMobile, isWeb } from '../utils'; import LocalTrack from './LocalTrack'; import { Track } from './Track'; diff --git a/src/room/track/RemoteAudioTrack.ts b/src/room/track/RemoteAudioTrack.ts index 0a003c15cd..8016790645 100644 --- a/src/room/track/RemoteAudioTrack.ts +++ b/src/room/track/RemoteAudioTrack.ts @@ -1,6 +1,7 @@ import log from '../../logger'; import { TrackEvent } from '../events'; -import { AudioReceiverStats, computeBitrate } from '../stats'; +import { computeBitrate } from '../stats'; +import type { AudioReceiverStats } from '../stats'; import { supportsSetSinkId } from '../utils'; import RemoteTrack from './RemoteTrack'; import { Track } from './Track'; diff --git a/src/room/track/RemoteVideoTrack.ts b/src/room/track/RemoteVideoTrack.ts index d754a8d92c..a3e081db14 100644 --- a/src/room/track/RemoteVideoTrack.ts +++ b/src/room/track/RemoteVideoTrack.ts @@ -1,15 +1,11 @@ import { debounce } from 'ts-debounce'; import log from '../../logger'; import { TrackEvent } from '../events'; -import { VideoReceiverStats, computeBitrate } from '../stats'; +import { computeBitrate } from '../stats'; +import type { VideoReceiverStats } from '../stats'; import CriticalTimers from '../timers'; -import { - ObservableMediaElement, - getDevicePixelRatio, - getIntersectionObserver, - getResizeObserver, - isWeb, -} from '../utils'; +import { getDevicePixelRatio, getIntersectionObserver, getResizeObserver, isWeb } from '../utils'; +import type { ObservableMediaElement } from '../utils'; import RemoteTrack from './RemoteTrack'; import { Track, attachToElement, detachTrack } from './Track'; import type { AdaptiveStreamSettings } from './types'; diff --git a/src/room/track/create.ts b/src/room/track/create.ts index 4f5d99cbae..b352d8fd5c 100644 --- a/src/room/track/create.ts +++ b/src/room/track/create.ts @@ -6,12 +6,12 @@ import LocalAudioTrack from './LocalAudioTrack'; import type LocalTrack from './LocalTrack'; import LocalVideoTrack from './LocalVideoTrack'; import { Track } from './Track'; -import { +import { VideoPresets } from './options'; +import type { AudioCaptureOptions, CreateLocalTracksOptions, ScreenShareCaptureOptions, VideoCaptureOptions, - VideoPresets, } from './options'; import { constraintsForOptions, mergeDefaultOptions } from './utils'; diff --git a/src/room/track/options.ts b/src/room/track/options.ts index ed0db94324..167e2b8ae1 100644 --- a/src/room/track/options.ts +++ b/src/room/track/options.ts @@ -23,10 +23,17 @@ export interface TrackPublishDefaults { videoCodec?: VideoCodec; /** - * max audio bitrate, defaults to [[AudioPresets.speech]] + * max audio bitrate, defaults to [[AudioPresets.music]] + * @deprecated use `audioPreset` instead */ audioBitrate?: number; + /** + * which audio preset should be used for publishing (audio) tracks + * defaults to [[AudioPresets.music]] + */ + audioPreset?: AudioPreset; + /** * dtx (Discontinuous Transmission of audio), enabled by default for mono tracks. */ @@ -215,6 +222,7 @@ export interface VideoResolution { export interface VideoEncoding { maxBitrate: number; maxFramerate?: number; + priority?: RTCPriorityType; } export class VideoPreset { @@ -224,12 +232,19 @@ export class VideoPreset { height: number; - constructor(width: number, height: number, maxBitrate: number, maxFramerate?: number) { + constructor( + width: number, + height: number, + maxBitrate: number, + maxFramerate?: number, + priority?: RTCPriorityType, + ) { this.width = width; this.height = height; this.encoding = { maxBitrate, maxFramerate, + priority, }; } @@ -245,6 +260,7 @@ export class VideoPreset { export interface AudioPreset { maxBitrate: number; + priority?: RTCPriorityType; } const codecs = ['vp8', 'h264', 'vp9', 'av1'] as const; @@ -322,9 +338,9 @@ export const VideoPresets43 = { } as const; export const ScreenSharePresets = { - h360fps3: new VideoPreset(640, 360, 200_000, 3), - h720fps5: new VideoPreset(1280, 720, 400_000, 5), - h720fps15: new VideoPreset(1280, 720, 1_000_000, 15), - h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15), - h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30), + h360fps3: new VideoPreset(640, 360, 200_000, 3, 'medium'), + h720fps5: new VideoPreset(1280, 720, 400_000, 5, 'medium'), + h720fps15: new VideoPreset(1280, 720, 1_000_000, 15, 'medium'), + h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15, 'medium'), + h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30, 'medium'), } as const; diff --git a/src/room/utils.ts b/src/room/utils.ts index e7849711df..4294b0bca7 100644 --- a/src/room/utils.ts +++ b/src/room/utils.ts @@ -119,6 +119,11 @@ export function isFireFox(): boolean { return navigator.userAgent.indexOf('Firefox') !== -1; } +export function isChromiumBased(): boolean { + if (!isWeb()) return false; + return navigator.userAgent.indexOf('Chrom') !== -1; +} + export function isSafari(): boolean { if (!isWeb()) return false; return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index c2ea02a72f..f8d7764481 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -4,11 +4,10 @@ "src/**/*.ts", "src/**/*.js", "example/**/*.ts", - "example/**/*.js", ".eslintrc.js", "jest.config.js", "rollup.config.js", "rollup.config.dev.js" ], - "exclude": [] + "exclude": ["dist/**"] } diff --git a/tsconfig.json b/tsconfig.json index e8119f5b03..57480562bb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "moduleResolution": "node", "resolveJsonModule": true, - "importsNotUsedAsValues": "error" + "importsNotUsedAsValues": "error", + "ignoreDeprecations": "5.0" }, "exclude": ["dist", "**/*.test.ts"], "include": ["src/**/*"], diff --git a/yarn.lock b/yarn.lock index 45d3358a71..1d2f6cd64e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7784,10 +7784,10 @@ typedoc@0.24.6: minimatch "^9.0.0" shiki "^0.14.1" -typescript@4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== typescript@next: version "4.9.0-dev.20221026"