livekit-client 0.16.6 → 0.17.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/dist/api/RequestQueue.js +6 -6
- package/dist/api/RequestQueue.js.map +1 -1
- package/dist/api/SignalClient.d.ts +3 -0
- package/dist/api/SignalClient.js +23 -3
- package/dist/api/SignalClient.js.map +1 -1
- package/dist/connect.js +1 -1
- package/dist/connect.js.map +1 -1
- package/dist/options.d.ts +7 -2
- package/dist/proto/livekit_models.d.ts +33 -0
- package/dist/proto/livekit_models.js +213 -3
- package/dist/proto/livekit_models.js.map +1 -1
- package/dist/proto/livekit_rtc.d.ts +15 -1
- package/dist/proto/livekit_rtc.js +128 -2
- package/dist/proto/livekit_rtc.js.map +1 -1
- package/dist/room/RTCEngine.d.ts +3 -2
- package/dist/room/RTCEngine.js +12 -8
- package/dist/room/RTCEngine.js.map +1 -1
- package/dist/room/Room.js +19 -8
- package/dist/room/Room.js.map +1 -1
- package/dist/room/events.d.ts +6 -1
- package/dist/room/events.js +6 -1
- package/dist/room/events.js.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts +3 -1
- package/dist/room/participant/LocalParticipant.js +17 -1
- package/dist/room/participant/LocalParticipant.js.map +1 -1
- package/dist/room/participant/RemoteParticipant.d.ts +2 -1
- package/dist/room/participant/RemoteParticipant.js +3 -3
- package/dist/room/participant/RemoteParticipant.js.map +1 -1
- package/dist/room/participant/publishUtils.d.ts +6 -0
- package/dist/room/participant/publishUtils.js +65 -24
- package/dist/room/participant/publishUtils.js.map +1 -1
- package/dist/room/participant/publishUtils.test.js +35 -5
- package/dist/room/participant/publishUtils.test.js.map +1 -1
- package/dist/room/track/LocalAudioTrack.d.ts +2 -0
- package/dist/room/track/LocalAudioTrack.js +23 -0
- package/dist/room/track/LocalAudioTrack.js.map +1 -1
- package/dist/room/track/LocalTrack.d.ts +4 -0
- package/dist/room/track/LocalTrack.js +34 -0
- package/dist/room/track/LocalTrack.js.map +1 -1
- package/dist/room/track/LocalVideoTrack.d.ts +1 -0
- package/dist/room/track/LocalVideoTrack.js +13 -0
- package/dist/room/track/LocalVideoTrack.js.map +1 -1
- package/dist/room/track/RemoteTrack.d.ts +1 -0
- package/dist/room/track/RemoteTrack.js +1 -0
- package/dist/room/track/RemoteTrack.js.map +1 -1
- package/dist/room/track/RemoteVideoTrack.d.ts +4 -2
- package/dist/room/track/RemoteVideoTrack.js +25 -11
- package/dist/room/track/RemoteVideoTrack.js.map +1 -1
- package/dist/room/track/Track.d.ts +4 -1
- package/dist/room/track/Track.js +20 -1
- package/dist/room/track/Track.js.map +1 -1
- package/dist/room/track/defaults.js +2 -2
- package/dist/room/track/defaults.js.map +1 -1
- package/dist/room/track/options.d.ts +65 -15
- package/dist/room/track/options.js +38 -0
- package/dist/room/track/options.js.map +1 -1
- package/dist/room/track/types.d.ts +11 -0
- package/dist/room/track/utils.d.ts +10 -0
- package/dist/room/track/utils.js +46 -1
- package/dist/room/track/utils.js.map +1 -1
- package/dist/room/utils.d.ts +1 -0
- package/dist/room/utils.js +5 -1
- package/dist/room/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/api/RequestQueue.ts +7 -7
- package/src/api/SignalClient.ts +31 -4
- package/src/connect.ts +1 -1
- package/src/options.ts +12 -3
- package/src/proto/livekit_models.ts +249 -0
- package/src/proto/livekit_rtc.ts +155 -0
- package/src/room/RTCEngine.ts +16 -9
- package/src/room/Room.ts +17 -7
- package/src/room/events.ts +6 -1
- package/src/room/participant/LocalParticipant.ts +23 -4
- package/src/room/participant/RemoteParticipant.ts +4 -4
- package/src/room/participant/publishUtils.test.ts +46 -6
- package/src/room/participant/publishUtils.ts +72 -27
- package/src/room/track/LocalAudioTrack.ts +19 -1
- package/src/room/track/LocalTrack.ts +36 -0
- package/src/room/track/LocalVideoTrack.ts +9 -1
- package/src/room/track/RemoteTrack.ts +2 -0
- package/src/room/track/RemoteVideoTrack.ts +20 -9
- package/src/room/track/Track.ts +14 -2
- package/src/room/track/defaults.ts +2 -2
- package/src/room/track/options.ts +55 -3
- package/src/room/track/types.ts +12 -0
- package/src/room/track/utils.ts +39 -0
- package/src/room/utils.ts +4 -0
package/src/proto/livekit_rtc.ts
CHANGED
@@ -6,6 +6,7 @@ import {
|
|
6
6
|
TrackSource,
|
7
7
|
Room,
|
8
8
|
ParticipantInfo,
|
9
|
+
ClientConfiguration,
|
9
10
|
TrackInfo,
|
10
11
|
VideoQuality,
|
11
12
|
ConnectionQuality,
|
@@ -192,6 +193,8 @@ export interface JoinResponse {
|
|
192
193
|
* when this is set, the other fields will be largely empty
|
193
194
|
*/
|
194
195
|
alternativeUrl: string;
|
196
|
+
clientConfiguration?: ClientConfiguration;
|
197
|
+
serverRegion: string;
|
195
198
|
}
|
196
199
|
|
197
200
|
export interface TrackPublishedResponse {
|
@@ -306,6 +309,12 @@ export interface SyncState {
|
|
306
309
|
answer?: SessionDescription;
|
307
310
|
subscription?: UpdateSubscription;
|
308
311
|
publishTracks: TrackPublishedResponse[];
|
312
|
+
dataChannels: DataChannelInfo[];
|
313
|
+
}
|
314
|
+
|
315
|
+
export interface DataChannelInfo {
|
316
|
+
label: string;
|
317
|
+
id: number;
|
309
318
|
}
|
310
319
|
|
311
320
|
export interface SimulateScenario {
|
@@ -1405,6 +1414,7 @@ const baseJoinResponse: object = {
|
|
1405
1414
|
serverVersion: "",
|
1406
1415
|
subscriberPrimary: false,
|
1407
1416
|
alternativeUrl: "",
|
1417
|
+
serverRegion: "",
|
1408
1418
|
};
|
1409
1419
|
|
1410
1420
|
export const JoinResponse = {
|
@@ -1436,6 +1446,15 @@ export const JoinResponse = {
|
|
1436
1446
|
if (message.alternativeUrl !== "") {
|
1437
1447
|
writer.uint32(58).string(message.alternativeUrl);
|
1438
1448
|
}
|
1449
|
+
if (message.clientConfiguration !== undefined) {
|
1450
|
+
ClientConfiguration.encode(
|
1451
|
+
message.clientConfiguration,
|
1452
|
+
writer.uint32(66).fork()
|
1453
|
+
).ldelim();
|
1454
|
+
}
|
1455
|
+
if (message.serverRegion !== "") {
|
1456
|
+
writer.uint32(74).string(message.serverRegion);
|
1457
|
+
}
|
1439
1458
|
return writer;
|
1440
1459
|
},
|
1441
1460
|
|
@@ -1471,6 +1490,15 @@ export const JoinResponse = {
|
|
1471
1490
|
case 7:
|
1472
1491
|
message.alternativeUrl = reader.string();
|
1473
1492
|
break;
|
1493
|
+
case 8:
|
1494
|
+
message.clientConfiguration = ClientConfiguration.decode(
|
1495
|
+
reader,
|
1496
|
+
reader.uint32()
|
1497
|
+
);
|
1498
|
+
break;
|
1499
|
+
case 9:
|
1500
|
+
message.serverRegion = reader.string();
|
1501
|
+
break;
|
1474
1502
|
default:
|
1475
1503
|
reader.skipType(tag & 7);
|
1476
1504
|
break;
|
@@ -1524,6 +1552,21 @@ export const JoinResponse = {
|
|
1524
1552
|
} else {
|
1525
1553
|
message.alternativeUrl = "";
|
1526
1554
|
}
|
1555
|
+
if (
|
1556
|
+
object.clientConfiguration !== undefined &&
|
1557
|
+
object.clientConfiguration !== null
|
1558
|
+
) {
|
1559
|
+
message.clientConfiguration = ClientConfiguration.fromJSON(
|
1560
|
+
object.clientConfiguration
|
1561
|
+
);
|
1562
|
+
} else {
|
1563
|
+
message.clientConfiguration = undefined;
|
1564
|
+
}
|
1565
|
+
if (object.serverRegion !== undefined && object.serverRegion !== null) {
|
1566
|
+
message.serverRegion = String(object.serverRegion);
|
1567
|
+
} else {
|
1568
|
+
message.serverRegion = "";
|
1569
|
+
}
|
1527
1570
|
return message;
|
1528
1571
|
},
|
1529
1572
|
|
@@ -1555,6 +1598,12 @@ export const JoinResponse = {
|
|
1555
1598
|
(obj.subscriberPrimary = message.subscriberPrimary);
|
1556
1599
|
message.alternativeUrl !== undefined &&
|
1557
1600
|
(obj.alternativeUrl = message.alternativeUrl);
|
1601
|
+
message.clientConfiguration !== undefined &&
|
1602
|
+
(obj.clientConfiguration = message.clientConfiguration
|
1603
|
+
? ClientConfiguration.toJSON(message.clientConfiguration)
|
1604
|
+
: undefined);
|
1605
|
+
message.serverRegion !== undefined &&
|
1606
|
+
(obj.serverRegion = message.serverRegion);
|
1558
1607
|
return obj;
|
1559
1608
|
},
|
1560
1609
|
|
@@ -1588,6 +1637,17 @@ export const JoinResponse = {
|
|
1588
1637
|
}
|
1589
1638
|
message.subscriberPrimary = object.subscriberPrimary ?? false;
|
1590
1639
|
message.alternativeUrl = object.alternativeUrl ?? "";
|
1640
|
+
if (
|
1641
|
+
object.clientConfiguration !== undefined &&
|
1642
|
+
object.clientConfiguration !== null
|
1643
|
+
) {
|
1644
|
+
message.clientConfiguration = ClientConfiguration.fromPartial(
|
1645
|
+
object.clientConfiguration
|
1646
|
+
);
|
1647
|
+
} else {
|
1648
|
+
message.clientConfiguration = undefined;
|
1649
|
+
}
|
1650
|
+
message.serverRegion = object.serverRegion ?? "";
|
1591
1651
|
return message;
|
1592
1652
|
},
|
1593
1653
|
};
|
@@ -3197,6 +3257,9 @@ export const SyncState = {
|
|
3197
3257
|
for (const v of message.publishTracks) {
|
3198
3258
|
TrackPublishedResponse.encode(v!, writer.uint32(26).fork()).ldelim();
|
3199
3259
|
}
|
3260
|
+
for (const v of message.dataChannels) {
|
3261
|
+
DataChannelInfo.encode(v!, writer.uint32(34).fork()).ldelim();
|
3262
|
+
}
|
3200
3263
|
return writer;
|
3201
3264
|
},
|
3202
3265
|
|
@@ -3205,6 +3268,7 @@ export const SyncState = {
|
|
3205
3268
|
let end = length === undefined ? reader.len : reader.pos + length;
|
3206
3269
|
const message = { ...baseSyncState } as SyncState;
|
3207
3270
|
message.publishTracks = [];
|
3271
|
+
message.dataChannels = [];
|
3208
3272
|
while (reader.pos < end) {
|
3209
3273
|
const tag = reader.uint32();
|
3210
3274
|
switch (tag >>> 3) {
|
@@ -3222,6 +3286,11 @@ export const SyncState = {
|
|
3222
3286
|
TrackPublishedResponse.decode(reader, reader.uint32())
|
3223
3287
|
);
|
3224
3288
|
break;
|
3289
|
+
case 4:
|
3290
|
+
message.dataChannels.push(
|
3291
|
+
DataChannelInfo.decode(reader, reader.uint32())
|
3292
|
+
);
|
3293
|
+
break;
|
3225
3294
|
default:
|
3226
3295
|
reader.skipType(tag & 7);
|
3227
3296
|
break;
|
@@ -3233,6 +3302,7 @@ export const SyncState = {
|
|
3233
3302
|
fromJSON(object: any): SyncState {
|
3234
3303
|
const message = { ...baseSyncState } as SyncState;
|
3235
3304
|
message.publishTracks = [];
|
3305
|
+
message.dataChannels = [];
|
3236
3306
|
if (object.answer !== undefined && object.answer !== null) {
|
3237
3307
|
message.answer = SessionDescription.fromJSON(object.answer);
|
3238
3308
|
} else {
|
@@ -3248,6 +3318,11 @@ export const SyncState = {
|
|
3248
3318
|
message.publishTracks.push(TrackPublishedResponse.fromJSON(e));
|
3249
3319
|
}
|
3250
3320
|
}
|
3321
|
+
if (object.dataChannels !== undefined && object.dataChannels !== null) {
|
3322
|
+
for (const e of object.dataChannels) {
|
3323
|
+
message.dataChannels.push(DataChannelInfo.fromJSON(e));
|
3324
|
+
}
|
3325
|
+
}
|
3251
3326
|
return message;
|
3252
3327
|
},
|
3253
3328
|
|
@@ -3268,6 +3343,13 @@ export const SyncState = {
|
|
3268
3343
|
} else {
|
3269
3344
|
obj.publishTracks = [];
|
3270
3345
|
}
|
3346
|
+
if (message.dataChannels) {
|
3347
|
+
obj.dataChannels = message.dataChannels.map((e) =>
|
3348
|
+
e ? DataChannelInfo.toJSON(e) : undefined
|
3349
|
+
);
|
3350
|
+
} else {
|
3351
|
+
obj.dataChannels = [];
|
3352
|
+
}
|
3271
3353
|
return obj;
|
3272
3354
|
},
|
3273
3355
|
|
@@ -3291,6 +3373,79 @@ export const SyncState = {
|
|
3291
3373
|
message.publishTracks.push(TrackPublishedResponse.fromPartial(e));
|
3292
3374
|
}
|
3293
3375
|
}
|
3376
|
+
message.dataChannels = [];
|
3377
|
+
if (object.dataChannels !== undefined && object.dataChannels !== null) {
|
3378
|
+
for (const e of object.dataChannels) {
|
3379
|
+
message.dataChannels.push(DataChannelInfo.fromPartial(e));
|
3380
|
+
}
|
3381
|
+
}
|
3382
|
+
return message;
|
3383
|
+
},
|
3384
|
+
};
|
3385
|
+
|
3386
|
+
const baseDataChannelInfo: object = { label: "", id: 0 };
|
3387
|
+
|
3388
|
+
export const DataChannelInfo = {
|
3389
|
+
encode(
|
3390
|
+
message: DataChannelInfo,
|
3391
|
+
writer: _m0.Writer = _m0.Writer.create()
|
3392
|
+
): _m0.Writer {
|
3393
|
+
if (message.label !== "") {
|
3394
|
+
writer.uint32(10).string(message.label);
|
3395
|
+
}
|
3396
|
+
if (message.id !== 0) {
|
3397
|
+
writer.uint32(16).uint32(message.id);
|
3398
|
+
}
|
3399
|
+
return writer;
|
3400
|
+
},
|
3401
|
+
|
3402
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): DataChannelInfo {
|
3403
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
3404
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
3405
|
+
const message = { ...baseDataChannelInfo } as DataChannelInfo;
|
3406
|
+
while (reader.pos < end) {
|
3407
|
+
const tag = reader.uint32();
|
3408
|
+
switch (tag >>> 3) {
|
3409
|
+
case 1:
|
3410
|
+
message.label = reader.string();
|
3411
|
+
break;
|
3412
|
+
case 2:
|
3413
|
+
message.id = reader.uint32();
|
3414
|
+
break;
|
3415
|
+
default:
|
3416
|
+
reader.skipType(tag & 7);
|
3417
|
+
break;
|
3418
|
+
}
|
3419
|
+
}
|
3420
|
+
return message;
|
3421
|
+
},
|
3422
|
+
|
3423
|
+
fromJSON(object: any): DataChannelInfo {
|
3424
|
+
const message = { ...baseDataChannelInfo } as DataChannelInfo;
|
3425
|
+
if (object.label !== undefined && object.label !== null) {
|
3426
|
+
message.label = String(object.label);
|
3427
|
+
} else {
|
3428
|
+
message.label = "";
|
3429
|
+
}
|
3430
|
+
if (object.id !== undefined && object.id !== null) {
|
3431
|
+
message.id = Number(object.id);
|
3432
|
+
} else {
|
3433
|
+
message.id = 0;
|
3434
|
+
}
|
3435
|
+
return message;
|
3436
|
+
},
|
3437
|
+
|
3438
|
+
toJSON(message: DataChannelInfo): unknown {
|
3439
|
+
const obj: any = {};
|
3440
|
+
message.label !== undefined && (obj.label = message.label);
|
3441
|
+
message.id !== undefined && (obj.id = message.id);
|
3442
|
+
return obj;
|
3443
|
+
},
|
3444
|
+
|
3445
|
+
fromPartial(object: DeepPartial<DataChannelInfo>): DataChannelInfo {
|
3446
|
+
const message = { ...baseDataChannelInfo } as DataChannelInfo;
|
3447
|
+
message.label = object.label ?? "";
|
3448
|
+
message.id = object.id ?? 0;
|
3294
3449
|
return message;
|
3295
3450
|
},
|
3296
3451
|
};
|
package/src/room/RTCEngine.ts
CHANGED
@@ -3,6 +3,8 @@ import type TypedEventEmitter from 'typed-emitter';
|
|
3
3
|
import { SignalClient, SignalOptions } from '../api/SignalClient';
|
4
4
|
import log from '../logger';
|
5
5
|
import {
|
6
|
+
ClientConfigSetting,
|
7
|
+
ClientConfiguration,
|
6
8
|
DataPacket, DataPacket_Kind, SpeakerInfo, TrackInfo, UserPacket,
|
7
9
|
} from '../proto/livekit_models';
|
8
10
|
import {
|
@@ -70,7 +72,9 @@ export default class RTCEngine extends (
|
|
70
72
|
|
71
73
|
private reconnectStart: number = 0;
|
72
74
|
|
73
|
-
private
|
75
|
+
private fullReconnectOnNext: boolean = false;
|
76
|
+
|
77
|
+
private clientConfiguration?: ClientConfiguration;
|
74
78
|
|
75
79
|
private connectedServerAddr?: string;
|
76
80
|
|
@@ -96,6 +100,7 @@ export default class RTCEngine extends (
|
|
96
100
|
if (!this.subscriberPrimary) {
|
97
101
|
this.negotiate();
|
98
102
|
}
|
103
|
+
this.clientConfiguration = joinResponse.clientConfiguration;
|
99
104
|
|
100
105
|
return joinResponse;
|
101
106
|
}
|
@@ -303,7 +308,7 @@ export default class RTCEngine extends (
|
|
303
308
|
|
304
309
|
this.client.onLeave = (leave?: LeaveRequest) => {
|
305
310
|
if (leave?.canReconnect) {
|
306
|
-
this.
|
311
|
+
this.fullReconnectOnNext = true;
|
307
312
|
this.primaryPC = undefined;
|
308
313
|
} else {
|
309
314
|
this.emit(EngineEvent.Disconnected);
|
@@ -376,19 +381,19 @@ export default class RTCEngine extends (
|
|
376
381
|
if (this.isClosed) {
|
377
382
|
return;
|
378
383
|
}
|
379
|
-
if (isFireFox()
|
380
|
-
|
381
|
-
this.
|
384
|
+
if (isFireFox() // TODO remove once clientConfiguration handles firefox case server side
|
385
|
+
|| this.clientConfiguration?.resumeConnection === ClientConfigSetting.DISABLED) {
|
386
|
+
this.fullReconnectOnNext = true;
|
382
387
|
}
|
383
388
|
|
384
389
|
try {
|
385
|
-
if (this.
|
390
|
+
if (this.fullReconnectOnNext) {
|
386
391
|
await this.restartConnection();
|
387
392
|
} else {
|
388
393
|
await this.resumeConnection();
|
389
394
|
}
|
390
395
|
this.reconnectAttempts = 0;
|
391
|
-
this.
|
396
|
+
this.fullReconnectOnNext = false;
|
392
397
|
} catch (e) {
|
393
398
|
this.reconnectAttempts += 1;
|
394
399
|
let recoverable = true;
|
@@ -398,7 +403,7 @@ export default class RTCEngine extends (
|
|
398
403
|
recoverable = false;
|
399
404
|
} else if (!(e instanceof SignalReconnectError)) {
|
400
405
|
// cannot resume
|
401
|
-
this.
|
406
|
+
this.fullReconnectOnNext = true;
|
402
407
|
}
|
403
408
|
|
404
409
|
const duration = Date.now() - this.reconnectStart;
|
@@ -444,6 +449,7 @@ export default class RTCEngine extends (
|
|
444
449
|
}
|
445
450
|
|
446
451
|
await this.waitForPCConnected();
|
452
|
+
this.client.setReconnected();
|
447
453
|
|
448
454
|
// reconnect success
|
449
455
|
this.emit(EngineEvent.Restarted, joinResponse);
|
@@ -478,6 +484,7 @@ export default class RTCEngine extends (
|
|
478
484
|
}
|
479
485
|
|
480
486
|
await this.waitForPCConnected();
|
487
|
+
this.client.setReconnected();
|
481
488
|
|
482
489
|
// resume success
|
483
490
|
this.emit(EngineEvent.Resumed);
|
@@ -564,7 +571,7 @@ export default class RTCEngine extends (
|
|
564
571
|
this.publisher.negotiate();
|
565
572
|
}
|
566
573
|
|
567
|
-
|
574
|
+
dataChannelForKind(kind: DataPacket_Kind): RTCDataChannel | undefined {
|
568
575
|
if (kind === DataPacket_Kind.LOSSY) {
|
569
576
|
return this.lossyDC;
|
570
577
|
} if (kind === DataPacket_Kind.RELIABLE) {
|
package/src/room/Room.ts
CHANGED
@@ -28,7 +28,8 @@ import LocalTrackPublication from './track/LocalTrackPublication';
|
|
28
28
|
import RemoteTrackPublication from './track/RemoteTrackPublication';
|
29
29
|
import { Track } from './track/Track';
|
30
30
|
import { TrackPublication } from './track/TrackPublication';
|
31
|
-
import { RemoteTrack } from './track/types';
|
31
|
+
import { AdaptiveStreamSettings, RemoteTrack } from './track/types';
|
32
|
+
import { getNewAudioContext } from './track/utils';
|
32
33
|
import { unpackStreamId } from './utils';
|
33
34
|
|
34
35
|
export enum RoomState {
|
@@ -195,7 +196,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
195
196
|
|
196
197
|
try {
|
197
198
|
const joinResponse = await this.engine.join(url, token, opts);
|
198
|
-
log.debug(
|
199
|
+
log.debug(`connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`);
|
199
200
|
|
200
201
|
if (!joinResponse.serverVersion) {
|
201
202
|
throw new UnsupportedServer('unknown server version');
|
@@ -437,12 +438,20 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
437
438
|
if (!trackId || trackId === '') trackId = mediaTrack.id;
|
438
439
|
|
439
440
|
const participant = this.getOrCreateParticipant(participantId);
|
441
|
+
let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
|
442
|
+
if (this.options.adaptiveStream) {
|
443
|
+
if (typeof this.options.adaptiveStream === 'object') {
|
444
|
+
adaptiveStreamSettings = this.options.adaptiveStream;
|
445
|
+
} else {
|
446
|
+
adaptiveStreamSettings = {};
|
447
|
+
}
|
448
|
+
}
|
440
449
|
participant.addSubscribedMediaTrack(
|
441
450
|
mediaTrack,
|
442
451
|
trackId,
|
443
452
|
stream,
|
444
453
|
receiver,
|
445
|
-
|
454
|
+
adaptiveStreamSettings,
|
446
455
|
);
|
447
456
|
}
|
448
457
|
|
@@ -458,6 +467,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
458
467
|
};
|
459
468
|
|
460
469
|
private handleRestarted = async (joinResponse: JoinResponse) => {
|
470
|
+
log.debug('reconnected to server region', joinResponse.serverRegion);
|
461
471
|
this.state = RoomState.Connected;
|
462
472
|
this.emit(RoomEvent.Reconnected);
|
463
473
|
this.emit(RoomEvent.StateChanged, this.state);
|
@@ -717,10 +727,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
717
727
|
}
|
718
728
|
// by using an AudioContext, it reduces lag on audio elements
|
719
729
|
// https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
this.audioContext = new AudioContext();
|
730
|
+
const ctx = getNewAudioContext();
|
731
|
+
if (ctx) {
|
732
|
+
this.audioContext = ctx;
|
724
733
|
}
|
725
734
|
}
|
726
735
|
|
@@ -826,6 +835,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
826
835
|
participantTracks: [],
|
827
836
|
},
|
828
837
|
publishTracks: this.localParticipant.publishedTracksInfo(),
|
838
|
+
dataChannels: this.localParticipant.dataChannelsInfo(),
|
829
839
|
});
|
830
840
|
}
|
831
841
|
|
package/src/room/events.ts
CHANGED
@@ -185,7 +185,7 @@ export enum RoomEvent {
|
|
185
185
|
ConnectionQualityChanged = 'connectionQualityChanged',
|
186
186
|
|
187
187
|
/**
|
188
|
-
* StreamState indicates if a subscribed track has been paused by the SFU
|
188
|
+
* StreamState indicates if a subscribed (remote) track has been paused by the SFU
|
189
189
|
* (typically this happens because of subscriber's bandwidth constraints)
|
190
190
|
*
|
191
191
|
* When bandwidth conditions allow, the track will be resumed automatically.
|
@@ -400,6 +400,11 @@ export enum TrackEvent {
|
|
400
400
|
AudioPlaybackStarted = 'audioPlaybackStarted',
|
401
401
|
/** @internal */
|
402
402
|
AudioPlaybackFailed = 'audioPlaybackFailed',
|
403
|
+
/**
|
404
|
+
* @internal
|
405
|
+
* Only fires on LocalAudioTrack instances
|
406
|
+
*/
|
407
|
+
AudioSilenceDetected = 'audioSilenceDetected',
|
403
408
|
/** @internal */
|
404
409
|
VisibilityChanged = 'visibilityChanged',
|
405
410
|
/** @internal */
|
@@ -3,7 +3,9 @@ import { RoomOptions } from '../../options';
|
|
3
3
|
import {
|
4
4
|
DataPacket, DataPacket_Kind,
|
5
5
|
} from '../../proto/livekit_models';
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
AddTrackRequest, DataChannelInfo, SubscribedQualityUpdate, TrackPublishedResponse,
|
8
|
+
} from '../../proto/livekit_rtc';
|
7
9
|
import {
|
8
10
|
TrackInvalidError,
|
9
11
|
UnexpectedConnectionState,
|
@@ -17,7 +19,8 @@ import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTr
|
|
17
19
|
import {
|
18
20
|
CreateLocalTracksOptions,
|
19
21
|
ScreenShareCaptureOptions,
|
20
|
-
|
22
|
+
ScreenSharePresets,
|
23
|
+
TrackPublishOptions, VideoCodec,
|
21
24
|
} from '../track/options';
|
22
25
|
import { Track } from '../track/Track';
|
23
26
|
import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
|
@@ -272,7 +275,7 @@ export default class LocalParticipant extends Participant {
|
|
272
275
|
options = {};
|
273
276
|
}
|
274
277
|
if (options.resolution === undefined) {
|
275
|
-
options.resolution =
|
278
|
+
options.resolution = ScreenSharePresets.h720fps15.resolution;
|
276
279
|
}
|
277
280
|
|
278
281
|
let videoConstraints: MediaTrackConstraints | boolean = true;
|
@@ -525,7 +528,7 @@ export default class LocalParticipant extends Participant {
|
|
525
528
|
destination?: RemoteParticipant[] | string[]) {
|
526
529
|
const dest: string[] = [];
|
527
530
|
if (destination !== undefined) {
|
528
|
-
destination.forEach((val
|
531
|
+
destination.forEach((val: any) => {
|
529
532
|
if (val instanceof RemoteParticipant) {
|
530
533
|
dest.push(val.sid);
|
531
534
|
} else {
|
@@ -681,4 +684,20 @@ export default class LocalParticipant extends Participant {
|
|
681
684
|
});
|
682
685
|
return infos;
|
683
686
|
}
|
687
|
+
|
688
|
+
/** @internal */
|
689
|
+
dataChannelsInfo(): DataChannelInfo[] {
|
690
|
+
const infos: DataChannelInfo[] = [];
|
691
|
+
const getInfo = (dc: RTCDataChannel | undefined) => {
|
692
|
+
if (dc?.id !== undefined && dc.id !== null) {
|
693
|
+
infos.push({
|
694
|
+
label: dc.label,
|
695
|
+
id: dc.id,
|
696
|
+
});
|
697
|
+
}
|
698
|
+
};
|
699
|
+
getInfo(this.engine.dataChannelForKind(DataPacket_Kind.LOSSY));
|
700
|
+
getInfo(this.engine.dataChannelForKind(DataPacket_Kind.RELIABLE));
|
701
|
+
return infos;
|
702
|
+
}
|
684
703
|
}
|
@@ -10,7 +10,7 @@ import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
|
10
10
|
import RemoteTrackPublication from '../track/RemoteTrackPublication';
|
11
11
|
import RemoteVideoTrack from '../track/RemoteVideoTrack';
|
12
12
|
import { Track } from '../track/Track';
|
13
|
-
import { RemoteTrack } from '../track/types';
|
13
|
+
import { AdaptiveStreamSettings, RemoteTrack } from '../track/types';
|
14
14
|
import Participant, { ParticipantEventCallbacks } from './Participant';
|
15
15
|
|
16
16
|
export default class RemoteParticipant extends Participant {
|
@@ -82,7 +82,7 @@ export default class RemoteParticipant extends Participant {
|
|
82
82
|
sid: Track.SID,
|
83
83
|
mediaStream: MediaStream,
|
84
84
|
receiver?: RTCRtpReceiver,
|
85
|
-
|
85
|
+
adaptiveStreamSettings?: AdaptiveStreamSettings,
|
86
86
|
triesLeft?: number,
|
87
87
|
) {
|
88
88
|
// find the track publication
|
@@ -114,7 +114,7 @@ export default class RemoteParticipant extends Participant {
|
|
114
114
|
if (triesLeft === undefined) triesLeft = 20;
|
115
115
|
setTimeout(() => {
|
116
116
|
this.addSubscribedMediaTrack(mediaTrack, sid, mediaStream,
|
117
|
-
receiver,
|
117
|
+
receiver, adaptiveStreamSettings, triesLeft! - 1);
|
118
118
|
}, 150);
|
119
119
|
return;
|
120
120
|
}
|
@@ -122,7 +122,7 @@ export default class RemoteParticipant extends Participant {
|
|
122
122
|
const isVideo = mediaTrack.kind === 'video';
|
123
123
|
let track: RemoteTrack;
|
124
124
|
if (isVideo) {
|
125
|
-
track = new RemoteVideoTrack(mediaTrack, sid, receiver,
|
125
|
+
track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
|
126
126
|
} else {
|
127
127
|
track = new RemoteAudioTrack(mediaTrack, sid, receiver);
|
128
128
|
}
|
@@ -1,11 +1,15 @@
|
|
1
|
-
import { VideoPresets, VideoPresets43 } from '../track/options';
|
2
1
|
import {
|
2
|
+
ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43,
|
3
|
+
} from '../track/options';
|
4
|
+
import {
|
5
|
+
computeDefaultScreenShareSimulcastPresets,
|
3
6
|
computeVideoEncodings,
|
4
7
|
determineAppropriateEncoding,
|
5
8
|
presets169,
|
6
9
|
presets43,
|
7
10
|
presetsForResolution,
|
8
11
|
presetsScreenShare,
|
12
|
+
sortPresets,
|
9
13
|
} from './publishUtils';
|
10
14
|
|
11
15
|
describe('presetsForResolution', () => {
|
@@ -63,7 +67,7 @@ describe('computeVideoEncodings', () => {
|
|
63
67
|
|
64
68
|
// ensure they are what we expect
|
65
69
|
expect(encodings![0].rid).toBe('q');
|
66
|
-
expect(encodings![0].maxBitrate).toBe(VideoPresets.
|
70
|
+
expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
|
67
71
|
expect(encodings![0].scaleResolutionDownBy).toBe(3);
|
68
72
|
expect(encodings![1].rid).toBe('h');
|
69
73
|
expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
|
@@ -77,7 +81,7 @@ describe('computeVideoEncodings', () => {
|
|
77
81
|
expect(encodings).toHaveLength(3);
|
78
82
|
expect(encodings![0].scaleResolutionDownBy).toBe(3);
|
79
83
|
expect(encodings![1].scaleResolutionDownBy).toBe(1.5);
|
80
|
-
expect(encodings![2].maxBitrate).toBe(VideoPresets.
|
84
|
+
expect(encodings![2].maxBitrate).toBe(VideoPresets.h540.encoding.maxBitrate);
|
81
85
|
});
|
82
86
|
|
83
87
|
it('returns two encodings for lower-res simulcast', () => {
|
@@ -88,9 +92,9 @@ describe('computeVideoEncodings', () => {
|
|
88
92
|
|
89
93
|
// ensure they are what we expect
|
90
94
|
expect(encodings![0].rid).toBe('q');
|
91
|
-
expect(encodings![0].maxBitrate).toBe(VideoPresets.
|
95
|
+
expect(encodings![0].maxBitrate).toBe(VideoPresets.h180.encoding.maxBitrate);
|
92
96
|
expect(encodings![1].rid).toBe('h');
|
93
|
-
expect(encodings![1].maxBitrate).toBe(VideoPresets.
|
97
|
+
expect(encodings![1].maxBitrate).toBe(VideoPresets.h360.encoding.maxBitrate);
|
94
98
|
});
|
95
99
|
|
96
100
|
it('respects provided min resolution', () => {
|
@@ -99,7 +103,43 @@ describe('computeVideoEncodings', () => {
|
|
99
103
|
});
|
100
104
|
expect(encodings).toHaveLength(1);
|
101
105
|
expect(encodings![0].rid).toBe('q');
|
102
|
-
expect(encodings![0].maxBitrate).toBe(VideoPresets43.
|
106
|
+
expect(encodings![0].maxBitrate).toBe(VideoPresets43.h120.encoding.maxBitrate);
|
103
107
|
expect(encodings![0].scaleResolutionDownBy).toBe(1);
|
104
108
|
});
|
105
109
|
});
|
110
|
+
|
111
|
+
describe('customSimulcastLayers', () => {
|
112
|
+
it('sorts presets from lowest to highest', () => {
|
113
|
+
const sortedPresets = sortPresets(
|
114
|
+
[VideoPresets.h1440, VideoPresets.h360, VideoPresets.h1080, VideoPresets.h90],
|
115
|
+
) as Array<VideoPreset>;
|
116
|
+
expect(sortPresets).not.toBeUndefined();
|
117
|
+
expect(sortedPresets[0]).toBe(VideoPresets.h90);
|
118
|
+
expect(sortedPresets[1]).toBe(VideoPresets.h360);
|
119
|
+
expect(sortedPresets[2]).toBe(VideoPresets.h1080);
|
120
|
+
expect(sortedPresets[3]).toBe(VideoPresets.h1440);
|
121
|
+
});
|
122
|
+
it('sorts presets from lowest to highest, even when dimensions are the same', () => {
|
123
|
+
const sortedPresets = sortPresets([
|
124
|
+
new VideoPreset(1920, 1080, 3_000_000, 20),
|
125
|
+
new VideoPreset(1920, 1080, 2_000_000, 15),
|
126
|
+
new VideoPreset(1920, 1080, 3_000_000, 15),
|
127
|
+
]) as Array<VideoPreset>;
|
128
|
+
expect(sortPresets).not.toBeUndefined();
|
129
|
+
expect(sortedPresets[0].encoding.maxBitrate).toBe(2_000_000);
|
130
|
+
expect(sortedPresets[1].encoding.maxFramerate).toBe(15);
|
131
|
+
expect(sortedPresets[2].encoding.maxFramerate).toBe(20);
|
132
|
+
});
|
133
|
+
});
|
134
|
+
|
135
|
+
describe('screenShareSimulcastDefaults', () => {
|
136
|
+
it('computes appropriate bitrate from original preset', () => {
|
137
|
+
const defaultSimulcastLayers = computeDefaultScreenShareSimulcastPresets(
|
138
|
+
ScreenSharePresets.h720fps15,
|
139
|
+
);
|
140
|
+
expect(defaultSimulcastLayers[0].width).toBe(640);
|
141
|
+
expect(defaultSimulcastLayers[0].height).toBe(360);
|
142
|
+
expect(defaultSimulcastLayers[0].encoding.maxFramerate).toBe(3);
|
143
|
+
expect(defaultSimulcastLayers[0].encoding.maxBitrate).toBe(150_000);
|
144
|
+
});
|
145
|
+
});
|