@stream-io/video-client 1.50.0 → 1.52.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/CHANGELOG.md +24 -0
- package/dist/index.browser.es.js +597 -70
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +597 -69
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +597 -70
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +1 -0
- package/dist/src/devices/CameraManager.d.ts +1 -0
- package/dist/src/devices/DeviceManager.d.ts +20 -0
- package/dist/src/devices/VirtualDevice.d.ts +59 -0
- package/dist/src/devices/devicePersistence.d.ts +1 -1
- package/dist/src/devices/index.d.ts +1 -0
- package/dist/src/gen/video/sfu/event/events.d.ts +4 -0
- package/dist/src/gen/video/sfu/models/models.d.ts +204 -2
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +9 -1
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +67 -1
- package/dist/src/helpers/participantUtils.d.ts +10 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +8 -3
- package/dist/src/rtc/Publisher.d.ts +21 -3
- package/dist/src/rtc/TransceiverCache.d.ts +5 -1
- package/dist/src/rtc/helpers/degradationPreference.d.ts +1 -0
- package/dist/src/rtc/types.d.ts +3 -0
- package/dist/src/stats/rtc/StatsTracer.d.ts +2 -1
- package/dist/src/stats/utils.d.ts +1 -0
- package/package.json +14 -14
- package/src/Call.ts +27 -12
- package/src/devices/CameraManager.ts +9 -2
- package/src/devices/DeviceManager.ts +148 -8
- package/src/devices/DeviceManagerState.ts +4 -1
- package/src/devices/VirtualDevice.ts +69 -0
- package/src/devices/__tests__/CameraManager.test.ts +22 -1
- package/src/devices/__tests__/DeviceManager.test.ts +124 -2
- package/src/devices/__tests__/MicrophoneManager.test.ts +3 -1
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +3 -1
- package/src/devices/__tests__/ScreenShareManager.test.ts +3 -1
- package/src/devices/__tests__/web-audio.mocks.ts +3 -1
- package/src/devices/devicePersistence.ts +2 -1
- package/src/devices/index.ts +1 -0
- package/src/gen/video/sfu/event/events.ts +10 -0
- package/src/gen/video/sfu/models/models.ts +338 -0
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +28 -2
- package/src/gen/video/sfu/signal_rpc/signal.ts +121 -15
- package/src/helpers/__tests__/DynascaleManager.test.ts +8 -7
- package/src/helpers/__tests__/browsers.test.ts +4 -4
- package/src/helpers/__tests__/participantUtils.test.ts +47 -0
- package/src/helpers/client-details.ts +4 -1
- package/src/helpers/participantUtils.ts +15 -0
- package/src/rtc/BasePeerConnection.ts +22 -4
- package/src/rtc/Publisher.ts +140 -41
- package/src/rtc/Subscriber.ts +1 -0
- package/src/rtc/TransceiverCache.ts +10 -3
- package/src/rtc/__tests__/Call.reconnect.test.ts +121 -2
- package/src/rtc/__tests__/Publisher.test.ts +659 -112
- package/src/rtc/__tests__/Subscriber.test.ts +7 -3
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +16 -15
- package/src/rtc/helpers/__tests__/degradationPreference.test.ts +33 -1
- package/src/rtc/helpers/degradationPreference.ts +18 -0
- package/src/rtc/types.ts +3 -0
- package/src/stats/rtc/StatsTracer.ts +25 -4
- package/src/stats/rtc/__tests__/StatsTracer.test.ts +155 -0
package/dist/index.es.js
CHANGED
|
@@ -1187,6 +1187,14 @@ var SdkType;
|
|
|
1187
1187
|
* @generated from protobuf enum value: SDK_TYPE_PLAIN_JAVASCRIPT = 9;
|
|
1188
1188
|
*/
|
|
1189
1189
|
SdkType[SdkType["PLAIN_JAVASCRIPT"] = 9] = "PLAIN_JAVASCRIPT";
|
|
1190
|
+
/**
|
|
1191
|
+
* @generated from protobuf enum value: SDK_TYPE_PYTHON = 10;
|
|
1192
|
+
*/
|
|
1193
|
+
SdkType[SdkType["PYTHON"] = 10] = "PYTHON";
|
|
1194
|
+
/**
|
|
1195
|
+
* @generated from protobuf enum value: SDK_TYPE_VISION_AGENTS = 11;
|
|
1196
|
+
*/
|
|
1197
|
+
SdkType[SdkType["VISION_AGENTS"] = 11] = "VISION_AGENTS";
|
|
1190
1198
|
})(SdkType || (SdkType = {}));
|
|
1191
1199
|
/**
|
|
1192
1200
|
* @generated from protobuf enum stream.video.sfu.models.TrackUnpublishReason
|
|
@@ -1398,6 +1406,12 @@ var ClientCapability;
|
|
|
1398
1406
|
* @generated from protobuf enum value: CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE = 1;
|
|
1399
1407
|
*/
|
|
1400
1408
|
ClientCapability[ClientCapability["SUBSCRIBER_VIDEO_PAUSE"] = 1] = "SUBSCRIBER_VIDEO_PAUSE";
|
|
1409
|
+
/**
|
|
1410
|
+
* Instructs SFU that stats will be sent to the coordinator
|
|
1411
|
+
*
|
|
1412
|
+
* @generated from protobuf enum value: CLIENT_CAPABILITY_COORDINATOR_STATS = 2;
|
|
1413
|
+
*/
|
|
1414
|
+
ClientCapability[ClientCapability["COORDINATOR_STATS"] = 2] = "COORDINATOR_STATS";
|
|
1401
1415
|
})(ClientCapability || (ClientCapability = {}));
|
|
1402
1416
|
/**
|
|
1403
1417
|
* DegradationPreference represents the RTCDegradationPreference from WebRTC.
|
|
@@ -1863,6 +1877,12 @@ class ClientDetails$Type extends MessageType {
|
|
|
1863
1877
|
{ no: 2, name: 'os', kind: 'message', T: () => OS },
|
|
1864
1878
|
{ no: 3, name: 'browser', kind: 'message', T: () => Browser },
|
|
1865
1879
|
{ no: 4, name: 'device', kind: 'message', T: () => Device },
|
|
1880
|
+
{
|
|
1881
|
+
no: 5,
|
|
1882
|
+
name: 'webrtc_version',
|
|
1883
|
+
kind: 'scalar',
|
|
1884
|
+
T: 9 /*ScalarType.STRING*/,
|
|
1885
|
+
},
|
|
1866
1886
|
]);
|
|
1867
1887
|
}
|
|
1868
1888
|
}
|
|
@@ -2135,6 +2155,171 @@ class PerformanceStats$Type extends MessageType {
|
|
|
2135
2155
|
* @generated MessageType for protobuf message stream.video.sfu.models.PerformanceStats
|
|
2136
2156
|
*/
|
|
2137
2157
|
const PerformanceStats = new PerformanceStats$Type();
|
|
2158
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2159
|
+
class RtpBase$Type extends MessageType {
|
|
2160
|
+
constructor() {
|
|
2161
|
+
super('stream.video.sfu.models.RtpBase', [
|
|
2162
|
+
{ no: 1, name: 'ssrc', kind: 'scalar', T: 13 /*ScalarType.UINT32*/ },
|
|
2163
|
+
{ no: 2, name: 'kind', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
2164
|
+
{
|
|
2165
|
+
no: 3,
|
|
2166
|
+
name: 'timestamp_ms',
|
|
2167
|
+
kind: 'scalar',
|
|
2168
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2169
|
+
},
|
|
2170
|
+
]);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.RtpBase
|
|
2175
|
+
*/
|
|
2176
|
+
const RtpBase = new RtpBase$Type();
|
|
2177
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2178
|
+
class InboundRtp$Type extends MessageType {
|
|
2179
|
+
constructor() {
|
|
2180
|
+
super('stream.video.sfu.models.InboundRtp', [
|
|
2181
|
+
{ no: 1, name: 'base', kind: 'message', T: () => RtpBase },
|
|
2182
|
+
{
|
|
2183
|
+
no: 2,
|
|
2184
|
+
name: 'jitter_seconds',
|
|
2185
|
+
kind: 'scalar',
|
|
2186
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2187
|
+
},
|
|
2188
|
+
{
|
|
2189
|
+
no: 3,
|
|
2190
|
+
name: 'packets_received',
|
|
2191
|
+
kind: 'scalar',
|
|
2192
|
+
T: 4 /*ScalarType.UINT64*/,
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
no: 4,
|
|
2196
|
+
name: 'packets_lost',
|
|
2197
|
+
kind: 'scalar',
|
|
2198
|
+
T: 4 /*ScalarType.UINT64*/,
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
no: 5,
|
|
2202
|
+
name: 'packet_loss_percent',
|
|
2203
|
+
kind: 'scalar',
|
|
2204
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
no: 10,
|
|
2208
|
+
name: 'concealment_events',
|
|
2209
|
+
kind: 'scalar',
|
|
2210
|
+
T: 13 /*ScalarType.UINT32*/,
|
|
2211
|
+
},
|
|
2212
|
+
{
|
|
2213
|
+
no: 11,
|
|
2214
|
+
name: 'concealment_percent',
|
|
2215
|
+
kind: 'scalar',
|
|
2216
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2217
|
+
},
|
|
2218
|
+
{ no: 20, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
|
|
2219
|
+
{
|
|
2220
|
+
no: 21,
|
|
2221
|
+
name: 'freeze_duration_seconds',
|
|
2222
|
+
kind: 'scalar',
|
|
2223
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
no: 22,
|
|
2227
|
+
name: 'avg_decode_time_seconds',
|
|
2228
|
+
kind: 'scalar',
|
|
2229
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
no: 23,
|
|
2233
|
+
name: 'min_dimension_px',
|
|
2234
|
+
kind: 'scalar',
|
|
2235
|
+
T: 13 /*ScalarType.UINT32*/,
|
|
2236
|
+
},
|
|
2237
|
+
]);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2241
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.InboundRtp
|
|
2242
|
+
*/
|
|
2243
|
+
const InboundRtp = new InboundRtp$Type();
|
|
2244
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2245
|
+
class OutboundRtp$Type extends MessageType {
|
|
2246
|
+
constructor() {
|
|
2247
|
+
super('stream.video.sfu.models.OutboundRtp', [
|
|
2248
|
+
{ no: 1, name: 'base', kind: 'message', T: () => RtpBase },
|
|
2249
|
+
{ no: 10, name: 'fps', kind: 'scalar', T: 1 /*ScalarType.DOUBLE*/ },
|
|
2250
|
+
{
|
|
2251
|
+
no: 11,
|
|
2252
|
+
name: 'avg_encode_time_seconds',
|
|
2253
|
+
kind: 'scalar',
|
|
2254
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2255
|
+
},
|
|
2256
|
+
{
|
|
2257
|
+
no: 12,
|
|
2258
|
+
name: 'bitrate_bps',
|
|
2259
|
+
kind: 'scalar',
|
|
2260
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2261
|
+
},
|
|
2262
|
+
{
|
|
2263
|
+
no: 13,
|
|
2264
|
+
name: 'min_dimension_px',
|
|
2265
|
+
kind: 'scalar',
|
|
2266
|
+
T: 13 /*ScalarType.UINT32*/,
|
|
2267
|
+
},
|
|
2268
|
+
]);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.OutboundRtp
|
|
2273
|
+
*/
|
|
2274
|
+
const OutboundRtp = new OutboundRtp$Type();
|
|
2275
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2276
|
+
class RemoteInboundRtp$Type extends MessageType {
|
|
2277
|
+
constructor() {
|
|
2278
|
+
super('stream.video.sfu.models.RemoteInboundRtp', [
|
|
2279
|
+
{ no: 1, name: 'base', kind: 'message', T: () => RtpBase },
|
|
2280
|
+
{
|
|
2281
|
+
no: 2,
|
|
2282
|
+
name: 'jitter_seconds',
|
|
2283
|
+
kind: 'scalar',
|
|
2284
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2285
|
+
},
|
|
2286
|
+
{
|
|
2287
|
+
no: 3,
|
|
2288
|
+
name: 'round_trip_time_s',
|
|
2289
|
+
kind: 'scalar',
|
|
2290
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2291
|
+
},
|
|
2292
|
+
]);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.RemoteInboundRtp
|
|
2297
|
+
*/
|
|
2298
|
+
const RemoteInboundRtp = new RemoteInboundRtp$Type();
|
|
2299
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2300
|
+
class RemoteOutboundRtp$Type extends MessageType {
|
|
2301
|
+
constructor() {
|
|
2302
|
+
super('stream.video.sfu.models.RemoteOutboundRtp', [
|
|
2303
|
+
{ no: 1, name: 'base', kind: 'message', T: () => RtpBase },
|
|
2304
|
+
{
|
|
2305
|
+
no: 2,
|
|
2306
|
+
name: 'jitter_seconds',
|
|
2307
|
+
kind: 'scalar',
|
|
2308
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2309
|
+
},
|
|
2310
|
+
{
|
|
2311
|
+
no: 3,
|
|
2312
|
+
name: 'round_trip_time_s',
|
|
2313
|
+
kind: 'scalar',
|
|
2314
|
+
T: 1 /*ScalarType.DOUBLE*/,
|
|
2315
|
+
},
|
|
2316
|
+
]);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.RemoteOutboundRtp
|
|
2321
|
+
*/
|
|
2322
|
+
const RemoteOutboundRtp = new RemoteOutboundRtp$Type();
|
|
2138
2323
|
|
|
2139
2324
|
var models = /*#__PURE__*/Object.freeze({
|
|
2140
2325
|
__proto__: null,
|
|
@@ -2159,8 +2344,10 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
2159
2344
|
get ErrorCode () { return ErrorCode; },
|
|
2160
2345
|
get GoAwayReason () { return GoAwayReason; },
|
|
2161
2346
|
ICETrickle: ICETrickle$1,
|
|
2347
|
+
InboundRtp: InboundRtp,
|
|
2162
2348
|
InputDevices: InputDevices,
|
|
2163
2349
|
OS: OS,
|
|
2350
|
+
OutboundRtp: OutboundRtp,
|
|
2164
2351
|
Participant: Participant,
|
|
2165
2352
|
ParticipantCount: ParticipantCount,
|
|
2166
2353
|
get ParticipantSource () { return ParticipantSource; },
|
|
@@ -2169,6 +2356,9 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
2169
2356
|
Pin: Pin,
|
|
2170
2357
|
PublishOption: PublishOption,
|
|
2171
2358
|
RTMPIngress: RTMPIngress,
|
|
2359
|
+
RemoteInboundRtp: RemoteInboundRtp,
|
|
2360
|
+
RemoteOutboundRtp: RemoteOutboundRtp,
|
|
2361
|
+
RtpBase: RtpBase,
|
|
2172
2362
|
Sdk: Sdk,
|
|
2173
2363
|
get SdkType () { return SdkType; },
|
|
2174
2364
|
StreamQuality: StreamQuality,
|
|
@@ -2286,6 +2476,62 @@ class Telemetry$Type extends MessageType {
|
|
|
2286
2476
|
*/
|
|
2287
2477
|
const Telemetry = new Telemetry$Type();
|
|
2288
2478
|
// @generated message type with reflection information, may provide speed optimized methods
|
|
2479
|
+
class SendMetricsRequest$Type extends MessageType {
|
|
2480
|
+
constructor() {
|
|
2481
|
+
super('stream.video.sfu.signal.SendMetricsRequest', [
|
|
2482
|
+
{ no: 1, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
2483
|
+
{
|
|
2484
|
+
no: 2,
|
|
2485
|
+
name: 'unified_session_id',
|
|
2486
|
+
kind: 'scalar',
|
|
2487
|
+
T: 9 /*ScalarType.STRING*/,
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
no: 3,
|
|
2491
|
+
name: 'inbounds',
|
|
2492
|
+
kind: 'message',
|
|
2493
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2494
|
+
T: () => InboundRtp,
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
no: 4,
|
|
2498
|
+
name: 'outbounds',
|
|
2499
|
+
kind: 'message',
|
|
2500
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2501
|
+
T: () => OutboundRtp,
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
no: 5,
|
|
2505
|
+
name: 'remote_inbounds',
|
|
2506
|
+
kind: 'message',
|
|
2507
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2508
|
+
T: () => RemoteInboundRtp,
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
no: 6,
|
|
2512
|
+
name: 'remote_outbounds',
|
|
2513
|
+
kind: 'message',
|
|
2514
|
+
repeat: 2 /*RepeatType.UNPACKED*/,
|
|
2515
|
+
T: () => RemoteOutboundRtp,
|
|
2516
|
+
},
|
|
2517
|
+
]);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
/**
|
|
2521
|
+
* @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsRequest
|
|
2522
|
+
*/
|
|
2523
|
+
const SendMetricsRequest = new SendMetricsRequest$Type();
|
|
2524
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2525
|
+
class SendMetricsResponse$Type extends MessageType {
|
|
2526
|
+
constructor() {
|
|
2527
|
+
super('stream.video.sfu.signal.SendMetricsResponse', []);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* @generated MessageType for protobuf message stream.video.sfu.signal.SendMetricsResponse
|
|
2532
|
+
*/
|
|
2533
|
+
const SendMetricsResponse = new SendMetricsResponse$Type();
|
|
2534
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
2289
2535
|
class SendStatsRequest$Type extends MessageType {
|
|
2290
2536
|
constructor() {
|
|
2291
2537
|
super('stream.video.sfu.signal.SendStatsRequest', [
|
|
@@ -2559,6 +2805,12 @@ class SendAnswerRequest$Type extends MessageType {
|
|
|
2559
2805
|
},
|
|
2560
2806
|
{ no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
2561
2807
|
{ no: 3, name: 'session_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
2808
|
+
{
|
|
2809
|
+
no: 4,
|
|
2810
|
+
name: 'negotiation_id',
|
|
2811
|
+
kind: 'scalar',
|
|
2812
|
+
T: 13 /*ScalarType.UINT32*/,
|
|
2813
|
+
},
|
|
2562
2814
|
]);
|
|
2563
2815
|
}
|
|
2564
2816
|
}
|
|
@@ -2666,6 +2918,12 @@ const SignalServer = new ServiceType('stream.video.sfu.signal.SignalServer', [
|
|
|
2666
2918
|
I: SendStatsRequest,
|
|
2667
2919
|
O: SendStatsResponse,
|
|
2668
2920
|
},
|
|
2921
|
+
{
|
|
2922
|
+
name: 'SendMetrics',
|
|
2923
|
+
options: {},
|
|
2924
|
+
I: SendMetricsRequest,
|
|
2925
|
+
O: SendMetricsResponse,
|
|
2926
|
+
},
|
|
2669
2927
|
{
|
|
2670
2928
|
name: 'StartNoiseCancellation',
|
|
2671
2929
|
options: {},
|
|
@@ -3346,6 +3604,12 @@ class SubscriberOffer$Type extends MessageType {
|
|
|
3346
3604
|
super('stream.video.sfu.event.SubscriberOffer', [
|
|
3347
3605
|
{ no: 1, name: 'ice_restart', kind: 'scalar', T: 8 /*ScalarType.BOOL*/ },
|
|
3348
3606
|
{ no: 2, name: 'sdp', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
|
|
3607
|
+
{
|
|
3608
|
+
no: 3,
|
|
3609
|
+
name: 'negotiation_id',
|
|
3610
|
+
kind: 'scalar',
|
|
3611
|
+
T: 13 /*ScalarType.UINT32*/,
|
|
3612
|
+
},
|
|
3349
3613
|
]);
|
|
3350
3614
|
}
|
|
3351
3615
|
}
|
|
@@ -3804,18 +4068,25 @@ class SignalServerClient {
|
|
|
3804
4068
|
const method = this.methods[6], opt = this._transport.mergeOptions(options);
|
|
3805
4069
|
return stackIntercept('unary', this._transport, method, opt, input);
|
|
3806
4070
|
}
|
|
4071
|
+
/**
|
|
4072
|
+
* @generated from protobuf rpc: SendMetrics(stream.video.sfu.signal.SendMetricsRequest) returns (stream.video.sfu.signal.SendMetricsResponse);
|
|
4073
|
+
*/
|
|
4074
|
+
sendMetrics(input, options) {
|
|
4075
|
+
const method = this.methods[7], opt = this._transport.mergeOptions(options);
|
|
4076
|
+
return stackIntercept('unary', this._transport, method, opt, input);
|
|
4077
|
+
}
|
|
3807
4078
|
/**
|
|
3808
4079
|
* @generated from protobuf rpc: StartNoiseCancellation(stream.video.sfu.signal.StartNoiseCancellationRequest) returns (stream.video.sfu.signal.StartNoiseCancellationResponse);
|
|
3809
4080
|
*/
|
|
3810
4081
|
startNoiseCancellation(input, options) {
|
|
3811
|
-
const method = this.methods[
|
|
4082
|
+
const method = this.methods[8], opt = this._transport.mergeOptions(options);
|
|
3812
4083
|
return stackIntercept('unary', this._transport, method, opt, input);
|
|
3813
4084
|
}
|
|
3814
4085
|
/**
|
|
3815
4086
|
* @generated from protobuf rpc: StopNoiseCancellation(stream.video.sfu.signal.StopNoiseCancellationRequest) returns (stream.video.sfu.signal.StopNoiseCancellationResponse);
|
|
3816
4087
|
*/
|
|
3817
4088
|
stopNoiseCancellation(input, options) {
|
|
3818
|
-
const method = this.methods[
|
|
4089
|
+
const method = this.methods[9], opt = this._transport.mergeOptions(options);
|
|
3819
4090
|
return stackIntercept('unary', this._transport, method, opt, input);
|
|
3820
4091
|
}
|
|
3821
4092
|
}
|
|
@@ -4995,6 +5266,16 @@ const hasScreenShareAudio = (p) => p.publishedTracks.includes(TrackType.SCREEN_S
|
|
|
4995
5266
|
* @param p the participant.
|
|
4996
5267
|
*/
|
|
4997
5268
|
const isPinned = (p) => !!p.pin && (p.pin.isLocalPin || p.pin.pinnedAt > 0);
|
|
5269
|
+
/**
|
|
5270
|
+
* Check if a participant has a track that is currently interrupted: the
|
|
5271
|
+
* participant intends to publish it (it is in `publishedTracks`) but no
|
|
5272
|
+
* media is flowing right now (it is in `interruptedTracks`).
|
|
5273
|
+
*
|
|
5274
|
+
* @param p the participant to check.
|
|
5275
|
+
* @param trackType the track type to check.
|
|
5276
|
+
*/
|
|
5277
|
+
const hasInterruptedTrack = (p, trackType) => !!p.interruptedTracks?.includes(trackType) &&
|
|
5278
|
+
p.publishedTracks.includes(trackType);
|
|
4998
5279
|
/**
|
|
4999
5280
|
* Check if a participant has a paused track of the specified type.
|
|
5000
5281
|
*
|
|
@@ -6360,7 +6641,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6360
6641
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6361
6642
|
};
|
|
6362
6643
|
|
|
6363
|
-
const version = "1.
|
|
6644
|
+
const version = "1.52.0";
|
|
6364
6645
|
const [major, minor, patch] = version.split('.');
|
|
6365
6646
|
let sdkInfo = {
|
|
6366
6647
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6458,6 +6739,7 @@ const getClientDetails = async () => {
|
|
|
6458
6739
|
sdk: sdkInfo,
|
|
6459
6740
|
os: osInfo,
|
|
6460
6741
|
device: deviceInfo,
|
|
6742
|
+
webrtcVersion: webRtcInfo?.version || '',
|
|
6461
6743
|
};
|
|
6462
6744
|
}
|
|
6463
6745
|
// @ts-expect-error - userAgentData is not yet in the TS types
|
|
@@ -6485,11 +6767,12 @@ const getClientDetails = async () => {
|
|
|
6485
6767
|
// Eliminates the generic "Chromium" name and "Not)A_Brand" name from the list.
|
|
6486
6768
|
// https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section
|
|
6487
6769
|
const uaBrowser = userAgentData?.fullVersionList?.find((v) => !v.brand.includes('Chromium') && !v.brand.match(/[()\-./:;=?_]/g));
|
|
6770
|
+
const browserVersion = uaBrowser?.version || browser.version || '';
|
|
6488
6771
|
return {
|
|
6489
6772
|
sdk: sdkInfo,
|
|
6490
6773
|
browser: {
|
|
6491
6774
|
name: uaBrowser?.brand || browser.name || navigator.userAgent,
|
|
6492
|
-
version:
|
|
6775
|
+
version: browserVersion,
|
|
6493
6776
|
},
|
|
6494
6777
|
os: {
|
|
6495
6778
|
name: userAgentData?.platform || os.name || '',
|
|
@@ -6502,6 +6785,7 @@ const getClientDetails = async () => {
|
|
|
6502
6785
|
.join(' '),
|
|
6503
6786
|
version: '',
|
|
6504
6787
|
},
|
|
6788
|
+
webrtcVersion: browserVersion,
|
|
6505
6789
|
};
|
|
6506
6790
|
};
|
|
6507
6791
|
|
|
@@ -7165,7 +7449,7 @@ class StatsTracer {
|
|
|
7165
7449
|
/**
|
|
7166
7450
|
* Creates a new StatsTracer instance.
|
|
7167
7451
|
*/
|
|
7168
|
-
constructor(pc, peerType, trackIdToTrackType) {
|
|
7452
|
+
constructor(pc, peerType, trackIdToTrackType, statsTimestampDriftThresholdMs = 0) {
|
|
7169
7453
|
this.previousStats = {};
|
|
7170
7454
|
this.frameTimeHistory = [];
|
|
7171
7455
|
this.fpsHistory = [];
|
|
@@ -7179,7 +7463,7 @@ class StatsTracer {
|
|
|
7179
7463
|
*/
|
|
7180
7464
|
this.get = async () => {
|
|
7181
7465
|
const stats = await this.pc.getStats();
|
|
7182
|
-
const currentStats =
|
|
7466
|
+
const currentStats = toObjectWithCorrectedTimestamp(stats, Date.now(), this.driftThresholdMs);
|
|
7183
7467
|
const performanceStats = this.withOverrides(this.peerType === PeerType.SUBSCRIBER
|
|
7184
7468
|
? this.getDecodeStats(currentStats)
|
|
7185
7469
|
: this.getEncodeStats(currentStats));
|
|
@@ -7298,17 +7582,28 @@ class StatsTracer {
|
|
|
7298
7582
|
this.pc = pc;
|
|
7299
7583
|
this.peerType = peerType;
|
|
7300
7584
|
this.trackIdToTrackType = trackIdToTrackType;
|
|
7585
|
+
this.driftThresholdMs = statsTimestampDriftThresholdMs;
|
|
7301
7586
|
}
|
|
7302
7587
|
}
|
|
7303
7588
|
/**
|
|
7304
|
-
* Convert the stat report to an object.
|
|
7589
|
+
* Convert the stat report to an object, correcting clock drift along the way.
|
|
7590
|
+
* Entries whose `timestamp` differs from `wallNow` by more than `thresholdMs`
|
|
7591
|
+
* are replaced with a clone whose `timestamp` is set to `wallNow`. The platform
|
|
7592
|
+
* clock backing `DOMHighResTimeStamp` can desynchronise from `Date.now()` after
|
|
7593
|
+
* system sleep or clock-jump events (notably on Electron/Chromium), which
|
|
7594
|
+
* corrupts the delta-compressed stats payload. A non-positive `thresholdMs`
|
|
7595
|
+
* disables correction.
|
|
7305
7596
|
*
|
|
7306
7597
|
* @param report the stat report to convert.
|
|
7598
|
+
* @param wallNow current wall-clock time used as the drift reference.
|
|
7599
|
+
* @param thresholdMs maximum tolerated drift in milliseconds.
|
|
7307
7600
|
*/
|
|
7308
|
-
const
|
|
7601
|
+
const toObjectWithCorrectedTimestamp = (report, wallNow, thresholdMs) => {
|
|
7309
7602
|
const obj = {};
|
|
7603
|
+
const correct = thresholdMs > 0;
|
|
7310
7604
|
report.forEach((v, k) => {
|
|
7311
|
-
|
|
7605
|
+
const drift = Math.abs(v.timestamp - wallNow);
|
|
7606
|
+
obj[k] = correct && drift > thresholdMs ? { ...v, timestamp: wallNow } : v;
|
|
7312
7607
|
});
|
|
7313
7608
|
return obj;
|
|
7314
7609
|
};
|
|
@@ -7440,7 +7735,7 @@ class BasePeerConnection {
|
|
|
7440
7735
|
/**
|
|
7441
7736
|
* Constructs a new `BasePeerConnection` instance.
|
|
7442
7737
|
*/
|
|
7443
|
-
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, }) {
|
|
7738
|
+
constructor(peerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay = 2500, statsTimestampDriftThresholdMs = 0, }) {
|
|
7444
7739
|
this.iceHasEverConnected = false;
|
|
7445
7740
|
this.isIceRestarting = false;
|
|
7446
7741
|
this.isDisposed = false;
|
|
@@ -7479,7 +7774,7 @@ class BasePeerConnection {
|
|
|
7479
7774
|
this.on = (event, fn) => {
|
|
7480
7775
|
const getTag = () => this.tag;
|
|
7481
7776
|
this.subscriptions.push(this.dispatcher.on(event, getTag, (e) => {
|
|
7482
|
-
const lockKey =
|
|
7777
|
+
const lockKey = this.eventLockKey(event);
|
|
7483
7778
|
withoutConcurrency(lockKey, async () => fn(e)).catch((err) => {
|
|
7484
7779
|
if (this.isDisposed)
|
|
7485
7780
|
return;
|
|
@@ -7487,6 +7782,13 @@ class BasePeerConnection {
|
|
|
7487
7782
|
});
|
|
7488
7783
|
}));
|
|
7489
7784
|
};
|
|
7785
|
+
/**
|
|
7786
|
+
* Returns the per-event `withoutConcurrency` tag used to serialize the
|
|
7787
|
+
* dispatcher handler for `event` on this peer connection.
|
|
7788
|
+
*/
|
|
7789
|
+
this.eventLockKey = (event) => {
|
|
7790
|
+
return `pc.${this.lock}.${event}`;
|
|
7791
|
+
};
|
|
7490
7792
|
/**
|
|
7491
7793
|
* Appends the trickled ICE candidates to the `RTCPeerConnection`.
|
|
7492
7794
|
*/
|
|
@@ -7727,7 +8029,7 @@ class BasePeerConnection {
|
|
|
7727
8029
|
this.onIceConnected = onIceConnected;
|
|
7728
8030
|
this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
|
|
7729
8031
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
7730
|
-
this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType);
|
|
8032
|
+
this.stats = new StatsTracer(this.pc, peerType, this.trackIdToTrackType, statsTimestampDriftThresholdMs);
|
|
7731
8033
|
if (enableTracing) {
|
|
7732
8034
|
this.tracer = new Tracer(`${tag}-${peerType === PeerType.SUBSCRIBER ? 'sub' : 'pub'}`);
|
|
7733
8035
|
this.tracer.trace('create', {
|
|
@@ -7740,7 +8042,7 @@ class BasePeerConnection {
|
|
|
7740
8042
|
/**
|
|
7741
8043
|
* Disposes the `RTCPeerConnection` instance.
|
|
7742
8044
|
*/
|
|
7743
|
-
dispose() {
|
|
8045
|
+
async dispose() {
|
|
7744
8046
|
clearTimeout(this.iceRestartTimeout);
|
|
7745
8047
|
this.iceRestartTimeout = undefined;
|
|
7746
8048
|
clearTimeout(this.preConnectStuckTimeout);
|
|
@@ -7762,6 +8064,7 @@ class BasePeerConnection {
|
|
|
7762
8064
|
pc.removeEventListener('signalingstatechange', this.onSignalingChange);
|
|
7763
8065
|
pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange);
|
|
7764
8066
|
pc.removeEventListener('icegatheringstatechange', this.onIceGatherChange);
|
|
8067
|
+
pc.removeEventListener('connectionstatechange', this.onConnectionStateChange);
|
|
7765
8068
|
this.unsubscribeIceTrickle?.();
|
|
7766
8069
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
7767
8070
|
this.subscriptions = [];
|
|
@@ -7789,8 +8092,14 @@ class TransceiverCache {
|
|
|
7789
8092
|
* Gets the transceiver for the given publish option.
|
|
7790
8093
|
*/
|
|
7791
8094
|
this.get = (publishOption) => {
|
|
7792
|
-
return this.
|
|
7793
|
-
|
|
8095
|
+
return this.getBy(publishOption.id, publishOption.trackType);
|
|
8096
|
+
};
|
|
8097
|
+
/**
|
|
8098
|
+
* Gets the transceiver for the given publish option id and track type.
|
|
8099
|
+
*/
|
|
8100
|
+
this.getBy = (publishOptionId, trackType) => {
|
|
8101
|
+
return this.cache.find((bundle) => bundle.publishOption.id === publishOptionId &&
|
|
8102
|
+
bundle.publishOption.trackType === trackType);
|
|
7794
8103
|
};
|
|
7795
8104
|
/**
|
|
7796
8105
|
* Updates the cached bundle with the given patch.
|
|
@@ -8075,6 +8384,21 @@ const toRTCDegradationPreference = (preference) => {
|
|
|
8075
8384
|
ensureExhausted(preference, 'Unknown degradation preference');
|
|
8076
8385
|
}
|
|
8077
8386
|
};
|
|
8387
|
+
const fromRTCDegradationPreference = (preference) => {
|
|
8388
|
+
switch (preference) {
|
|
8389
|
+
case 'balanced':
|
|
8390
|
+
return DegradationPreference.BALANCED;
|
|
8391
|
+
case 'maintain-framerate':
|
|
8392
|
+
return DegradationPreference.MAINTAIN_FRAMERATE;
|
|
8393
|
+
case 'maintain-resolution':
|
|
8394
|
+
return DegradationPreference.MAINTAIN_RESOLUTION;
|
|
8395
|
+
// @ts-expect-error not in the typedefs yet
|
|
8396
|
+
case 'maintain-framerate-and-resolution':
|
|
8397
|
+
return DegradationPreference.MAINTAIN_FRAMERATE_AND_RESOLUTION;
|
|
8398
|
+
default:
|
|
8399
|
+
return DegradationPreference.UNSPECIFIED;
|
|
8400
|
+
}
|
|
8401
|
+
};
|
|
8078
8402
|
|
|
8079
8403
|
/**
|
|
8080
8404
|
* The `Publisher` is responsible for publishing/unpublishing media streams to/from the SFU
|
|
@@ -8109,13 +8433,13 @@ class Publisher extends BasePeerConnection {
|
|
|
8109
8433
|
// create a clone of the track as otherwise the same trackId will
|
|
8110
8434
|
// appear in the SDP in multiple transceivers
|
|
8111
8435
|
const trackToPublish = this.cloneTrack(track);
|
|
8112
|
-
const
|
|
8113
|
-
if (!
|
|
8436
|
+
const bundle = this.transceiverCache.get(publishOption);
|
|
8437
|
+
if (!bundle) {
|
|
8114
8438
|
await this.addTransceiver(trackToPublish, publishOption, options);
|
|
8115
8439
|
}
|
|
8116
8440
|
else {
|
|
8117
|
-
const previousTrack = transceiver.sender.track;
|
|
8118
|
-
await this.updateTransceiver(
|
|
8441
|
+
const previousTrack = bundle.transceiver.sender.track;
|
|
8442
|
+
await this.updateTransceiver(bundle, trackToPublish, options);
|
|
8119
8443
|
if (!isReactNative()) {
|
|
8120
8444
|
this.stopTrack(previousTrack);
|
|
8121
8445
|
}
|
|
@@ -8150,13 +8474,20 @@ class Publisher extends BasePeerConnection {
|
|
|
8150
8474
|
/**
|
|
8151
8475
|
* Updates the transceiver with the given track and track type.
|
|
8152
8476
|
*/
|
|
8153
|
-
this.updateTransceiver = async (
|
|
8477
|
+
this.updateTransceiver = async (bundle, track, options = {}) => {
|
|
8478
|
+
const { transceiver, publishOption } = bundle;
|
|
8479
|
+
const trackType = publishOption.trackType;
|
|
8154
8480
|
const sender = transceiver.sender;
|
|
8155
8481
|
if (sender.track)
|
|
8156
8482
|
this.trackIdToTrackType.delete(sender.track.id);
|
|
8157
8483
|
await sender.replaceTrack(track);
|
|
8158
|
-
if (track)
|
|
8484
|
+
if (track) {
|
|
8159
8485
|
this.trackIdToTrackType.set(track.id, trackType);
|
|
8486
|
+
if (isFirefox() && bundle.videoSender) {
|
|
8487
|
+
// restore the encoding config from the cache, if any
|
|
8488
|
+
await this.changePublishQuality(bundle.videoSender, bundle);
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8160
8491
|
if (isAudioTrackType(trackType)) {
|
|
8161
8492
|
await this.updateAudioPublishOptions(trackType, options);
|
|
8162
8493
|
}
|
|
@@ -8216,7 +8547,7 @@ class Publisher extends BasePeerConnection {
|
|
|
8216
8547
|
continue;
|
|
8217
8548
|
// it is safe to stop the track here, it is a clone
|
|
8218
8549
|
this.stopTrack(transceiver.sender.track);
|
|
8219
|
-
await this.updateTransceiver(
|
|
8550
|
+
await this.updateTransceiver(item, null);
|
|
8220
8551
|
}
|
|
8221
8552
|
};
|
|
8222
8553
|
/**
|
|
@@ -8273,33 +8604,38 @@ class Publisher extends BasePeerConnection {
|
|
|
8273
8604
|
/**
|
|
8274
8605
|
* Stops the cloned track that is being published to the SFU.
|
|
8275
8606
|
*/
|
|
8276
|
-
this.stopTracks = (...trackTypes) => {
|
|
8277
|
-
|
|
8278
|
-
const
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8607
|
+
this.stopTracks = async (...trackTypes) => {
|
|
8608
|
+
return withoutConcurrency(this.eventLockKey('changePublishQuality'), async () => {
|
|
8609
|
+
for (const item of this.transceiverCache.items()) {
|
|
8610
|
+
const { publishOption, transceiver } = item;
|
|
8611
|
+
if (!trackTypes.includes(publishOption.trackType))
|
|
8612
|
+
continue;
|
|
8613
|
+
const track = transceiver.sender.track;
|
|
8614
|
+
await this.silenceSenderOnFirefox(item);
|
|
8615
|
+
this.stopTrack(track);
|
|
8616
|
+
}
|
|
8617
|
+
});
|
|
8283
8618
|
};
|
|
8284
8619
|
/**
|
|
8285
8620
|
* Stops all the cloned tracks that are being published to the SFU.
|
|
8286
8621
|
*/
|
|
8287
|
-
this.stopAllTracks = () => {
|
|
8288
|
-
|
|
8289
|
-
this.
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8622
|
+
this.stopAllTracks = async () => {
|
|
8623
|
+
return withoutConcurrency(this.eventLockKey('changePublishQuality'), async () => {
|
|
8624
|
+
for (const item of this.transceiverCache.items()) {
|
|
8625
|
+
const track = item.transceiver.sender.track;
|
|
8626
|
+
await this.silenceSenderOnFirefox(item);
|
|
8627
|
+
this.stopTrack(track);
|
|
8628
|
+
}
|
|
8629
|
+
for (const track of this.clonedTracks) {
|
|
8630
|
+
this.stopTrack(track);
|
|
8631
|
+
}
|
|
8632
|
+
});
|
|
8294
8633
|
};
|
|
8295
|
-
this.changePublishQuality = async (videoSender) => {
|
|
8296
|
-
const
|
|
8297
|
-
const enabledLayers = layers.filter((l) => l.active);
|
|
8634
|
+
this.changePublishQuality = async (videoSender, bundle) => {
|
|
8635
|
+
const enabledLayers = videoSender.layers.filter((l) => l.active);
|
|
8298
8636
|
const tag = 'Update publish quality:';
|
|
8299
8637
|
this.logger.info(`${tag} requested layers by SFU:`, enabledLayers);
|
|
8300
|
-
const
|
|
8301
|
-
t.publishOption.trackType === trackType);
|
|
8302
|
-
const sender = transceiverId?.transceiver.sender;
|
|
8638
|
+
const sender = bundle?.transceiver.sender;
|
|
8303
8639
|
if (!sender) {
|
|
8304
8640
|
return this.logger.warn(`${tag} no video sender found.`);
|
|
8305
8641
|
}
|
|
@@ -8307,7 +8643,7 @@ class Publisher extends BasePeerConnection {
|
|
|
8307
8643
|
if (params.encodings.length === 0) {
|
|
8308
8644
|
return this.logger.warn(`${tag} there are no encodings set.`);
|
|
8309
8645
|
}
|
|
8310
|
-
const codecInUse =
|
|
8646
|
+
const codecInUse = bundle?.publishOption.codec?.name;
|
|
8311
8647
|
const usesSvcCodec = codecInUse && isSvcCodec(codecInUse);
|
|
8312
8648
|
let changed = false;
|
|
8313
8649
|
for (const encoder of params.encodings) {
|
|
@@ -8507,6 +8843,72 @@ class Publisher extends BasePeerConnection {
|
|
|
8507
8843
|
track.stop();
|
|
8508
8844
|
this.clonedTracks.delete(track);
|
|
8509
8845
|
};
|
|
8846
|
+
/**
|
|
8847
|
+
* Silences a Firefox sender on the wire during unpublish.
|
|
8848
|
+
*
|
|
8849
|
+
* Firefox keeps emitting RTP after track.stop(), but the right lever
|
|
8850
|
+
* differs by track type:
|
|
8851
|
+
* - audio: `replaceTrack(null)` is the only reliable silencer;
|
|
8852
|
+
* `setParameters({encodings:[...active:false]})` does NOT stop
|
|
8853
|
+
* the Opus encoder.
|
|
8854
|
+
* - video: `setParameters({encodings:[...active:false]})` pauses
|
|
8855
|
+
* the encoder; `replaceTrack(null)` does NOT reliably stop the
|
|
8856
|
+
* video encoder. The prior active=true configuration is captured
|
|
8857
|
+
* onto `bundle.videoSender` so `updateTransceiver` can restore
|
|
8858
|
+
* it on the next publish.
|
|
8859
|
+
*
|
|
8860
|
+
* No-op on non-Firefox browsers and during teardown.
|
|
8861
|
+
*/
|
|
8862
|
+
this.silenceSenderOnFirefox = async (bundle) => {
|
|
8863
|
+
if (this.isDisposed || !isFirefox())
|
|
8864
|
+
return;
|
|
8865
|
+
const { transceiver, publishOption } = bundle;
|
|
8866
|
+
if (isAudioTrackType(publishOption.trackType)) {
|
|
8867
|
+
await transceiver.sender.replaceTrack(null).catch((err) => {
|
|
8868
|
+
this.logger.warn('Failed to clear audio sender track', err);
|
|
8869
|
+
});
|
|
8870
|
+
return;
|
|
8871
|
+
}
|
|
8872
|
+
await this.disableAllEncodings(bundle);
|
|
8873
|
+
};
|
|
8874
|
+
this.disableAllEncodings = async (bundle) => {
|
|
8875
|
+
const { transceiver, publishOption } = bundle;
|
|
8876
|
+
const sender = transceiver.sender;
|
|
8877
|
+
const params = sender.getParameters();
|
|
8878
|
+
if (!params.encodings || params.encodings.length === 0)
|
|
8879
|
+
return;
|
|
8880
|
+
if (!bundle.videoSender) {
|
|
8881
|
+
this.transceiverCache.update(publishOption, {
|
|
8882
|
+
videoSender: {
|
|
8883
|
+
trackType: publishOption.trackType,
|
|
8884
|
+
publishOptionId: publishOption.id,
|
|
8885
|
+
codec: publishOption.codec,
|
|
8886
|
+
degradationPreference: fromRTCDegradationPreference(params.degradationPreference),
|
|
8887
|
+
layers: params.encodings.map((e) => ({
|
|
8888
|
+
name: e.rid ?? 'q',
|
|
8889
|
+
active: e.active ?? true,
|
|
8890
|
+
maxBitrate: e.maxBitrate ?? 0,
|
|
8891
|
+
scaleResolutionDownBy: e.scaleResolutionDownBy ?? 0,
|
|
8892
|
+
maxFramerate: e.maxFramerate ?? 0,
|
|
8893
|
+
// @ts-expect-error scalabilityMode is not in the typedefs yet
|
|
8894
|
+
scalabilityMode: e.scalabilityMode ?? '',
|
|
8895
|
+
})),
|
|
8896
|
+
},
|
|
8897
|
+
});
|
|
8898
|
+
}
|
|
8899
|
+
let changed = false;
|
|
8900
|
+
for (const encoding of params.encodings) {
|
|
8901
|
+
if (encoding.active !== false) {
|
|
8902
|
+
encoding.active = false;
|
|
8903
|
+
changed = true;
|
|
8904
|
+
}
|
|
8905
|
+
}
|
|
8906
|
+
if (!changed)
|
|
8907
|
+
return;
|
|
8908
|
+
await sender.setParameters(params).catch((err) => {
|
|
8909
|
+
this.logger.error('Failed to disable video sender encodings:', err);
|
|
8910
|
+
});
|
|
8911
|
+
};
|
|
8510
8912
|
this.publishOptions = publishOptions;
|
|
8511
8913
|
this.on('iceRestart', (iceRestart) => {
|
|
8512
8914
|
if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
|
|
@@ -8515,7 +8917,16 @@ class Publisher extends BasePeerConnection {
|
|
|
8515
8917
|
});
|
|
8516
8918
|
this.on('changePublishQuality', async (event) => {
|
|
8517
8919
|
for (const videoSender of event.videoSenders) {
|
|
8518
|
-
|
|
8920
|
+
// if not publishing, update the encodingConfigCache and don't modify the state.
|
|
8921
|
+
// we'll apply this config on the next publish/unmute.
|
|
8922
|
+
const { trackType, publishOptionId } = videoSender;
|
|
8923
|
+
const bundle = this.transceiverCache.getBy(publishOptionId, trackType);
|
|
8924
|
+
if (bundle) {
|
|
8925
|
+
this.transceiverCache.update(bundle.publishOption, { videoSender });
|
|
8926
|
+
}
|
|
8927
|
+
if (isFirefox() && !this.isPublishing(trackType))
|
|
8928
|
+
continue;
|
|
8929
|
+
await this.changePublishQuality(videoSender, bundle);
|
|
8519
8930
|
}
|
|
8520
8931
|
});
|
|
8521
8932
|
this.on('changePublishOptions', (event) => {
|
|
@@ -8526,9 +8937,14 @@ class Publisher extends BasePeerConnection {
|
|
|
8526
8937
|
/**
|
|
8527
8938
|
* Disposes this Publisher instance.
|
|
8528
8939
|
*/
|
|
8529
|
-
dispose() {
|
|
8530
|
-
super.dispose();
|
|
8531
|
-
|
|
8940
|
+
async dispose() {
|
|
8941
|
+
await super.dispose();
|
|
8942
|
+
try {
|
|
8943
|
+
await this.stopAllTracks();
|
|
8944
|
+
}
|
|
8945
|
+
catch (err) {
|
|
8946
|
+
this.logger.warn('Failed to stop tracks during dispose', err);
|
|
8947
|
+
}
|
|
8532
8948
|
this.clonedTracks.clear();
|
|
8533
8949
|
}
|
|
8534
8950
|
}
|
|
@@ -8699,6 +9115,7 @@ class Subscriber extends BasePeerConnection {
|
|
|
8699
9115
|
await this.sfuClient.sendAnswer({
|
|
8700
9116
|
peerType: PeerType.SUBSCRIBER,
|
|
8701
9117
|
sdp: answer.sdp || '',
|
|
9118
|
+
negotiationId: subscriberOffer.negotiationId,
|
|
8702
9119
|
});
|
|
8703
9120
|
this.isIceRestarting = false;
|
|
8704
9121
|
};
|
|
@@ -11312,8 +11729,8 @@ const normalize = (options) => {
|
|
|
11312
11729
|
: false,
|
|
11313
11730
|
};
|
|
11314
11731
|
};
|
|
11315
|
-
const createSyntheticDevice = (deviceId, kind) => {
|
|
11316
|
-
return { deviceId, kind, label
|
|
11732
|
+
const createSyntheticDevice = (deviceId, kind, label = '') => {
|
|
11733
|
+
return { deviceId, kind, label, groupId: '' };
|
|
11317
11734
|
};
|
|
11318
11735
|
const readPreferences = (storageKey) => {
|
|
11319
11736
|
try {
|
|
@@ -11367,6 +11784,8 @@ class DeviceManager {
|
|
|
11367
11784
|
this.areSubscriptionsSetUp = false;
|
|
11368
11785
|
this.isTrackStoppedDueToTrackEnd = false;
|
|
11369
11786
|
this.filters = [];
|
|
11787
|
+
this.virtualDevicesSubject = new BehaviorSubject([]);
|
|
11788
|
+
this.virtualDeviceConcurrencyTag = Symbol('virtualDeviceConcurrencyTag');
|
|
11370
11789
|
this.statusChangeConcurrencyTag = Symbol('statusChangeConcurrencyTag');
|
|
11371
11790
|
this.filterRegistrationConcurrencyTag = Symbol('filterRegistrationConcurrencyTag');
|
|
11372
11791
|
/**
|
|
@@ -11379,6 +11798,7 @@ class DeviceManager {
|
|
|
11379
11798
|
this.subscriptions.forEach((s) => s());
|
|
11380
11799
|
this.subscriptions = [];
|
|
11381
11800
|
this.areSubscriptionsSetUp = false;
|
|
11801
|
+
this.virtualDevicesSubject.next([]);
|
|
11382
11802
|
};
|
|
11383
11803
|
this.runCurrentStreamCleanups = () => {
|
|
11384
11804
|
this.currentStreamCleanups.forEach((c) => c());
|
|
@@ -11437,7 +11857,93 @@ class DeviceManager {
|
|
|
11437
11857
|
* @returns an Observable that will be updated if a device is connected or disconnected
|
|
11438
11858
|
*/
|
|
11439
11859
|
listDevices() {
|
|
11440
|
-
return this.getDevices()
|
|
11860
|
+
return combineLatest([this.getDevices(), this.virtualDevicesSubject]).pipe(map(([real, virtual]) => [
|
|
11861
|
+
...real,
|
|
11862
|
+
...virtual.map((d) => createSyntheticDevice(d.deviceId, d.kind, d.label)),
|
|
11863
|
+
]));
|
|
11864
|
+
}
|
|
11865
|
+
/**
|
|
11866
|
+
* Registers a virtual camera or microphone backed by a caller-supplied
|
|
11867
|
+
* stream factory. The device appears in `listDevices()` and can be selected
|
|
11868
|
+
* via `select()` like any real device.
|
|
11869
|
+
*
|
|
11870
|
+
* Web only. React Native is not supported.
|
|
11871
|
+
*
|
|
11872
|
+
* Only supported for camera and microphone managers; calling on any other
|
|
11873
|
+
* manager throws.
|
|
11874
|
+
*/
|
|
11875
|
+
registerVirtualDevice(virtualDevice) {
|
|
11876
|
+
if (isReactNative()) {
|
|
11877
|
+
throw new Error('Virtual devices are not supported on React Native.');
|
|
11878
|
+
}
|
|
11879
|
+
if (this.trackType !== TrackType.AUDIO &&
|
|
11880
|
+
this.trackType !== TrackType.VIDEO) {
|
|
11881
|
+
throw new Error('Virtual devices are only supported for camera and microphone.');
|
|
11882
|
+
}
|
|
11883
|
+
const deviceId = `stream-virtual:${generateUUIDv4()}`;
|
|
11884
|
+
const entry = {
|
|
11885
|
+
deviceId,
|
|
11886
|
+
kind: this.mediaDeviceKind,
|
|
11887
|
+
...virtualDevice,
|
|
11888
|
+
};
|
|
11889
|
+
setCurrentValue(this.virtualDevicesSubject, (current) => [
|
|
11890
|
+
...current,
|
|
11891
|
+
entry,
|
|
11892
|
+
]);
|
|
11893
|
+
return {
|
|
11894
|
+
deviceId: entry.deviceId,
|
|
11895
|
+
unregister: async () => {
|
|
11896
|
+
await withoutConcurrency(this.virtualDeviceConcurrencyTag, async () => {
|
|
11897
|
+
setCurrentValue(this.virtualDevicesSubject, (current) => current.filter((d) => d !== entry));
|
|
11898
|
+
if (this.activeVirtualSession?.deviceId === deviceId) {
|
|
11899
|
+
await this.stopActiveVirtualSession();
|
|
11900
|
+
}
|
|
11901
|
+
});
|
|
11902
|
+
if (this.state.selectedDevice === deviceId) {
|
|
11903
|
+
await this.statusChangeSettled();
|
|
11904
|
+
await this.disable({ forceStop: true });
|
|
11905
|
+
await this.select(undefined);
|
|
11906
|
+
}
|
|
11907
|
+
},
|
|
11908
|
+
};
|
|
11909
|
+
}
|
|
11910
|
+
sanitizeVirtualStream(stream) {
|
|
11911
|
+
stream.getTracks().forEach((track) => {
|
|
11912
|
+
const originalGetSettings = track.getSettings.bind(track);
|
|
11913
|
+
track.getSettings = () => {
|
|
11914
|
+
const settings = originalGetSettings();
|
|
11915
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11916
|
+
const { deviceId, ...rest } = settings;
|
|
11917
|
+
return rest;
|
|
11918
|
+
};
|
|
11919
|
+
});
|
|
11920
|
+
return stream;
|
|
11921
|
+
}
|
|
11922
|
+
findVirtualDevice(deviceId) {
|
|
11923
|
+
if (!deviceId)
|
|
11924
|
+
return undefined;
|
|
11925
|
+
return getCurrentValue(this.virtualDevicesSubject).find((d) => d.deviceId === deviceId);
|
|
11926
|
+
}
|
|
11927
|
+
async stopActiveVirtualSession() {
|
|
11928
|
+
const session = this.activeVirtualSession;
|
|
11929
|
+
this.activeVirtualSession = undefined;
|
|
11930
|
+
await session?.stop?.();
|
|
11931
|
+
}
|
|
11932
|
+
async getSelectedStream(constraints) {
|
|
11933
|
+
const deviceId = this.state.selectedDevice;
|
|
11934
|
+
if (!deviceId?.startsWith('stream-virtual')) {
|
|
11935
|
+
return this.getStream(constraints);
|
|
11936
|
+
}
|
|
11937
|
+
return withoutConcurrency(this.virtualDeviceConcurrencyTag, async () => {
|
|
11938
|
+
const virtualDevice = this.findVirtualDevice(deviceId);
|
|
11939
|
+
if (!virtualDevice) {
|
|
11940
|
+
throw new Error(`Virtual device is not registered: ${deviceId}`);
|
|
11941
|
+
}
|
|
11942
|
+
await this.stopActiveVirtualSession();
|
|
11943
|
+
const { stream, stop } = await virtualDevice.getUserMedia(constraints);
|
|
11944
|
+
this.activeVirtualSession = { deviceId, stop };
|
|
11945
|
+
return this.sanitizeVirtualStream(stream);
|
|
11946
|
+
});
|
|
11441
11947
|
}
|
|
11442
11948
|
/**
|
|
11443
11949
|
* Returns `true` when this device is in enabled state.
|
|
@@ -11597,6 +12103,9 @@ class DeviceManager {
|
|
|
11597
12103
|
}
|
|
11598
12104
|
});
|
|
11599
12105
|
}
|
|
12106
|
+
getResolvedConstraints(constraints) {
|
|
12107
|
+
return constraints;
|
|
12108
|
+
}
|
|
11600
12109
|
publishStream(stream, options) {
|
|
11601
12110
|
return this.call.publish(stream, this.trackType, options);
|
|
11602
12111
|
}
|
|
@@ -11617,6 +12126,7 @@ class DeviceManager {
|
|
|
11617
12126
|
this.muteLocalStream(stopTracks);
|
|
11618
12127
|
const allEnded = this.getTracks().every((t) => t.readyState === 'ended');
|
|
11619
12128
|
if (allEnded) {
|
|
12129
|
+
await this.stopActiveVirtualSession();
|
|
11620
12130
|
// @ts-expect-error release() is present in react-native-webrtc
|
|
11621
12131
|
if (typeof mediaStream.release === 'function') {
|
|
11622
12132
|
// @ts-expect-error called to dispose the stream in RN
|
|
@@ -11672,12 +12182,12 @@ class DeviceManager {
|
|
|
11672
12182
|
// before chainWith below registers new ones for the new chain.
|
|
11673
12183
|
this.runCurrentStreamCleanups();
|
|
11674
12184
|
const defaultConstraints = this.state.defaultConstraints;
|
|
11675
|
-
const constraints = {
|
|
12185
|
+
const constraints = this.getResolvedConstraints({
|
|
11676
12186
|
...defaultConstraints,
|
|
11677
12187
|
deviceId: this.state.selectedDevice
|
|
11678
12188
|
? { exact: this.state.selectedDevice }
|
|
11679
12189
|
: undefined,
|
|
11680
|
-
};
|
|
12190
|
+
});
|
|
11681
12191
|
/**
|
|
11682
12192
|
* Chains two media streams together.
|
|
11683
12193
|
*
|
|
@@ -11734,7 +12244,7 @@ class DeviceManager {
|
|
|
11734
12244
|
};
|
|
11735
12245
|
// the rootStream represents the stream coming from the actual device
|
|
11736
12246
|
// e.g. camera or microphone stream
|
|
11737
|
-
rootStreamPromise = this.
|
|
12247
|
+
rootStreamPromise = this.getSelectedStream(constraints);
|
|
11738
12248
|
// we publish the last MediaStream of the chain
|
|
11739
12249
|
stream = await this.filters.reduce((parent, entry) => parent
|
|
11740
12250
|
.then((inputStream) => {
|
|
@@ -12051,7 +12561,10 @@ class DeviceManagerState {
|
|
|
12051
12561
|
setCurrentValue(this.mediaStreamSubject, stream);
|
|
12052
12562
|
setCurrentValue(this.rootMediaStreamSubject, rootStream);
|
|
12053
12563
|
if (rootStream) {
|
|
12054
|
-
this.
|
|
12564
|
+
const derived = this.getDeviceIdFromStream(rootStream);
|
|
12565
|
+
if (derived) {
|
|
12566
|
+
this.setDevice(derived);
|
|
12567
|
+
}
|
|
12055
12568
|
}
|
|
12056
12569
|
}
|
|
12057
12570
|
/**
|
|
@@ -12264,7 +12777,7 @@ class CameraManager extends DeviceManager {
|
|
|
12264
12777
|
getDevices() {
|
|
12265
12778
|
return getVideoDevices(this.call.tracer);
|
|
12266
12779
|
}
|
|
12267
|
-
|
|
12780
|
+
getResolvedConstraints(constraints) {
|
|
12268
12781
|
constraints.width = this.targetResolution.width;
|
|
12269
12782
|
constraints.height = this.targetResolution.height;
|
|
12270
12783
|
// We can't set both device id and facing mode
|
|
@@ -12275,6 +12788,9 @@ class CameraManager extends DeviceManager {
|
|
|
12275
12788
|
constraints.facingMode =
|
|
12276
12789
|
this.state.direction === 'front' ? 'user' : 'environment';
|
|
12277
12790
|
}
|
|
12791
|
+
return constraints;
|
|
12792
|
+
}
|
|
12793
|
+
getStream(constraints) {
|
|
12278
12794
|
return getVideoStream(constraints, this.call.tracer);
|
|
12279
12795
|
}
|
|
12280
12796
|
}
|
|
@@ -13602,9 +14118,10 @@ class Call {
|
|
|
13602
14118
|
this.sfuStatsReporter?.flush();
|
|
13603
14119
|
this.sfuStatsReporter?.stop();
|
|
13604
14120
|
this.sfuStatsReporter = undefined;
|
|
13605
|
-
this.
|
|
14121
|
+
this.lastStatsOptions = undefined;
|
|
14122
|
+
await this.subscriber?.dispose();
|
|
13606
14123
|
this.subscriber = undefined;
|
|
13607
|
-
this.publisher?.dispose();
|
|
14124
|
+
await this.publisher?.dispose();
|
|
13608
14125
|
this.publisher = undefined;
|
|
13609
14126
|
await this.sfuClient?.leaveAndClose(leaveReason);
|
|
13610
14127
|
this.sfuClient = undefined;
|
|
@@ -13880,15 +14397,17 @@ class Call {
|
|
|
13880
14397
|
const performingMigration = this.reconnectStrategy === WebsocketReconnectStrategy.MIGRATE;
|
|
13881
14398
|
const performingRejoin = this.reconnectStrategy === WebsocketReconnectStrategy.REJOIN;
|
|
13882
14399
|
const performingFastReconnect = this.reconnectStrategy === WebsocketReconnectStrategy.FAST;
|
|
13883
|
-
let statsOptions = this.
|
|
14400
|
+
let statsOptions = this.lastStatsOptions;
|
|
13884
14401
|
if (!this.credentials ||
|
|
13885
14402
|
!statsOptions ||
|
|
13886
14403
|
performingRejoin ||
|
|
13887
|
-
performingMigration
|
|
14404
|
+
performingMigration ||
|
|
14405
|
+
data?.migrating_from) {
|
|
13888
14406
|
try {
|
|
13889
14407
|
const joinResponse = await this.doJoinRequest(data);
|
|
13890
14408
|
this.credentials = joinResponse.credentials;
|
|
13891
14409
|
statsOptions = joinResponse.stats_options;
|
|
14410
|
+
this.lastStatsOptions = statsOptions;
|
|
13892
14411
|
}
|
|
13893
14412
|
catch (error) {
|
|
13894
14413
|
// prevent triggering reconnect flow if the state is OFFLINE
|
|
@@ -13995,7 +14514,7 @@ class Call {
|
|
|
13995
14514
|
}
|
|
13996
14515
|
else {
|
|
13997
14516
|
const connectionConfig = toRtcConfiguration(this.credentials.ice_servers);
|
|
13998
|
-
this.initPublisherAndSubscriber({
|
|
14517
|
+
await this.initPublisherAndSubscriber({
|
|
13999
14518
|
sfuClient,
|
|
14000
14519
|
connectionConfig,
|
|
14001
14520
|
clientDetails,
|
|
@@ -14140,11 +14659,11 @@ class Call {
|
|
|
14140
14659
|
* Initializes the Publisher and Subscriber Peer Connections.
|
|
14141
14660
|
* @internal
|
|
14142
14661
|
*/
|
|
14143
|
-
this.initPublisherAndSubscriber = (opts) => {
|
|
14662
|
+
this.initPublisherAndSubscriber = async (opts) => {
|
|
14144
14663
|
const { sfuClient, connectionConfig, clientDetails, statsOptions, publishOptions, closePreviousInstances, unifiedSessionId, } = opts;
|
|
14145
|
-
const { enable_rtc_stats: enableTracing } = statsOptions;
|
|
14664
|
+
const { enable_rtc_stats: enableTracing, reporting_interval_ms: reportingIntervalMs, } = statsOptions;
|
|
14146
14665
|
if (closePreviousInstances && this.subscriber) {
|
|
14147
|
-
this.subscriber.dispose();
|
|
14666
|
+
await this.subscriber.dispose();
|
|
14148
14667
|
}
|
|
14149
14668
|
const basePeerConnectionOptions = {
|
|
14150
14669
|
sfuClient,
|
|
@@ -14153,6 +14672,7 @@ class Call {
|
|
|
14153
14672
|
connectionConfig,
|
|
14154
14673
|
tag: sfuClient.tag,
|
|
14155
14674
|
enableTracing,
|
|
14675
|
+
statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
|
|
14156
14676
|
clientPublishOptions: this.clientPublishOptions,
|
|
14157
14677
|
onReconnectionNeeded: (kind, reason, peerType) => {
|
|
14158
14678
|
this.reconnect(kind, reason).catch((err) => {
|
|
@@ -14173,7 +14693,7 @@ class Call {
|
|
|
14173
14693
|
const isAnonymous = this.streamClient.user?.type === 'anonymous';
|
|
14174
14694
|
if (!isAnonymous) {
|
|
14175
14695
|
if (closePreviousInstances && this.publisher) {
|
|
14176
|
-
this.publisher.dispose();
|
|
14696
|
+
await this.publisher.dispose();
|
|
14177
14697
|
}
|
|
14178
14698
|
this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
|
|
14179
14699
|
}
|
|
@@ -14276,10 +14796,17 @@ class Call {
|
|
|
14276
14796
|
* `ICE_NEVER_CONNECTED` increments the unsupported-network counter).
|
|
14277
14797
|
*/
|
|
14278
14798
|
this.reconnect = async (strategy, reason) => {
|
|
14279
|
-
if (this.state.callingState === CallingState.
|
|
14799
|
+
if (this.state.callingState === CallingState.JOINING ||
|
|
14800
|
+
this.state.callingState === CallingState.RECONNECTING ||
|
|
14280
14801
|
this.state.callingState === CallingState.MIGRATING ||
|
|
14281
14802
|
this.state.callingState === CallingState.RECONNECTING_FAILED)
|
|
14282
14803
|
return;
|
|
14804
|
+
// Drop redundant reconnect calls. If a reconnect is already queued or
|
|
14805
|
+
// running for this Call, that entry will resolve whatever broke;
|
|
14806
|
+
// queueing more entries just replays the full REJOIN cycle (one extra
|
|
14807
|
+
// `POST /join` per entry) once the call is already healthy again.
|
|
14808
|
+
if (hasPending(this.reconnectConcurrencyTag))
|
|
14809
|
+
return;
|
|
14283
14810
|
return withoutConcurrency(this.reconnectConcurrencyTag, async () => {
|
|
14284
14811
|
const reconnectStartTime = Date.now();
|
|
14285
14812
|
this.reconnectStrategy = strategy;
|
|
@@ -14484,8 +15011,8 @@ class Call {
|
|
|
14484
15011
|
this.state.setCallingState(CallingState.JOINED);
|
|
14485
15012
|
}
|
|
14486
15013
|
finally {
|
|
14487
|
-
currentSubscriber?.dispose();
|
|
14488
|
-
currentPublisher?.dispose();
|
|
15014
|
+
await currentSubscriber?.dispose();
|
|
15015
|
+
await currentPublisher?.dispose();
|
|
14489
15016
|
// and close the previous SFU client, without specifying close code
|
|
14490
15017
|
currentSfuClient.close(StreamSfuClient.NORMAL_CLOSURE, 'Migrating away');
|
|
14491
15018
|
}
|
|
@@ -14674,7 +15201,7 @@ class Call {
|
|
|
14674
15201
|
this.stopPublish = async (...trackTypes) => {
|
|
14675
15202
|
if (!this.sfuClient || !this.publisher)
|
|
14676
15203
|
return;
|
|
14677
|
-
this.publisher.stopTracks(...trackTypes);
|
|
15204
|
+
await this.publisher.stopTracks(...trackTypes);
|
|
14678
15205
|
await this.updateLocalStreamState(undefined, ...trackTypes);
|
|
14679
15206
|
};
|
|
14680
15207
|
/**
|
|
@@ -16787,7 +17314,7 @@ class StreamClient {
|
|
|
16787
17314
|
this.getUserAgent = () => {
|
|
16788
17315
|
if (!this.cachedUserAgent) {
|
|
16789
17316
|
const { clientAppIdentifier = {} } = this.options;
|
|
16790
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
17317
|
+
const { sdkName = 'js', sdkVersion = "1.52.0", ...extras } = clientAppIdentifier;
|
|
16791
17318
|
this.cachedUserAgent = [
|
|
16792
17319
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
16793
17320
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -17423,5 +17950,5 @@ const humanize = (n) => {
|
|
|
17423
17950
|
return String(n);
|
|
17424
17951
|
};
|
|
17425
17952
|
|
|
17426
|
-
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
|
|
17953
|
+
export { AudioSettingsRequestDefaultDeviceEnum, AudioSettingsResponseDefaultDeviceEnum, browsers as Browsers, Call, CallRecordingFailedEventRecordingTypeEnum, CallRecordingReadyEventRecordingTypeEnum, CallRecordingStartedEventRecordingTypeEnum, CallRecordingStoppedEventRecordingTypeEnum, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, DeviceManager, DeviceManagerState, DynascaleManager, ErrorFromResponse, FrameRecordingSettingsRequestModeEnum, FrameRecordingSettingsRequestQualityEnum, FrameRecordingSettingsResponseModeEnum, IndividualRecordingSettingsRequestModeEnum, IndividualRecordingSettingsResponseModeEnum, IngressAudioEncodingOptionsRequestChannelsEnum, IngressSourceRequestFpsEnum, IngressVideoLayerRequestCodecEnum, LayoutSettingsRequestNameEnum, MicrophoneManager, MicrophoneManagerState, NoiseCancellationSettingsModeEnum, OwnCapability, RTMPBroadcastRequestQualityEnum, RTMPSettingsRequestQualityEnum, RawRecordingSettingsRequestModeEnum, RawRecordingSettingsResponseModeEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, ScreenShareManager, ScreenShareState, events as SfuEvents, SfuJoinError, models as SfuModels, SpeakerManager, SpeakerState, StartClosedCaptionsRequestLanguageEnum, StartTranscriptionRequestLanguageEnum, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore, TranscriptionSettingsRequestClosedCaptionModeEnum, TranscriptionSettingsRequestLanguageEnum, TranscriptionSettingsRequestModeEnum, TranscriptionSettingsResponseClosedCaptionModeEnum, TranscriptionSettingsResponseLanguageEnum, TranscriptionSettingsResponseModeEnum, VideoSettingsRequestCameraFacingEnum, VideoSettingsResponseCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, deviceIds$, disposeOfMediaStream, dominantSpeaker, getAudioBrowserPermission, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceState, getScreenShareStream, getSdkInfo, getVideoBrowserPermission, getVideoDevices, getVideoStream, getWebRTCInfo, hasAudio$1 as hasAudio, hasInterruptedTrack, hasPausedTrack, hasScreenShare, hasScreenShareAudio, hasVideo, humanize, isPinned, livestreamOrAudioRoomSortPreset, logToConsole, name, noopComparator, paginatedLayoutSortPreset, pinned, publishingAudio, publishingVideo, reactionType, resolveDeviceId, role, screenSharing, setDeviceInfo, setOSInfo, setPowerState, setSdkInfo, setThermalState, setWebRTCInfo, speakerLayoutSortPreset, speaking, videoLoggerSystem, withParticipantSource };
|
|
17427
17954
|
//# sourceMappingURL=index.es.js.map
|