livekit-client 2.22.3 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +71 -8
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +1746 -547
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.fm.worker.js +1 -1
- package/dist/livekit-client.fm.worker.js.map +1 -1
- package/dist/livekit-client.fm.worker.mjs +2 -1
- package/dist/livekit-client.fm.worker.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +4 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/api/WebSocketStream.d.ts +1 -0
- package/dist/src/api/WebSocketStream.d.ts.map +1 -1
- package/dist/src/api/utils.d.ts +1 -1
- package/dist/src/api/utils.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/naluUtils.d.ts +12 -1
- package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/options.d.ts +14 -0
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/room/DefaultReconnectPolicy.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +100 -7
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +16 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +54 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-channel/DataChannelManager.d.ts +9 -8
- package/dist/src/room/data-channel/DataChannelManager.d.ts.map +1 -1
- package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts +25 -12
- package/dist/src/room/data-channel/FlowControlledDataChannel.d.ts.map +1 -1
- package/dist/src/room/data-channel/LossyDataChannel.d.ts.map +1 -1
- package/dist/src/room/data-channel/ReliableDataChannel.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +18 -0
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +3 -2
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +4 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/rpc/client/RpcClientManager.d.ts.map +1 -1
- package/dist/src/room/rpc/utils.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +24 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +18 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +9 -0
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteAudioTrack.d.ts +6 -0
- package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/processor/types.d.ts +11 -0
- package/dist/src/room/track/processor/types.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +13 -0
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +60 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/compressionStreamStubs.d.ts +12 -0
- package/dist/src/test/compressionStreamStubs.d.ts.map +1 -0
- package/dist/ts4.2/api/SignalClient.d.ts +187 -185
- package/dist/ts4.2/api/SignalClientStateMachine.d.ts +85 -85
- package/dist/ts4.2/api/WebSocketStream.d.ts +34 -33
- package/dist/ts4.2/api/utils.d.ts +9 -9
- package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +30 -30
- package/dist/ts4.2/connectionHelper/checks/Checker.d.ts +62 -62
- package/dist/ts4.2/connectionHelper/checks/cloudRegion.d.ts +18 -18
- package/dist/ts4.2/connectionHelper/checks/connectionProtocol.d.ts +20 -20
- package/dist/ts4.2/connectionHelper/checks/publishAudio.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/publishVideo.d.ts +7 -7
- package/dist/ts4.2/connectionHelper/checks/reconnect.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/turn.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/webrtc.d.ts +6 -6
- package/dist/ts4.2/connectionHelper/checks/websocket.d.ts +6 -6
- package/dist/ts4.2/e2ee/E2eeManager.d.ts +111 -111
- package/dist/ts4.2/e2ee/KeyProvider.d.ts +54 -54
- package/dist/ts4.2/e2ee/constants.d.ts +21 -21
- package/dist/ts4.2/e2ee/errors.d.ts +12 -12
- package/dist/ts4.2/e2ee/events.d.ts +39 -39
- package/dist/ts4.2/e2ee/index.d.ts +6 -6
- package/dist/ts4.2/e2ee/types.d.ts +231 -231
- package/dist/ts4.2/e2ee/utils.d.ts +32 -32
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +16 -16
- package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -21
- package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +209 -209
- package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +81 -84
- package/dist/ts4.2/e2ee/worker/e2ee.worker.d.ts +2 -2
- package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +39 -28
- package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +23 -23
- package/dist/ts4.2/frameMetadata/FrameMetadataManager.d.ts +53 -53
- package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +33 -33
- package/dist/ts4.2/frameMetadata/types.d.ts +62 -62
- package/dist/ts4.2/frameMetadata/utils.d.ts +9 -9
- package/dist/ts4.2/frameMetadata/worker/frameMetadata.worker.d.ts +2 -2
- package/dist/ts4.2/index.d.ts +76 -76
- package/dist/ts4.2/logger.d.ts +66 -66
- package/dist/ts4.2/options.d.ts +163 -149
- package/dist/ts4.2/room/BackOffStrategy.d.ts +27 -27
- package/dist/ts4.2/room/DefaultReconnectPolicy.d.ts +8 -8
- package/dist/ts4.2/room/DeviceManager.d.ts +12 -12
- package/dist/ts4.2/room/PCTransport.d.ts +268 -175
- package/dist/ts4.2/room/PCTransportManager.d.ts +77 -77
- package/dist/ts4.2/room/RTCEngine.d.ts +277 -262
- package/dist/ts4.2/room/ReconnectPolicy.d.ts +24 -24
- package/dist/ts4.2/room/RegionUrlProvider.d.ts +36 -36
- package/dist/ts4.2/room/Room.d.ts +407 -365
- package/dist/ts4.2/room/attribute-typings.d.ts +35 -35
- package/dist/ts4.2/room/data-channel/DataChannelManager.d.ts +63 -62
- package/dist/ts4.2/room/data-channel/FlowControlledDataChannel.d.ts +107 -95
- package/dist/ts4.2/room/data-channel/LossyDataChannel.d.ts +41 -41
- package/dist/ts4.2/room/data-channel/ReliableDataChannel.d.ts +57 -57
- package/dist/ts4.2/room/data-channel/types.d.ts +12 -12
- package/dist/ts4.2/room/data-stream/compression.d.ts +49 -49
- package/dist/ts4.2/room/data-stream/constants.d.ts +18 -18
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +29 -29
- package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +80 -80
- package/dist/ts4.2/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts +73 -73
- package/dist/ts4.2/room/data-stream/outgoing/StreamWriter.d.ts +16 -16
- package/dist/ts4.2/room/data-stream/outgoing/header-utils.d.ts +18 -18
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +79 -80
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +50 -50
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +59 -59
- package/dist/ts4.2/room/data-track/frame.d.ts +22 -22
- package/dist/ts4.2/room/data-track/handle.d.ts +27 -27
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +124 -124
- package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -24
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +41 -41
- package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -20
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +126 -126
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +74 -74
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +23 -23
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +48 -48
- package/dist/ts4.2/room/data-track/packet/constants.d.ts +20 -20
- package/dist/ts4.2/room/data-track/packet/errors.d.ts +40 -40
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +69 -69
- package/dist/ts4.2/room/data-track/packet/index.d.ts +98 -98
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +13 -13
- package/dist/ts4.2/room/data-track/packetizer.d.ts +43 -43
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -23
- package/dist/ts4.2/room/data-track/types.d.ts +22 -22
- package/dist/ts4.2/room/data-track/utils.d.ts +60 -60
- package/dist/ts4.2/room/debounce.d.ts +11 -11
- package/dist/ts4.2/room/defaults.d.ts +9 -9
- package/dist/ts4.2/room/errors.d.ts +180 -162
- package/dist/ts4.2/room/events.d.ts +638 -637
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +291 -287
- package/dist/ts4.2/room/participant/Participant.d.ts +142 -144
- package/dist/ts4.2/room/participant/ParticipantTrackPermission.d.ts +26 -26
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +85 -86
- package/dist/ts4.2/room/participant/publishUtils.d.ts +53 -53
- package/dist/ts4.2/room/rpc/client/RpcClientManager.d.ts +43 -43
- package/dist/ts4.2/room/rpc/client/events.d.ts +8 -8
- package/dist/ts4.2/room/rpc/index.d.ts +7 -7
- package/dist/ts4.2/room/rpc/server/RpcServerManager.d.ts +44 -44
- package/dist/ts4.2/room/rpc/server/events.d.ts +8 -8
- package/dist/ts4.2/room/rpc/utils.d.ts +131 -131
- package/dist/ts4.2/room/stats.d.ts +71 -71
- package/dist/ts4.2/room/statsSummary.d.ts +13 -13
- package/dist/ts4.2/room/timers.d.ts +12 -12
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +82 -82
- package/dist/ts4.2/room/token-source/types.d.ts +70 -70
- package/dist/ts4.2/room/token-source/utils.d.ts +8 -8
- package/dist/ts4.2/room/track/FrameMetadataExtractor.d.ts +19 -19
- package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +62 -42
- package/dist/ts4.2/room/track/LocalTrack.d.ts +143 -125
- package/dist/ts4.2/room/track/LocalTrackPublication.d.ts +42 -42
- package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +109 -100
- package/dist/ts4.2/room/track/RemoteAudioTrack.d.ts +61 -55
- package/dist/ts4.2/room/track/RemoteTrack.d.ts +38 -38
- package/dist/ts4.2/room/track/RemoteTrackPublication.d.ts +72 -75
- package/dist/ts4.2/room/track/RemoteVideoTrack.d.ts +73 -76
- package/dist/ts4.2/room/track/Track.d.ts +156 -166
- package/dist/ts4.2/room/track/TrackPublication.d.ts +75 -81
- package/dist/ts4.2/room/track/create.d.ts +25 -25
- package/dist/ts4.2/room/track/facingMode.d.ts +41 -41
- package/dist/ts4.2/room/track/options.d.ts +403 -403
- package/dist/ts4.2/room/track/processor/types.d.ts +42 -31
- package/dist/ts4.2/room/track/record.d.ts +13 -13
- package/dist/ts4.2/room/track/types.d.ts +28 -28
- package/dist/ts4.2/room/track/utils.d.ts +60 -47
- package/dist/ts4.2/room/types.d.ts +127 -127
- package/dist/ts4.2/room/utils.d.ts +273 -214
- package/dist/ts4.2/test/MockMediaStreamTrack.d.ts +26 -26
- package/dist/ts4.2/test/compressionStreamStubs.d.ts +12 -0
- package/dist/ts4.2/test/mocks.d.ts +12 -12
- package/dist/ts4.2/test/promiseState.d.ts +12 -12
- package/dist/ts4.2/test/signalToken.d.ts +26 -26
- package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -2
- package/dist/ts4.2/utils/AsyncQueue.d.ts +23 -23
- package/dist/ts4.2/utils/TypedPromise.d.ts +17 -17
- package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -13
- package/dist/ts4.2/utils/browserParser.d.ts +13 -13
- package/dist/ts4.2/utils/camelToSnakeCase.d.ts +8 -8
- package/dist/ts4.2/utils/cloneDeep.d.ts +2 -2
- package/dist/ts4.2/utils/dataPacketBuffer.d.ts +34 -34
- package/dist/ts4.2/utils/deferrable-map.d.ts +32 -32
- package/dist/ts4.2/utils/machineInspector.d.ts +54 -54
- package/dist/ts4.2/utils/serializer.d.ts +48 -48
- package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -15
- package/dist/ts4.2/utils/ttlmap.d.ts +20 -20
- package/dist/ts4.2/version.d.ts +15 -15
- package/package.json +5 -4
- package/src/api/SignalClient.e2e.test.ts +1 -3
- package/src/api/SignalClient.test.ts +394 -10
- package/src/api/SignalClient.ts +132 -76
- package/src/api/WebSocketStream.test.ts +11 -0
- package/src/api/WebSocketStream.ts +22 -1
- package/src/api/utils.ts +1 -1
- package/src/e2ee/E2eeManager.test.ts +43 -0
- package/src/e2ee/E2eeManager.ts +6 -2
- package/src/e2ee/worker/FrameCryptor.test.ts +212 -90
- package/src/e2ee/worker/FrameCryptor.ts +21 -4
- package/src/e2ee/worker/naluUtils.test.ts +123 -0
- package/src/e2ee/worker/naluUtils.ts +57 -2
- package/src/index.ts +2 -0
- package/src/options.ts +16 -0
- package/src/room/DefaultReconnectPolicy.test.ts +58 -0
- package/src/room/DefaultReconnectPolicy.ts +14 -2
- package/src/room/PCTransport.test.ts +327 -9
- package/src/room/PCTransport.ts +339 -36
- package/src/room/PCTransportManager.ts +3 -1
- package/src/room/RTCEngine.test.ts +318 -2
- package/src/room/RTCEngine.ts +126 -12
- package/src/room/Room.test.ts +148 -6
- package/src/room/Room.ts +312 -80
- package/src/room/data-channel/DataChannelManager.test.ts +24 -7
- package/src/room/data-channel/DataChannelManager.ts +43 -38
- package/src/room/data-channel/FlowControlledDataChannel.test.ts +60 -18
- package/src/room/data-channel/FlowControlledDataChannel.ts +40 -22
- package/src/room/data-channel/LossyDataChannel.test.ts +38 -0
- package/src/room/data-channel/LossyDataChannel.ts +10 -1
- package/src/room/data-channel/ReliableDataChannel.test.ts +72 -0
- package/src/room/data-channel/ReliableDataChannel.ts +16 -5
- package/src/room/data-stream/incoming/IncomingDataStreamManager.test.ts +47 -20
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +16 -5
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.test.ts +60 -39
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +4 -4
- package/src/room/defaults.ts +1 -0
- package/src/room/errors.test.ts +36 -0
- package/src/room/errors.ts +27 -0
- package/src/room/events.ts +2 -1
- package/src/room/participant/LocalParticipant.test.ts +136 -1
- package/src/room/participant/LocalParticipant.ts +66 -17
- package/src/room/participant/Participant.ts +6 -3
- package/src/room/participant/publishUtils.ts +0 -19
- package/src/room/participant/restartOnDefaultAudioDevice.test.ts +177 -0
- package/src/room/rpc/client/RpcClientManager.test.ts +51 -0
- package/src/room/rpc/client/RpcClientManager.ts +32 -15
- package/src/room/rpc/utils.test.ts +32 -0
- package/src/room/rpc/utils.ts +7 -0
- package/src/room/track/LocalAudioTrack.ts +101 -10
- package/src/room/track/LocalTrack.ts +87 -19
- package/src/room/track/LocalVideoTrack.ts +17 -3
- package/src/room/track/RemoteAudioTrack.ts +59 -14
- package/src/room/track/RemoteVideoTrack.test.ts +210 -0
- package/src/room/track/RemoteVideoTrack.ts +29 -3
- package/src/room/track/processor/types.ts +11 -0
- package/src/room/track/utils.test.ts +206 -2
- package/src/room/track/utils.ts +117 -5
- package/src/room/utils.test.ts +250 -0
- package/src/room/utils.ts +152 -19
- package/src/test/compressionStreamStubs.ts +53 -0
package/src/room/PCTransport.ts
CHANGED
|
@@ -28,57 +28,232 @@ interface TrackBitrateInfo {
|
|
|
28
28
|
* Why same for all codecs: Target bitrate already accounts for codec efficiency
|
|
29
29
|
* (e.g., users set lower targets for VP9/AV1 knowing they're more efficient).
|
|
30
30
|
* Why cap at 1 Mbps: Prevents BWE from starting too aggressively on high bitrate tracks.
|
|
31
|
+
* The cap is lowered further for connections that were slow to set up, see
|
|
32
|
+
* `computeStartBitrateCap`, or that the browser reports as slow, see
|
|
33
|
+
* `connectionEffectiveTypeStartBitrateCap`.
|
|
31
34
|
*/
|
|
32
35
|
const startBitrateMultiplier = 0.9;
|
|
33
36
|
|
|
34
37
|
/** Maximum x-google-start-bitrate in kbps. 1 Mbps prevents BWE from starting too aggressively. */
|
|
35
38
|
const maxStartBitrateKbps = 1000;
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Minimum x-google-start-bitrate in kbps: libwebrtc's own default starting estimate. A target
|
|
42
|
+
* below this gets no hint, since seeding above the real capacity costs more than the ramp it
|
|
43
|
+
* saves, and a slow connection is seeded no higher than this.
|
|
44
|
+
*/
|
|
45
|
+
const minTargetBitrateKbps = 300;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Connection setup times (ms) bounding the ramp in `computeStartBitrateCap`: at or below the
|
|
49
|
+
* first the hint is capped at `maxStartBitrateKbps`, at or above the second at
|
|
50
|
+
* `minTargetBitrateKbps`. Measured on shaped links (CLT-3380): healthy links set up in under
|
|
51
|
+
* 860 ms, a 1 Mbps link with 150 ms RTT took 1.1–1.7 s, a 500 kbps link ~2.3 s at the median,
|
|
52
|
+
* and a 300 kbps link never under 3 s. The first sits above the 1 Mbps link, which a lower seed
|
|
53
|
+
* only slows down, and the second above the 500 kbps link's median, so only links that cannot
|
|
54
|
+
* carry more land at the floor.
|
|
55
|
+
*/
|
|
56
|
+
const setupTimeForMaxStartBitrateMs = 1500;
|
|
57
|
+
const setupTimeForMinStartBitrateMs = 3500;
|
|
58
|
+
|
|
59
|
+
/** Cap on the start bitrate hint, in kbps, when the browser reports a `3g` connection. */
|
|
60
|
+
const effectiveType3gStartBitrateKbps = 500;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The part of the Network Information API (`navigator.connection`) read here. It is Chromium
|
|
64
|
+
* only, so TypeScript's DOM typings do not declare it.
|
|
65
|
+
*/
|
|
66
|
+
interface NetworkInformation {
|
|
67
|
+
readonly effectiveType?: 'slow-2g' | '2g' | '3g' | '4g';
|
|
68
|
+
}
|
|
69
|
+
|
|
37
70
|
const debounceInterval = 20;
|
|
38
71
|
|
|
39
72
|
/**
|
|
40
|
-
*
|
|
41
|
-
* This SDP munging is used for a bitrate setting that cannot be applied through
|
|
42
|
-
* `RTCRtpEncodingParameters`.
|
|
73
|
+
* Codec payload for `codec` in this media section, when the section carries `cid`.
|
|
43
74
|
*
|
|
44
|
-
* Returns `undefined` when the section does not belong to the track, `0` when
|
|
45
|
-
*
|
|
46
|
-
* requested codec is present (whether the bitrate was added or already set).
|
|
75
|
+
* Returns `undefined` when the section does not belong to the track, `0` when it does but
|
|
76
|
+
* does not offer the requested codec, and the codec payload otherwise.
|
|
47
77
|
*
|
|
48
78
|
* @internal
|
|
49
79
|
*/
|
|
50
|
-
export function
|
|
80
|
+
export function findTrackCodecPayload(
|
|
51
81
|
media: MediaDescription,
|
|
52
82
|
cid: string,
|
|
53
83
|
codec: string,
|
|
54
|
-
maxbr: number,
|
|
55
|
-
isScreenShare = false,
|
|
56
84
|
): number | undefined {
|
|
57
85
|
if (!media.msid?.includes(cid)) {
|
|
58
86
|
return undefined;
|
|
59
87
|
}
|
|
88
|
+
return media.rtp.find((rtp) => rtp.codec.toUpperCase() === codec.toUpperCase())?.payload ?? 0;
|
|
89
|
+
}
|
|
60
90
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Start bitrate hinted for a single track, or `undefined` when its target is too low to
|
|
93
|
+
* be worth seeding.
|
|
94
|
+
*
|
|
95
|
+
* 90% of the target leaves ~10% headroom for the estimator to settle. The same multiplier
|
|
96
|
+
* is used for every codec because the target already reflects the codec's efficiency.
|
|
97
|
+
* Camera is capped at 1 Mbps so the estimator does not open too aggressively on a
|
|
98
|
+
* high-bitrate track; screen share is exempt, because its content needs the bitrate
|
|
99
|
+
* immediately to stay legible.
|
|
100
|
+
*
|
|
101
|
+
* The cap is lowered for a connection that was slow to set up (`computeStartBitrateCap`), and
|
|
102
|
+
* for one the browser reports as slow (`connectionEffectiveTypeStartBitrateCap`), whichever is
|
|
103
|
+
* lower. Screen share is exempt from both, however slow the connection.
|
|
104
|
+
*
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
export function computeTrackStartBitrate(
|
|
108
|
+
trackbr: TrackBitrateInfo,
|
|
109
|
+
connectionSetupTimeMs?: number,
|
|
110
|
+
): number | undefined {
|
|
111
|
+
if (trackbr.maxbr < minTargetBitrateKbps) {
|
|
112
|
+
return undefined;
|
|
65
113
|
}
|
|
114
|
+
const calculated = Math.min(Math.round(trackbr.maxbr * startBitrateMultiplier), trackbr.maxbr);
|
|
115
|
+
if (trackbr.isScreenShare) {
|
|
116
|
+
return calculated;
|
|
117
|
+
}
|
|
118
|
+
const cap = Math.min(
|
|
119
|
+
computeStartBitrateCap(connectionSetupTimeMs),
|
|
120
|
+
connectionEffectiveTypeStartBitrateCap(),
|
|
121
|
+
);
|
|
122
|
+
return Math.min(calculated, cap);
|
|
123
|
+
}
|
|
66
124
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Cap on the start bitrate hint, in kbps, from the browser's effective connection type
|
|
127
|
+
* (`navigator.connection.effectiveType`, Chromium only): `slow-2g` and `2g` cap at
|
|
128
|
+
* `minTargetBitrateKbps`, `3g` at `effectiveType3gStartBitrateKbps`.
|
|
129
|
+
*
|
|
130
|
+
* It catches links that have low bandwidth but low latency, which set up quickly and so slip
|
|
131
|
+
* past the setup time ramp. `4g`, and browsers without the API, give no guidance and stay at the
|
|
132
|
+
* `maxStartBitrateKbps` ceiling, as `computeStartBitrateCap` does without a setup time. The
|
|
133
|
+
* connection RTT the API also reports is not used: it lags behind network changes, and the setup
|
|
134
|
+
* time already reflects latency.
|
|
135
|
+
*/
|
|
136
|
+
function connectionEffectiveTypeStartBitrateCap(): number {
|
|
137
|
+
// A `typeof` guard, since `globalThis` is missing from some supported browsers (Chrome < 71,
|
|
138
|
+
// Firefox < 65, Safari < 12.1) and the build does not polyfill it.
|
|
139
|
+
const connection =
|
|
140
|
+
typeof navigator !== 'undefined'
|
|
141
|
+
? (navigator as Navigator & { connection?: NetworkInformation }).connection
|
|
142
|
+
: undefined;
|
|
143
|
+
switch (connection?.effectiveType) {
|
|
144
|
+
case 'slow-2g':
|
|
145
|
+
case '2g':
|
|
146
|
+
return minTargetBitrateKbps;
|
|
147
|
+
case '3g':
|
|
148
|
+
return effectiveType3gStartBitrateKbps;
|
|
149
|
+
default:
|
|
150
|
+
return maxStartBitrateKbps;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cap on the start bitrate hint, in kbps, from how long the connection took to set up.
|
|
156
|
+
*
|
|
157
|
+
* Connection setup time (signaling join plus ICE/DTLS) is the network signal every platform has
|
|
158
|
+
* before the first video offer, since libwebrtc cannot probe the path until a video sender
|
|
159
|
+
* exists. It grows with round-trip time and loss, which also mark the links where a 1 Mbps
|
|
160
|
+
* seed overshoots, so the cap ramps linearly from `maxStartBitrateKbps` at
|
|
161
|
+
* `setupTimeForMaxStartBitrateMs` down to `minTargetBitrateKbps` at
|
|
162
|
+
* `setupTimeForMinStartBitrateMs`. A video offer built while the connection is still being set
|
|
163
|
+
* up (publishing during connect) passes the time elapsed so far, a lower bound on the setup time.
|
|
164
|
+
* Without any timing the cap stays at the 1 Mbps ceiling.
|
|
165
|
+
*/
|
|
166
|
+
function computeStartBitrateCap(connectionSetupTimeMs?: number): number {
|
|
167
|
+
if (connectionSetupTimeMs === undefined) {
|
|
168
|
+
return maxStartBitrateKbps;
|
|
169
|
+
}
|
|
170
|
+
const ramp = Math.min(
|
|
171
|
+
Math.max(
|
|
172
|
+
(connectionSetupTimeMs - setupTimeForMaxStartBitrateMs) /
|
|
173
|
+
(setupTimeForMinStartBitrateMs - setupTimeForMaxStartBitrateMs),
|
|
174
|
+
0,
|
|
175
|
+
),
|
|
176
|
+
1,
|
|
177
|
+
);
|
|
178
|
+
return Math.round(maxStartBitrateKbps - ramp * (maxStartBitrateKbps - minTargetBitrateKbps));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The single start bitrate for this peer connection: the largest hint among the video
|
|
183
|
+
* m-sections of `media` that map to a published track.
|
|
184
|
+
*
|
|
185
|
+
* libwebrtc reads `x-google-start-bitrate` per m-section but applies it to the shared
|
|
186
|
+
* `Call` (`WebRtcVideoSendChannel::ApplyChangedParams` -> `SetSdpBitrateParameters`), where
|
|
187
|
+
* `RtpBitrateConfigurator` holds one config for the whole connection. Differing per-section
|
|
188
|
+
* values are therefore last-writer-wins, decided by m-section order, so every video section
|
|
189
|
+
* gets the same number instead.
|
|
190
|
+
*
|
|
191
|
+
* Only sections that can send local media are considered. `trackBitrates` is append-only and an
|
|
192
|
+
* unpublished section keeps its `a=msid`, so matching on msid alone would still pair a stale
|
|
193
|
+
* entry with its old section — letting an uncapped screen-share target seed a connection that
|
|
194
|
+
* now carries only a camera, or consuming the one-shot hint on a section that sends nothing.
|
|
195
|
+
*
|
|
196
|
+
* The direction is the discriminator, as an exclusion rather than a match: `recvonly` and
|
|
197
|
+
* `inactive` are the only directions that cannot carry local media, and they are exactly the
|
|
198
|
+
* two a dead section lands on — unpublish sets `inactive` (explicitly in
|
|
199
|
+
* `LocalParticipant.unpublishTrack`, and via `removeTrack`'s sendonly -> inactive transition for
|
|
200
|
+
* the simulcast senders), `removeTrack` on a `sendrecv` transceiver leaves `recvonly`, and the
|
|
201
|
+
* pre-populated placeholders are `recvonly`. Everything else sends: `sendonly` from the
|
|
202
|
+
* `addTransceiver` path, `sendrecv` from the legacy `addTrack` fallback (which reuses a
|
|
203
|
+
* transceiver rather than creating a sendonly one), and a section with no direction attribute,
|
|
204
|
+
* which SDP defaults to `sendrecv`.
|
|
205
|
+
*
|
|
206
|
+
* @internal
|
|
207
|
+
*/
|
|
208
|
+
export function computeConnectionStartBitrate(
|
|
209
|
+
media: MediaDescription[],
|
|
210
|
+
trackBitrates: TrackBitrateInfo[],
|
|
211
|
+
connectionSetupTimeMs?: number,
|
|
212
|
+
): number | undefined {
|
|
213
|
+
let connectionStartBitrate: number | undefined;
|
|
214
|
+
for (const m of media) {
|
|
215
|
+
if (m.type !== 'video' || m.direction === 'recvonly' || m.direction === 'inactive') {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
for (const trackbr of trackBitrates) {
|
|
219
|
+
if (!trackbr.cid) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const codecPayload = findTrackCodecPayload(m, trackbr.cid, trackbr.codec);
|
|
223
|
+
if (codecPayload === undefined) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const startBitrate =
|
|
227
|
+
codecPayload > 0 ? computeTrackStartBitrate(trackbr, connectionSetupTimeMs) : undefined;
|
|
228
|
+
if (
|
|
229
|
+
startBitrate !== undefined &&
|
|
230
|
+
(connectionStartBitrate === undefined || startBitrate > connectionStartBitrate)
|
|
231
|
+
) {
|
|
232
|
+
connectionStartBitrate = startBitrate;
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return connectionStartBitrate;
|
|
238
|
+
}
|
|
75
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Declares `x-google-start-bitrate` on `codecPayload`'s fmtp. This SDP munging is used for
|
|
242
|
+
* a bitrate setting that cannot be applied through `RTCRtpEncodingParameters`.
|
|
243
|
+
*
|
|
244
|
+
* Returns whether the section now carries the hint.
|
|
245
|
+
*
|
|
246
|
+
* @internal
|
|
247
|
+
*/
|
|
248
|
+
export function applyVideoStartBitrate(
|
|
249
|
+
media: MediaDescription,
|
|
250
|
+
codecPayload: number,
|
|
251
|
+
startBitrate: number,
|
|
252
|
+
): boolean {
|
|
76
253
|
const fmtp = media.fmtp.find((entry) => entry.payload === codecPayload);
|
|
77
254
|
if (fmtp) {
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// This forces every track sharing that payload to use the initial track's
|
|
81
|
-
// start bitrate.
|
|
255
|
+
// A payload type is shared across the bundle, so a value written for one section is
|
|
256
|
+
// already the connection-level one; leave it rather than rewrite it.
|
|
82
257
|
if (!fmtp.config.includes('x-google-start-bitrate')) {
|
|
83
258
|
fmtp.config += `;x-google-start-bitrate=${startBitrate}`;
|
|
84
259
|
}
|
|
@@ -90,7 +265,7 @@ export function applyVideoStartBitrate(
|
|
|
90
265
|
});
|
|
91
266
|
}
|
|
92
267
|
|
|
93
|
-
return
|
|
268
|
+
return true;
|
|
94
269
|
}
|
|
95
270
|
|
|
96
271
|
export const PCEvents = {
|
|
@@ -127,6 +302,11 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
127
302
|
|
|
128
303
|
latestOfferId: number = 0;
|
|
129
304
|
|
|
305
|
+
private answeredOfferId: number = 0;
|
|
306
|
+
|
|
307
|
+
/** offerId (0 if unnumbered) of the answer being applied right now */
|
|
308
|
+
private answerInFlight: number | undefined;
|
|
309
|
+
|
|
130
310
|
latestAcknowledgedOfferId: number = 0;
|
|
131
311
|
|
|
132
312
|
private offerLock: Mutex;
|
|
@@ -141,6 +321,29 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
141
321
|
|
|
142
322
|
trackBitrates: TrackBitrateInfo[] = [];
|
|
143
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Whether an offer carrying the connection-level `x-google-start-bitrate` has been
|
|
326
|
+
* accepted locally. The hint is written once per peer connection: libwebrtc retains
|
|
327
|
+
* `start_bitrate_bps` in `RtpBitrateConfigurator` and re-applies it on network route
|
|
328
|
+
* changes, so a later rewrite is at best a no-op and at worst restarts a converged
|
|
329
|
+
* bandwidth estimator. A new peer connection (full reconnect) seeds a new estimator.
|
|
330
|
+
*/
|
|
331
|
+
private hasAppliedVideoStartBitrate = false;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* How long this peer connection took to set up, in ms, recorded once the initial connect
|
|
335
|
+
* succeeds. Lowers the start bitrate hint for a slow connection; see `computeStartBitrateCap`.
|
|
336
|
+
*/
|
|
337
|
+
private connectionSetupTimeMs?: number;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* When the connection attempt that built this peer connection began (`performance.now()`).
|
|
341
|
+
* Until `connectionSetupTimeMs` is known, the time elapsed since then is used as a lower bound
|
|
342
|
+
* on the setup time, so a video published during connect is still seeded from it rather than
|
|
343
|
+
* waiting for the connection, which would delay the offer.
|
|
344
|
+
*/
|
|
345
|
+
private connectionStartedAt?: number;
|
|
346
|
+
|
|
144
347
|
remoteStereoMids: string[] = [];
|
|
145
348
|
|
|
146
349
|
remoteNackMids: string[] = [];
|
|
@@ -250,6 +453,36 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
250
453
|
});
|
|
251
454
|
return false;
|
|
252
455
|
}
|
|
456
|
+
if (sd.type === 'answer') {
|
|
457
|
+
if (
|
|
458
|
+
isDuplicateAnswer(
|
|
459
|
+
offerId,
|
|
460
|
+
this.answerInFlight,
|
|
461
|
+
this.answeredOfferId,
|
|
462
|
+
this._pc?.signalingState,
|
|
463
|
+
this.pendingInitialOffer !== undefined,
|
|
464
|
+
)
|
|
465
|
+
) {
|
|
466
|
+
// a repeat of an answer already applied or being applied (e.g.
|
|
467
|
+
// duplicated by a proxy): there is no local offer left to answer
|
|
468
|
+
this.log.warn('ignoring duplicate answer', { offerId });
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
// claimed before any await, so a copy arriving mid-apply is caught too;
|
|
472
|
+
// released however the apply ends, so a genuine retry is still possible
|
|
473
|
+
this.answerInFlight = offerId;
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
return await this.applyRemoteDescription(sd, offerId);
|
|
477
|
+
} finally {
|
|
478
|
+
if (sd.type === 'answer') this.answerInFlight = undefined;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private async applyRemoteDescription(
|
|
483
|
+
sd: RTCSessionDescriptionInit,
|
|
484
|
+
offerId: number,
|
|
485
|
+
): Promise<boolean> {
|
|
253
486
|
let mungedSDP: string | undefined = undefined;
|
|
254
487
|
if (sd.type === 'offer') {
|
|
255
488
|
let { stereoMids, nackMids } = extractStereoAndNackAudioFromOffer(sd);
|
|
@@ -331,6 +564,8 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
331
564
|
mungedSDP = write(sdpParsed);
|
|
332
565
|
}
|
|
333
566
|
await this.setMungedSDP(sd, mungedSDP, true);
|
|
567
|
+
// only a successfully applied answer makes later copies duplicates
|
|
568
|
+
if (sd.type === 'answer' && offerId > 0) this.answeredOfferId = offerId;
|
|
334
569
|
|
|
335
570
|
if (this.pendingCandidates.length > 0) {
|
|
336
571
|
this.iceLog.debug('flushing queued ICE candidates', {
|
|
@@ -453,6 +688,18 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
453
688
|
this.log.debug('original offer', { sdp: offer.sdp });
|
|
454
689
|
|
|
455
690
|
const sdpParsed = parse(offer.sdp ?? '');
|
|
691
|
+
// One value for every video m-section, written only on the first offer that carries
|
|
692
|
+
// local video: the hint is connection-level in libwebrtc, so differing per-section
|
|
693
|
+
// values would be last-writer-wins on m-section order. Offers before any video is
|
|
694
|
+
// published (data channel or audio only) find no target and leave the latch unset.
|
|
695
|
+
const connectionStartBitrate = this.hasAppliedVideoStartBitrate
|
|
696
|
+
? undefined
|
|
697
|
+
: computeConnectionStartBitrate(
|
|
698
|
+
sdpParsed.media,
|
|
699
|
+
this.trackBitrates,
|
|
700
|
+
this.connectionSetupTimeEstimateMs(),
|
|
701
|
+
);
|
|
702
|
+
let appliedVideoStartBitrate = false;
|
|
456
703
|
sdpParsed.media.forEach((media) => {
|
|
457
704
|
ensureIPAddrMatchVersion(media);
|
|
458
705
|
if (media.type === 'audio') {
|
|
@@ -463,19 +710,21 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
463
710
|
return false;
|
|
464
711
|
}
|
|
465
712
|
|
|
466
|
-
const codecPayload =
|
|
467
|
-
media,
|
|
468
|
-
trackbr.cid,
|
|
469
|
-
trackbr.codec,
|
|
470
|
-
trackbr.maxbr,
|
|
471
|
-
trackbr.isScreenShare,
|
|
472
|
-
);
|
|
713
|
+
const codecPayload = findTrackCodecPayload(media, trackbr.cid, trackbr.codec);
|
|
473
714
|
if (codecPayload === undefined) {
|
|
474
715
|
return false;
|
|
475
716
|
}
|
|
476
717
|
|
|
477
|
-
if (codecPayload > 0
|
|
478
|
-
|
|
718
|
+
if (codecPayload > 0) {
|
|
719
|
+
if (connectionStartBitrate !== undefined) {
|
|
720
|
+
appliedVideoStartBitrate =
|
|
721
|
+
applyVideoStartBitrate(media, codecPayload, connectionStartBitrate) ||
|
|
722
|
+
appliedVideoStartBitrate;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (isSVCCodec(trackbr.codec) && !isSafari()) {
|
|
726
|
+
this.ddExtID = ensureVideoDDExtension(media, sdpParsed, this.ddExtID);
|
|
727
|
+
}
|
|
479
728
|
}
|
|
480
729
|
|
|
481
730
|
return true;
|
|
@@ -500,7 +749,17 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
500
749
|
});
|
|
501
750
|
return;
|
|
502
751
|
}
|
|
503
|
-
|
|
752
|
+
const mungedSdp = write(sdpParsed);
|
|
753
|
+
await this.setMungedSDP(offer, mungedSdp);
|
|
754
|
+
// setMungedSDP falls back to the unmunged SDP on rejection. Only consume the
|
|
755
|
+
// one-shot hint once the SDP carrying it has been accepted locally.
|
|
756
|
+
if (appliedVideoStartBitrate && offer.sdp === mungedSdp) {
|
|
757
|
+
this.hasAppliedVideoStartBitrate = true;
|
|
758
|
+
this.log.info('applied x-google-start-bitrate', {
|
|
759
|
+
startBitrateKbps: connectionStartBitrate,
|
|
760
|
+
connectionSetupTimeMs: this.connectionSetupTimeEstimateMs(),
|
|
761
|
+
});
|
|
762
|
+
}
|
|
504
763
|
this.onOffer(offer, this.latestOfferId);
|
|
505
764
|
} finally {
|
|
506
765
|
unlock();
|
|
@@ -554,6 +813,30 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
554
813
|
this.trackBitrates.push(info);
|
|
555
814
|
}
|
|
556
815
|
|
|
816
|
+
/**
|
|
817
|
+
* Record how long this peer connection took to set up. Called once, when the initial connect
|
|
818
|
+
* succeeds; resumes and ICE restarts keep the estimator they have and never call this.
|
|
819
|
+
*/
|
|
820
|
+
setConnectionSetupTime(ms: number) {
|
|
821
|
+
this.connectionSetupTimeMs = ms;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/** Record when the connection attempt that built this peer connection began. */
|
|
825
|
+
setConnectionStartedAt(startedAt: number) {
|
|
826
|
+
this.connectionStartedAt = startedAt;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** The measured setup time, or while still connecting, the time elapsed so far. */
|
|
830
|
+
private connectionSetupTimeEstimateMs(): number | undefined {
|
|
831
|
+
if (this.connectionSetupTimeMs !== undefined) {
|
|
832
|
+
return this.connectionSetupTimeMs;
|
|
833
|
+
}
|
|
834
|
+
if (this.connectionStartedAt === undefined) {
|
|
835
|
+
return undefined;
|
|
836
|
+
}
|
|
837
|
+
return Math.round(performance.now() - this.connectionStartedAt);
|
|
838
|
+
}
|
|
839
|
+
|
|
557
840
|
setConfiguration(rtcConfig: RTCConfiguration) {
|
|
558
841
|
if (!this._pc) {
|
|
559
842
|
throw new UnexpectedConnectionState('PC closed, cannot configure');
|
|
@@ -952,6 +1235,26 @@ export function conformBundledCodecFmtp(
|
|
|
952
1235
|
}
|
|
953
1236
|
|
|
954
1237
|
/** @internal */
|
|
1238
|
+
/**
|
|
1239
|
+
* Whether an incoming answer repeats one already applied or being applied.
|
|
1240
|
+
* Any answer for the offer whose answer is in flight is a duplicate, numbered
|
|
1241
|
+
* or not. With an offerId (current servers) an answer is also a duplicate if
|
|
1242
|
+
* that offer's answer was already applied. Without one, fall back to the
|
|
1243
|
+
* signaling state: in `stable` no local offer is pending, unless the initial
|
|
1244
|
+
* offer is still deferred (it is set locally only once its answer arrives).
|
|
1245
|
+
*/
|
|
1246
|
+
export function isDuplicateAnswer(
|
|
1247
|
+
offerId: number,
|
|
1248
|
+
answerInFlight: number | undefined,
|
|
1249
|
+
answeredOfferId: number,
|
|
1250
|
+
signalingState: RTCSignalingState | undefined,
|
|
1251
|
+
hasPendingInitialOffer: boolean,
|
|
1252
|
+
): boolean {
|
|
1253
|
+
if (answerInFlight === offerId) return true;
|
|
1254
|
+
if (offerId > 0) return offerId === answeredOfferId;
|
|
1255
|
+
return signalingState === 'stable' && !hasPendingInitialOffer;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
955
1258
|
export function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
|
|
956
1259
|
stereoMids: string[];
|
|
957
1260
|
nackMids: string[];
|
|
@@ -229,7 +229,9 @@ export class PCTransportManager {
|
|
|
229
229
|
this.publisher.getConnectionState() !== 'connecting'
|
|
230
230
|
) {
|
|
231
231
|
this.log.debug('negotiation required, start negotiating');
|
|
232
|
-
this.publisher.negotiate()
|
|
232
|
+
this.publisher.negotiate().catch((err) => {
|
|
233
|
+
this.log.error(err);
|
|
234
|
+
});
|
|
233
235
|
}
|
|
234
236
|
await Promise.all(
|
|
235
237
|
this.requiredTransports?.map((transport) =>
|