livekit-client 2.20.0 → 2.20.1
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/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 +8 -4
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +156 -16
- 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 +8 -0
- 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.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +1 -0
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +1 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +1 -0
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts +1 -0
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +1 -0
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/naluUtils.d.ts +1 -0
- package/dist/src/e2ee/worker/naluUtils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/sifPayload.d.ts +1 -0
- package/dist/src/e2ee/worker/sifPayload.d.ts.map +1 -1
- package/dist/src/frameMetadata/frameMetadata.d.ts +1 -0
- package/dist/src/frameMetadata/frameMetadata.d.ts.map +1 -1
- package/dist/src/frameMetadata/types.d.ts +1 -0
- package/dist/src/frameMetadata/types.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +40 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +1 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +1 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
- package/dist/src/room/data-track/frame.d.ts +1 -0
- package/dist/src/room/data-track/frame.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/types.d.ts +1 -0
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts +1 -0
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/index.d.ts +1 -0
- package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/serializable.d.ts +1 -0
- package/dist/src/room/data-track/packet/serializable.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -0
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +1 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/record.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/type-polyfills/non-shared-typed-arrays.d.ts +2 -0
- package/dist/src/type-polyfills/non-shared-typed-arrays.d.ts.map +1 -0
- package/dist/src/utils/dataPacketBuffer.d.ts +1 -0
- package/dist/src/utils/dataPacketBuffer.d.ts.map +1 -1
- package/dist/ts4.2/e2ee/E2eeManager.d.ts +1 -0
- package/dist/ts4.2/e2ee/types.d.ts +1 -0
- package/dist/ts4.2/e2ee/utils.d.ts +1 -0
- package/dist/ts4.2/e2ee/worker/DataCryptor.d.ts +1 -0
- package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +1 -0
- package/dist/ts4.2/e2ee/worker/naluUtils.d.ts +1 -0
- package/dist/ts4.2/e2ee/worker/sifPayload.d.ts +1 -0
- package/dist/ts4.2/frameMetadata/frameMetadata.d.ts +1 -0
- package/dist/ts4.2/frameMetadata/types.d.ts +1 -0
- package/dist/ts4.2/room/PCTransport.d.ts +40 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +1 -0
- package/dist/ts4.2/room/Room.d.ts +1 -0
- package/dist/ts4.2/room/data-track/frame.d.ts +1 -0
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +1 -0
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +1 -0
- package/dist/ts4.2/room/data-track/packet/index.d.ts +1 -0
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +1 -0
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/ts4.2/room/participant/Participant.d.ts +1 -0
- package/dist/ts4.2/room/track/Track.d.ts +1 -0
- package/dist/ts4.2/room/utils.d.ts +1 -0
- package/dist/ts4.2/type-polyfills/non-shared-typed-arrays.d.ts +2 -0
- package/dist/ts4.2/utils/dataPacketBuffer.d.ts +1 -0
- package/package.json +2 -2
- package/src/api/SignalClient.ts +1 -0
- package/src/e2ee/E2eeManager.ts +3 -1
- package/src/e2ee/types.ts +1 -0
- package/src/e2ee/utils.ts +1 -0
- package/src/e2ee/worker/DataCryptor.ts +1 -0
- package/src/e2ee/worker/FrameCryptor.test.ts +1 -0
- package/src/e2ee/worker/FrameCryptor.ts +1 -0
- package/src/e2ee/worker/ParticipantKeyHandler.test.ts +1 -0
- package/src/e2ee/worker/e2ee.worker.ts +1 -0
- package/src/e2ee/worker/naluUtils.ts +1 -0
- package/src/e2ee/worker/sifPayload.ts +1 -0
- package/src/frameMetadata/frameMetadata.test.ts +83 -0
- package/src/frameMetadata/frameMetadata.ts +8 -0
- package/src/frameMetadata/types.ts +1 -0
- package/src/room/PCTransport.test.ts +150 -0
- package/src/room/PCTransport.ts +113 -0
- package/src/room/RTCEngine.ts +1 -0
- package/src/room/Room.ts +1 -0
- package/src/room/data-track/depacketizer.ts +1 -0
- package/src/room/data-track/frame.ts +1 -0
- package/src/room/data-track/outgoing/types.ts +1 -0
- package/src/room/data-track/packet/extensions.ts +1 -0
- package/src/room/data-track/packet/index.ts +1 -0
- package/src/room/data-track/packet/serializable.ts +1 -0
- package/src/room/participant/LocalParticipant.ts +1 -0
- package/src/room/participant/Participant.ts +1 -0
- package/src/room/track/Track.ts +1 -0
- package/src/room/track/record.ts +1 -0
- package/src/room/utils.ts +1 -0
- package/src/type-polyfills/{non-shared-typed-arrays.d.ts → non-shared-typed-arrays.ts} +6 -1
- package/src/utils/dataPacketBuffer.ts +2 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { type MediaDescription, parse } from 'sdp-transform';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { conformBundledCodecFmtp, placeholderMidsFromTransceivers } from './PCTransport';
|
|
4
|
+
|
|
5
|
+
/** Parse the `key[=value]` pairs of an fmtp config into a comparable set. */
|
|
6
|
+
const paramSet = (config: string) => new Set(config.split(';').filter(Boolean));
|
|
7
|
+
|
|
8
|
+
const fmtpOf = (media: MediaDescription[], mid: string, payload: number) => {
|
|
9
|
+
const m = media.find((section) => `${section.mid}` === mid)!;
|
|
10
|
+
return m.fmtp.find((f) => f.payload === payload)?.config;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Predicate for conformBundledCodecFmtp based on an explicit set of mids. */
|
|
14
|
+
const placeholders = (mids: string[]) => {
|
|
15
|
+
const set = new Set(mids);
|
|
16
|
+
return (m: MediaDescription) => set.has(`${m.mid}`);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// One publisher bundle: a published mic + camera (real sends, with msid/ssrc)
|
|
20
|
+
// and their pre-populated recvonly placeholders.
|
|
21
|
+
const PUBLISHER_SDP = `v=0
|
|
22
|
+
o=- 0 0 IN IP4 127.0.0.1
|
|
23
|
+
s=-
|
|
24
|
+
t=0 0
|
|
25
|
+
a=group:BUNDLE 0 1 2 3
|
|
26
|
+
m=audio 9 UDP/TLS/RTP/SAVPF 111
|
|
27
|
+
c=IN IP4 0.0.0.0
|
|
28
|
+
a=mid:0
|
|
29
|
+
a=sendonly
|
|
30
|
+
a=msid:s mic
|
|
31
|
+
a=ssrc:1111 cname:a
|
|
32
|
+
a=rtpmap:111 opus/48000/2
|
|
33
|
+
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
|
34
|
+
m=audio 9 UDP/TLS/RTP/SAVPF 111
|
|
35
|
+
c=IN IP4 0.0.0.0
|
|
36
|
+
a=mid:1
|
|
37
|
+
a=recvonly
|
|
38
|
+
a=rtpmap:111 opus/48000/2
|
|
39
|
+
a=fmtp:111 minptime=10;useinbandfec=1
|
|
40
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
41
|
+
c=IN IP4 0.0.0.0
|
|
42
|
+
a=mid:2
|
|
43
|
+
a=sendonly
|
|
44
|
+
a=msid:s cam
|
|
45
|
+
a=ssrc:2222 cname:b
|
|
46
|
+
a=rtpmap:49 H265/90000
|
|
47
|
+
a=fmtp:49 level-id=186;profile-id=1;tier-flag=0;tx-mode=SRST
|
|
48
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
49
|
+
c=IN IP4 0.0.0.0
|
|
50
|
+
a=mid:3
|
|
51
|
+
a=recvonly
|
|
52
|
+
a=rtpmap:49 H265/90000
|
|
53
|
+
a=fmtp:49 level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST`;
|
|
54
|
+
|
|
55
|
+
describe('placeholderMidsFromTransceivers', () => {
|
|
56
|
+
const tr = (mid: string | null, track: unknown) =>
|
|
57
|
+
({ mid, sender: { track } }) as unknown as RTCRtpTransceiver;
|
|
58
|
+
|
|
59
|
+
it('includes transceivers with a mid and no outgoing track', () => {
|
|
60
|
+
const mids = placeholderMidsFromTransceivers([
|
|
61
|
+
tr('0', {}), // real send
|
|
62
|
+
tr('1', null), // pre-populated placeholder
|
|
63
|
+
tr('2', null), // reverted after unpublish (msid may linger in SDP, but no track)
|
|
64
|
+
tr(null, null), // not yet negotiated — no mid, excluded
|
|
65
|
+
]);
|
|
66
|
+
expect(mids).toEqual(new Set(['1', '2']));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('conformBundledCodecFmtp', () => {
|
|
71
|
+
it('copies opus and H.265 fmtp from real sections onto placeholders', () => {
|
|
72
|
+
const { media } = parse(PUBLISHER_SDP);
|
|
73
|
+
|
|
74
|
+
conformBundledCodecFmtp(media, placeholders(['1', '3']));
|
|
75
|
+
|
|
76
|
+
expect(fmtpOf(media, '1', 111)).toBe(fmtpOf(media, '0', 111));
|
|
77
|
+
expect(paramSet(fmtpOf(media, '1', 111)!)).toContain('usedtx=1');
|
|
78
|
+
expect(fmtpOf(media, '3', 49)).toBe(fmtpOf(media, '2', 49));
|
|
79
|
+
expect(paramSet(fmtpOf(media, '3', 49)!)).toContain('level-id=186');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('never rewrites the real (non-placeholder) sections', () => {
|
|
83
|
+
const { media } = parse(PUBLISHER_SDP);
|
|
84
|
+
|
|
85
|
+
conformBundledCodecFmtp(media, placeholders(['1', '3']));
|
|
86
|
+
|
|
87
|
+
expect(fmtpOf(media, '0', 111)).toBe('minptime=10;useinbandfec=1;usedtx=1');
|
|
88
|
+
expect(paramSet(fmtpOf(media, '2', 49)!)).toContain('level-id=186');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('converges divergent placeholders even when no real section declares the payload', () => {
|
|
92
|
+
// Two placeholders that disagree (a reverted one kept level 186, a fresh one
|
|
93
|
+
// has 180) with no active send present.
|
|
94
|
+
const { media } = parse(`v=0
|
|
95
|
+
o=- 0 0 IN IP4 127.0.0.1
|
|
96
|
+
s=-
|
|
97
|
+
t=0 0
|
|
98
|
+
a=group:BUNDLE 10 11
|
|
99
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
100
|
+
a=mid:10
|
|
101
|
+
a=rtpmap:49 H265/90000
|
|
102
|
+
a=fmtp:49 level-id=186;profile-id=1;tier-flag=0;tx-mode=SRST
|
|
103
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
104
|
+
a=mid:11
|
|
105
|
+
a=rtpmap:49 H265/90000
|
|
106
|
+
a=fmtp:49 level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST`);
|
|
107
|
+
|
|
108
|
+
conformBundledCodecFmtp(media, placeholders(['10', '11']));
|
|
109
|
+
|
|
110
|
+
expect(fmtpOf(media, '10', 49)).toBe(fmtpOf(media, '11', 49));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('conforms a reverted section that kept its msid (second-publish case)', () => {
|
|
114
|
+
// The real second publish: a live camera send (level 186), plus the section
|
|
115
|
+
// left over from the first (unpublished) track. Chrome keeps that section's
|
|
116
|
+
// `a=msid`/`a=ssrc` even though it no longer sends, so it must be identified
|
|
117
|
+
// as a placeholder by its transceiver (no track), not by SDP.
|
|
118
|
+
const { media } = parse(`v=0
|
|
119
|
+
o=- 0 0 IN IP4 127.0.0.1
|
|
120
|
+
s=-
|
|
121
|
+
t=0 0
|
|
122
|
+
a=group:BUNDLE 3 4 5
|
|
123
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
124
|
+
a=mid:3
|
|
125
|
+
a=sendonly
|
|
126
|
+
a=msid:s cam
|
|
127
|
+
a=ssrc:9 cname:c
|
|
128
|
+
a=rtpmap:49 H265/90000
|
|
129
|
+
a=fmtp:49 level-id=186;profile-id=1;tier-flag=0;tx-mode=SRST
|
|
130
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
131
|
+
a=mid:4
|
|
132
|
+
a=sendonly
|
|
133
|
+
a=msid:s stale
|
|
134
|
+
a=ssrc:8 cname:d
|
|
135
|
+
a=rtpmap:49 H265/90000
|
|
136
|
+
a=fmtp:49 level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST
|
|
137
|
+
m=video 9 UDP/TLS/RTP/SAVPF 49
|
|
138
|
+
a=mid:5
|
|
139
|
+
a=recvonly
|
|
140
|
+
a=rtpmap:49 H265/90000
|
|
141
|
+
a=fmtp:49 level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST`);
|
|
142
|
+
|
|
143
|
+
// mid 4 still has msid/ssrc in the SDP, but its transceiver has no track.
|
|
144
|
+
conformBundledCodecFmtp(media, placeholders(['4', '5']));
|
|
145
|
+
|
|
146
|
+
expect(paramSet(fmtpOf(media, '3', 49)!)).toContain('level-id=186'); // real send untouched
|
|
147
|
+
expect(fmtpOf(media, '4', 49)).toBe(fmtpOf(media, '3', 49)); // stale conformed
|
|
148
|
+
expect(fmtpOf(media, '5', 49)).toBe(fmtpOf(media, '3', 49)); // placeholder conformed
|
|
149
|
+
});
|
|
150
|
+
});
|
package/src/room/PCTransport.ts
CHANGED
|
@@ -234,6 +234,17 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
234
234
|
});
|
|
235
235
|
}
|
|
236
236
|
});
|
|
237
|
+
// The server's answer sets per-track codec params (e.g. opus `usedtx`) on
|
|
238
|
+
// the sections mapped to a published track, but leaves the pre-populated
|
|
239
|
+
// recvonly placeholder sections with defaults. Conform the placeholders so
|
|
240
|
+
// each shared payload type is consistent across the bundle, otherwise
|
|
241
|
+
// libwebrtc flags a "bundled payload type collision".
|
|
242
|
+
const placeholderMids = this.getPlaceholderMids();
|
|
243
|
+
if (placeholderMids.size > 0) {
|
|
244
|
+
conformBundledCodecFmtp(sdpParsed.media, (media) =>
|
|
245
|
+
placeholderMids.has(getMidString(media.mid!)),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
237
248
|
mungedSDP = write(sdpParsed);
|
|
238
249
|
}
|
|
239
250
|
await this.setMungedSDP(sd, mungedSDP, true);
|
|
@@ -400,6 +411,17 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
400
411
|
});
|
|
401
412
|
}
|
|
402
413
|
});
|
|
414
|
+
// Conform the placeholder sections (pre-populated, or reverted after an
|
|
415
|
+
// unpublish) so every shared payload type carries identical fmtp across the
|
|
416
|
+
// bundle, otherwise libwebrtc flags a "bundled payload type collision".
|
|
417
|
+
// Detection is by transceiver (mids are stable across renegotiations) since
|
|
418
|
+
// an unpublished section keeps its `a=msid`.
|
|
419
|
+
const placeholderMids = this.getPlaceholderMids();
|
|
420
|
+
if (placeholderMids.size > 0) {
|
|
421
|
+
conformBundledCodecFmtp(sdpParsed.media, (media) =>
|
|
422
|
+
placeholderMids.has(getMidString(media.mid!)),
|
|
423
|
+
);
|
|
424
|
+
}
|
|
403
425
|
if (this.latestOfferId > offerId) {
|
|
404
426
|
this.log.warn('latestOfferId mismatch', {
|
|
405
427
|
...this.logContext,
|
|
@@ -428,6 +450,17 @@ export default class PCTransport extends (EventEmitter as new () => TypedEmitter
|
|
|
428
450
|
return answer;
|
|
429
451
|
}
|
|
430
452
|
|
|
453
|
+
/**
|
|
454
|
+
* Returns the mids of transceivers that carry no outgoing track on this
|
|
455
|
+
* (publisher) connection: the pre-populated placeholders added by
|
|
456
|
+
* `RTCEngine.applyInitialPublisherLayout`, plus any transceiver that was used
|
|
457
|
+
* for a track and reverted on unpublish. Their codec fmtp is conformed to the
|
|
458
|
+
* published tracks so a shared payload type stays consistent across the bundle.
|
|
459
|
+
*/
|
|
460
|
+
private getPlaceholderMids(): Set<string> {
|
|
461
|
+
return placeholderMidsFromTransceivers(this._pc?.getTransceivers() ?? []);
|
|
462
|
+
}
|
|
463
|
+
|
|
431
464
|
createDataChannel(label: string, dataChannelDict: RTCDataChannelInit) {
|
|
432
465
|
return this.pc.createDataChannel(label, dataChannelDict);
|
|
433
466
|
}
|
|
@@ -703,6 +736,86 @@ function ensureAudioNackAndStereo(
|
|
|
703
736
|
}
|
|
704
737
|
}
|
|
705
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Returns the mids of transceivers that carry no outgoing track: the
|
|
741
|
+
* pre-populated placeholders added by `RTCEngine.applyInitialPublisherLayout`,
|
|
742
|
+
* plus any transceiver that was used for a track and reverted on unpublish. The
|
|
743
|
+
* `sender.track` check is the reliable signal — an unpublished section keeps its
|
|
744
|
+
* `a=msid` (and its stale send-derived fmtp), so it can't be told apart from a
|
|
745
|
+
* real send by SDP alone. Transceiver mids are stable across renegotiations, so
|
|
746
|
+
* this works for every offer/answer after the first.
|
|
747
|
+
* @internal
|
|
748
|
+
*/
|
|
749
|
+
export function placeholderMidsFromTransceivers(
|
|
750
|
+
transceivers: readonly RTCRtpTransceiver[],
|
|
751
|
+
): Set<string> {
|
|
752
|
+
const mids = new Set<string>();
|
|
753
|
+
for (const transceiver of transceivers) {
|
|
754
|
+
if (transceiver.mid && !transceiver.sender.track) {
|
|
755
|
+
mids.add(transceiver.mid);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return mids;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Within a BUNDLE group a payload type must map to identical codec parameters
|
|
763
|
+
* across every m-line. When the same payload type carries different fmtp between
|
|
764
|
+
* sections — e.g. opus `usedtx=1` on the published microphone but not on the
|
|
765
|
+
* pre-populated recvonly placeholders, or H.265 with different `level-id` between
|
|
766
|
+
* a published video track and a placeholder — libwebrtc flags a "bundled payload
|
|
767
|
+
* type collision".
|
|
768
|
+
*
|
|
769
|
+
* Rewrite the placeholder sections so every shared payload type carries the
|
|
770
|
+
* same fmtp. Real (non-placeholder) sections always win the canonical value, so
|
|
771
|
+
* a published track's encoder parameters are never altered. When no real
|
|
772
|
+
* section declares a payload type — e.g. a placeholder that was reused for a
|
|
773
|
+
* track and then reverted to recvonly keeps its send-derived `level-id` while
|
|
774
|
+
* fresh placeholders use the default — the placeholders still converge on the
|
|
775
|
+
* first value seen, so two placeholders can't disagree either. Only placeholder
|
|
776
|
+
* sections are ever rewritten, and it is codec-agnostic (opus, H.265, ...).
|
|
777
|
+
* `isPlaceholder` identifies the sections to conform.
|
|
778
|
+
* @internal
|
|
779
|
+
*/
|
|
780
|
+
export function conformBundledCodecFmtp(
|
|
781
|
+
media: MediaDescription[],
|
|
782
|
+
isPlaceholder: (media: MediaDescription) => boolean,
|
|
783
|
+
) {
|
|
784
|
+
// Canonical fmtp per payload type. Payload types are unique within a BUNDLE
|
|
785
|
+
// group, so keying by payload alone (across audio and video) is safe. A real
|
|
786
|
+
// section's value always takes precedence; otherwise the first placeholder
|
|
787
|
+
// value seen is used so divergent placeholders still converge.
|
|
788
|
+
const canonicalByPayload = new Map<number, string>();
|
|
789
|
+
const fromRealSection = new Set<number>();
|
|
790
|
+
for (const m of media) {
|
|
791
|
+
const placeholder = isPlaceholder(m);
|
|
792
|
+
for (const fmtp of m.fmtp ?? []) {
|
|
793
|
+
if (!placeholder) {
|
|
794
|
+
canonicalByPayload.set(fmtp.payload, fmtp.config);
|
|
795
|
+
fromRealSection.add(fmtp.payload);
|
|
796
|
+
} else if (!canonicalByPayload.has(fmtp.payload)) {
|
|
797
|
+
canonicalByPayload.set(fmtp.payload, fmtp.config);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (canonicalByPayload.size === 0) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Conform placeholder sections to the canonical fmtp for each shared payload.
|
|
806
|
+
for (const m of media) {
|
|
807
|
+
if (!isPlaceholder(m)) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
for (const fmtp of m.fmtp ?? []) {
|
|
811
|
+
const config = canonicalByPayload.get(fmtp.payload);
|
|
812
|
+
if (config !== undefined && fmtp.config !== config) {
|
|
813
|
+
fmtp.config = config;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
706
819
|
function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
|
|
707
820
|
stereoMids: string[];
|
|
708
821
|
nackMids: string[];
|
package/src/room/RTCEngine.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
} from '../frameMetadata/utils';
|
|
62
62
|
import log, { LoggerNames, getLogger } from '../logger';
|
|
63
63
|
import type { InternalRoomOptions } from '../options';
|
|
64
|
+
import type { NonSharedUint8Array } from '../type-polyfills/non-shared-typed-arrays';
|
|
64
65
|
import TypedPromise from '../utils/TypedPromise';
|
|
65
66
|
import { DataPacketBuffer } from '../utils/dataPacketBuffer';
|
|
66
67
|
import { TTLMap } from '../utils/ttlmap';
|
package/src/room/Room.ts
CHANGED
|
@@ -46,6 +46,7 @@ import type {
|
|
|
46
46
|
RoomConnectOptions,
|
|
47
47
|
RoomOptions,
|
|
48
48
|
} from '../options';
|
|
49
|
+
import type { NonSharedUint8Array } from '../type-polyfills/non-shared-typed-arrays';
|
|
49
50
|
import TypedPromise from '../utils/TypedPromise';
|
|
50
51
|
import { getBrowser } from '../utils/browserParser';
|
|
51
52
|
import { CLIENT_PROTOCOL_DEFAULT } from '../version';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
2
2
|
import { LoggerNames, getLogger } from '../../logger';
|
|
3
|
+
import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
|
|
3
4
|
import { LivekitReasonedError } from '../errors';
|
|
4
5
|
import { type DataTrackFrameInternal } from './frame';
|
|
5
6
|
import { DataTrackPacket, FrameMarker } from './packet';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
|
|
1
2
|
import { DataTrackExtensions, DataTrackUserTimestampExtension } from './packet/extensions';
|
|
2
3
|
|
|
3
4
|
/** A pair of payload bytes and packet extensions which can be fed into a {@link DataTrackPacketizer}. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
2
|
+
import type { NonSharedUint8Array } from '../../../type-polyfills/non-shared-typed-arrays';
|
|
2
3
|
import { coerceToDataView } from '../utils';
|
|
3
4
|
import { EXT_TAG_PADDING, U8_LENGTH_BYTES, U64_LENGTH_BYTES } from './constants';
|
|
4
5
|
import { DataTrackDeserializeError, DataTrackDeserializeErrorReason } from './errors';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
2
|
+
import type { NonSharedUint8Array } from '../../../type-polyfills/non-shared-typed-arrays';
|
|
2
3
|
import { DataTrackHandle, DataTrackHandleError, DataTrackHandleErrorReason } from '../handle';
|
|
3
4
|
import {
|
|
4
5
|
DataTrackTimestamp,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
2
|
+
import type { NonSharedUint8Array } from '../../../type-polyfills/non-shared-typed-arrays';
|
|
2
3
|
import { DataTrackSerializeError } from './errors';
|
|
3
4
|
|
|
4
5
|
/** An abstract class implementing common behavior related to data track binary serialization. */
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
isFrameMetadataSupported,
|
|
30
30
|
} from '../../frameMetadata/utils';
|
|
31
31
|
import type { InternalRoomOptions } from '../../options';
|
|
32
|
+
import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
|
|
32
33
|
import TypedPromise from '../../utils/TypedPromise';
|
|
33
34
|
import { PCTransportState } from '../PCTransportManager';
|
|
34
35
|
import type RTCEngine from '../RTCEngine';
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { EventEmitter } from 'events';
|
|
13
13
|
import type TypedEmitter from 'typed-emitter';
|
|
14
14
|
import log, { LoggerNames, type StructuredLogger, getLogger } from '../../logger';
|
|
15
|
+
import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
|
|
15
16
|
import { ParticipantEvent, TrackEvent } from '../events';
|
|
16
17
|
import type LocalTrackPublication from '../track/LocalTrackPublication';
|
|
17
18
|
import type LocalVideoTrack from '../track/LocalVideoTrack';
|
package/src/room/track/Track.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { EventEmitter } from 'events';
|
|
|
9
9
|
import type TypedEventEmitter from 'typed-emitter';
|
|
10
10
|
import type { SignalClient } from '../../api/SignalClient';
|
|
11
11
|
import log, { LoggerNames, type StructuredLogger, getLogger } from '../../logger';
|
|
12
|
+
import type { NonSharedUint8Array } from '../../type-polyfills/non-shared-typed-arrays';
|
|
12
13
|
import { TrackEvent } from '../events';
|
|
13
14
|
import type { LoggerOptions } from '../types';
|
|
14
15
|
import { isFireFox, isSafari, isWeb } from '../utils';
|
package/src/room/track/record.ts
CHANGED
package/src/room/utils.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Transcription as TranscriptionModel,
|
|
8
8
|
} from '@livekit/protocol';
|
|
9
9
|
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
10
|
+
import type { NonSharedUint8Array } from '../type-polyfills/non-shared-typed-arrays';
|
|
10
11
|
import TypedPromise from '../utils/TypedPromise';
|
|
11
12
|
import { getBrowser } from '../utils/browserParser';
|
|
12
13
|
import type { BrowserDetails } from '../utils/browserParser';
|
|
@@ -3,4 +3,9 @@
|
|
|
3
3
|
// only accept the non-shared variant `Uint8Array<ArrayBuffer>`. Using `ReturnType<typeof Uint8Array.from>`
|
|
4
4
|
// resolves to that non-shared variant on TS versions that support the generic, while remaining
|
|
5
5
|
// equivalent to plain `Uint8Array` on older versions — so this alias works across the range we support.
|
|
6
|
-
|
|
6
|
+
//
|
|
7
|
+
// Exported as a normal type (rather than an ambient global) so it is emitted into the published `dist`
|
|
8
|
+
// types and imported explicitly wherever it is used. An earlier ambient `.d.ts` declaration was resolved
|
|
9
|
+
// during our own build but never shipped, so consumers' type-checkers (and tools like API Extractor)
|
|
10
|
+
// failed with "Cannot find name 'NonSharedUint8Array'" on our emitted `.d.ts` files.
|
|
11
|
+
export type NonSharedUint8Array = ReturnType<typeof Uint8Array.from>;
|