@fishjam-cloud/webrtc-client 0.25.2 → 0.25.3

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.
Files changed (85) hide show
  1. package/package.json +3 -3
  2. package/dist/protobufs/fishjam/media_events/peer/peer.d.ts +0 -130
  3. package/dist/protobufs/fishjam/media_events/peer/peer.js +0 -1283
  4. package/dist/protobufs/fishjam/media_events/server/server.d.ts +0 -197
  5. package/dist/protobufs/fishjam/media_events/server/server.js +0 -2121
  6. package/dist/protobufs/fishjam/media_events/shared.d.ts +0 -48
  7. package/dist/protobufs/fishjam/media_events/shared.js +0 -315
  8. package/dist/webrtc-client/src/CommandsQueue.d.ts +0 -23
  9. package/dist/webrtc-client/src/CommandsQueue.js +0 -87
  10. package/dist/webrtc-client/src/ConnectionManager.d.ts +0 -17
  11. package/dist/webrtc-client/src/ConnectionManager.js +0 -49
  12. package/dist/webrtc-client/src/bitrate.d.ts +0 -12
  13. package/dist/webrtc-client/src/bitrate.js +0 -15
  14. package/dist/webrtc-client/src/dataChannels/DataChannel.d.ts +0 -56
  15. package/dist/webrtc-client/src/dataChannels/DataChannel.js +0 -144
  16. package/dist/webrtc-client/src/dataChannels/DataChannelManager.d.ts +0 -83
  17. package/dist/webrtc-client/src/dataChannels/DataChannelManager.js +0 -184
  18. package/dist/webrtc-client/src/deferred.d.ts +0 -8
  19. package/dist/webrtc-client/src/deferred.js +0 -17
  20. package/dist/webrtc-client/src/index.d.ts +0 -6
  21. package/dist/webrtc-client/src/index.js +0 -4
  22. package/dist/webrtc-client/src/internal.d.ts +0 -27
  23. package/dist/webrtc-client/src/internal.js +0 -18
  24. package/dist/webrtc-client/src/logger.d.ts +0 -5
  25. package/dist/webrtc-client/src/logger.js +0 -15
  26. package/dist/webrtc-client/src/mediaEvent.d.ts +0 -13
  27. package/dist/webrtc-client/src/mediaEvent.js +0 -20
  28. package/dist/webrtc-client/src/tracks/Local.d.ts +0 -49
  29. package/dist/webrtc-client/src/tracks/Local.js +0 -243
  30. package/dist/webrtc-client/src/tracks/LocalTrack.d.ts +0 -61
  31. package/dist/webrtc-client/src/tracks/LocalTrack.js +0 -212
  32. package/dist/webrtc-client/src/tracks/LocalTrackManager.d.ts +0 -39
  33. package/dist/webrtc-client/src/tracks/LocalTrackManager.js +0 -76
  34. package/dist/webrtc-client/src/tracks/Remote.d.ts +0 -32
  35. package/dist/webrtc-client/src/tracks/Remote.js +0 -165
  36. package/dist/webrtc-client/src/tracks/RemoteTrack.d.ts +0 -40
  37. package/dist/webrtc-client/src/tracks/RemoteTrack.js +0 -71
  38. package/dist/webrtc-client/src/tracks/TrackCommon.d.ts +0 -9
  39. package/dist/webrtc-client/src/tracks/TrackCommon.js +0 -1
  40. package/dist/webrtc-client/src/tracks/bandwidth.d.ts +0 -1
  41. package/dist/webrtc-client/src/tracks/bandwidth.js +0 -27
  42. package/dist/webrtc-client/src/tracks/encodings.d.ts +0 -3
  43. package/dist/webrtc-client/src/tracks/encodings.js +0 -11
  44. package/dist/webrtc-client/src/tracks/muteTrackUtils.d.ts +0 -3
  45. package/dist/webrtc-client/src/tracks/muteTrackUtils.js +0 -23
  46. package/dist/webrtc-client/src/tracks/transceivers.d.ts +0 -2
  47. package/dist/webrtc-client/src/tracks/transceivers.js +0 -93
  48. package/dist/webrtc-client/src/types.d.ts +0 -340
  49. package/dist/webrtc-client/src/types.js +0 -1
  50. package/dist/webrtc-client/src/webRTCEndpoint.d.ts +0 -350
  51. package/dist/webrtc-client/src/webRTCEndpoint.js +0 -747
  52. package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.d.ts +0 -1
  53. package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.js +0 -21
  54. package/dist/webrtc-client/tests/events/connectedEvent.test.d.ts +0 -1
  55. package/dist/webrtc-client/tests/events/connectedEvent.test.js +0 -70
  56. package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.d.ts +0 -1
  57. package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.js +0 -60
  58. package/dist/webrtc-client/tests/events/endpointAddedEvent.test.d.ts +0 -1
  59. package/dist/webrtc-client/tests/events/endpointAddedEvent.test.js +0 -57
  60. package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.d.ts +0 -1
  61. package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.js +0 -64
  62. package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.d.ts +0 -1
  63. package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.js +0 -78
  64. package/dist/webrtc-client/tests/events/trackAddedEvent.test.d.ts +0 -1
  65. package/dist/webrtc-client/tests/events/trackAddedEvent.test.js +0 -84
  66. package/dist/webrtc-client/tests/events/trackRemovedEvent.test.d.ts +0 -1
  67. package/dist/webrtc-client/tests/events/trackRemovedEvent.test.js +0 -26
  68. package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.d.ts +0 -1
  69. package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.js +0 -77
  70. package/dist/webrtc-client/tests/events/vadNotificationEvent.test.d.ts +0 -1
  71. package/dist/webrtc-client/tests/events/vadNotificationEvent.test.js +0 -42
  72. package/dist/webrtc-client/tests/fixtures.d.ts +0 -27
  73. package/dist/webrtc-client/tests/fixtures.js +0 -172
  74. package/dist/webrtc-client/tests/methods/addTrackMethod.test.d.ts +0 -1
  75. package/dist/webrtc-client/tests/methods/addTrackMethod.test.js +0 -46
  76. package/dist/webrtc-client/tests/methods/cleanUpMethod.test.d.ts +0 -1
  77. package/dist/webrtc-client/tests/methods/cleanUpMethod.test.js +0 -14
  78. package/dist/webrtc-client/tests/methods/connectMethod.test.d.ts +0 -1
  79. package/dist/webrtc-client/tests/methods/connectMethod.test.js +0 -40
  80. package/dist/webrtc-client/tests/methods/disconnectMethod.test.d.ts +0 -1
  81. package/dist/webrtc-client/tests/methods/disconnectMethod.test.js +0 -29
  82. package/dist/webrtc-client/tests/mocks.d.ts +0 -5
  83. package/dist/webrtc-client/tests/mocks.js +0 -80
  84. package/dist/webrtc-client/tests/utils.d.ts +0 -3
  85. package/dist/webrtc-client/tests/utils.js +0 -56
@@ -1,747 +0,0 @@
1
- import { MediaEvent_Connect, MediaEvent_Disconnect, MediaEvent_RenegotiateTracks, MediaEvent_UpdateTrackMetadata, } from '@fishjam-cloud/protobufs/peer';
2
- import { Candidate } from '@fishjam-cloud/protobufs/shared';
3
- import EventEmitter from 'events';
4
- import { v4 as uuidv4 } from 'uuid';
5
- import { CommandsQueue } from './CommandsQueue';
6
- import { ConnectionManager } from './ConnectionManager';
7
- import { DataChannelManager } from './dataChannels/DataChannelManager';
8
- import { Deferred } from './deferred';
9
- import { getLogger } from './logger';
10
- import { deserializeServerMediaEvent, serializePeerMediaEvent } from './mediaEvent';
11
- import { Local } from './tracks/Local';
12
- import { LocalTrackManager } from './tracks/LocalTrackManager';
13
- import { Remote } from './tracks/Remote';
14
- /**
15
- * Main class that is responsible for connecting to the RTC Engine, sending and receiving media.
16
- */
17
- export class WebRTCEndpoint extends EventEmitter {
18
- localTrackManager;
19
- remote;
20
- local;
21
- commandsQueue;
22
- dataChannelManager;
23
- proposedIceServers = [];
24
- logger;
25
- bandwidthEstimation = BigInt(0);
26
- connectionManager;
27
- clearConnectionCallbacks = null;
28
- constructor(props = {}) {
29
- super();
30
- this.logger = getLogger(!!props.debug);
31
- const sendEvent = (mediaEvent) => this.sendMediaEvent(mediaEvent);
32
- const emit = (events, ...args) => {
33
- this.emit(events, ...args);
34
- };
35
- this.remote = new Remote(emit, sendEvent);
36
- this.local = new Local(emit, sendEvent);
37
- this.localTrackManager = new LocalTrackManager(this.local, sendEvent);
38
- this.commandsQueue = new CommandsQueue(this.localTrackManager);
39
- const createDataChannelFn = (label, init) => {
40
- if (!this.connectionManager) {
41
- this.connectionManager = new ConnectionManager(this.proposedIceServers);
42
- }
43
- return this.connectionManager.createDataChannel(label, init);
44
- };
45
- const triggerRenegotiationFn = () => {
46
- this.sendMediaEvent({ renegotiateTracks: MediaEvent_RenegotiateTracks.create() });
47
- };
48
- this.dataChannelManager = new DataChannelManager(createDataChannelFn, triggerRenegotiationFn, this.logger);
49
- this.dataChannelManager.on('ready', () => {
50
- this.emit('dataChannelsReady');
51
- });
52
- this.dataChannelManager.on('data', (payload) => {
53
- this.emit('dataChannelPayload', payload);
54
- });
55
- this.dataChannelManager.on('error', (_, event) => {
56
- this.emit('dataChannelsError', new Error(`Data channel error event: ${event.type}`));
57
- });
58
- }
59
- /**
60
- * Tries to connect to the RTC Engine. If user is successfully connected then {@link WebRTCEndpointEvents.connected}
61
- * will be emitted.
62
- *
63
- * @param metadata - Any information that other endpoints will receive in {@link WebRTCEndpointEvents.endpointAdded}
64
- * after accepting this endpoint
65
- *
66
- * @example
67
- * ```ts
68
- * let webrtc = new WebRTCEndpoint();
69
- * webrtc.connect({displayName: "Bob"});
70
- * ```
71
- */
72
- connect = (metadata) => {
73
- this.local.setEndpointMetadata(metadata);
74
- const connect = MediaEvent_Connect.create({ metadataJson: JSON.stringify(metadata) });
75
- this.sendMediaEvent({ connect });
76
- };
77
- /**
78
- * Feeds media event received from RTC Engine to {@link WebRTCEndpoint}.
79
- * This function should be called whenever some media event from RTC Engine
80
- * was received and can result in {@link WebRTCEndpoint} generating some other
81
- * media events.
82
- *
83
- * @param mediaEvent - String data received over custom signalling layer.
84
- *
85
- * @example
86
- * This example assumes phoenix channels as signalling layer.
87
- * As phoenix channels require objects, RTC Engine encapsulates binary data into
88
- * map with one field that is converted to object with one field on the TS side.
89
- * ```ts
90
- * webrtcChannel.on("mediaEvent", (event) => webrtc.receiveMediaEvent(event.data));
91
- * ```
92
- */
93
- receiveMediaEvent = async (mediaEvent) => {
94
- const deserializedMediaEvent = deserializeServerMediaEvent(mediaEvent);
95
- if (deserializedMediaEvent.connected) {
96
- const connectedEvent = deserializedMediaEvent.connected;
97
- this.proposedIceServers = connectedEvent.iceServers;
98
- this.local.setLocalEndpointId(connectedEvent.endpointId);
99
- const localEndpointMetadataJson = connectedEvent.endpointIdToEndpoint[connectedEvent.endpointId]?.metadataJson;
100
- if (localEndpointMetadataJson) {
101
- this.local.setEndpointMetadata(JSON.parse(localEndpointMetadataJson));
102
- }
103
- const connectedEndpoint = connectedEvent.endpointIdToEndpoint[connectedEvent.endpointId];
104
- if (connectedEndpoint?.metadataJson) {
105
- const parsedMetadata = JSON.parse(connectedEndpoint?.metadataJson);
106
- this.local.setEndpointMetadata(parsedMetadata);
107
- }
108
- Object.entries(connectedEvent.endpointIdToEndpoint)
109
- .filter(([endpointId]) => endpointId !== this.local.getEndpoint().id)
110
- .forEach(([endpointId, endpoint]) => {
111
- this.remote.addRemoteEndpoint(endpointId, endpoint.metadataJson, endpoint.trackIdToTrack);
112
- });
113
- const remoteEndpoints = Object.values(this.remote.getRemoteEndpoints());
114
- this.emit('connected', this.local.getEndpoint().id, remoteEndpoints);
115
- return;
116
- }
117
- if (this.getEndpointId())
118
- await this.handleMediaEvent(deserializedMediaEvent);
119
- };
120
- getEndpointId = () => this.local.getEndpoint().id;
121
- onTrackReady = (event) => {
122
- const stream = event.streams[0];
123
- if (!stream)
124
- throw new Error('Cannot find media stream');
125
- const mid = event.transceiver.mid;
126
- const remoteTrack = this.remote.getTrackByMid(mid);
127
- remoteTrack.setReady(stream, event.track);
128
- this.emit('trackReady', remoteTrack.trackContext);
129
- };
130
- /**
131
- * Retrieves statistics related to the RTCPeerConnection.
132
- * These statistics provide insights into the performance and status of the connection.
133
- *
134
- * @return {Promise<RTCStatsReport>}
135
- *
136
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats | MDN Web Docs: RTCPeerConnection.getStats()}
137
- */
138
- async getStatistics(selector) {
139
- return (await this.connectionManager?.getConnection().getStats(selector)) ?? new Map();
140
- }
141
- /**
142
- * Returns a snapshot of currently received remote tracks.
143
- *
144
- * @example
145
- * if (webRTCEndpoint.getRemoteTracks()[trackId]?.simulcastConfig?.enabled) {
146
- * webRTCEndpoint.setTargetTrackEncoding(trackId, encoding);
147
- * }
148
- */
149
- getRemoteTracks() {
150
- return this.remote.getRemoteTrackContexts();
151
- }
152
- /**
153
- * Returns a snapshot of currently received remote endpoints.
154
- */
155
- getRemoteEndpoints() {
156
- return this.remote.getRemoteEndpoints();
157
- }
158
- getLocalEndpoint() {
159
- return this.local.getEndpoint();
160
- }
161
- getBandwidthEstimation() {
162
- return this.bandwidthEstimation;
163
- }
164
- getDataChannelsReadiness() {
165
- return this.dataChannelManager?.getChannelsReadiness() ?? false;
166
- }
167
- handleMediaEvent = async (event) => {
168
- if (event.offerData) {
169
- await this.onOfferData(event.offerData);
170
- }
171
- else if (event.tracksAdded) {
172
- this.localTrackManager.ongoingRenegotiation = true;
173
- const { trackIdToTrack, endpointId } = event.tracksAdded;
174
- if (this.getEndpointId() === endpointId)
175
- return;
176
- this.remote.addTracks(endpointId, trackIdToTrack);
177
- }
178
- else if (event.tracksRemoved) {
179
- const { endpointId, trackIds } = event.tracksRemoved;
180
- if (this.getEndpointId() === endpointId)
181
- return;
182
- this.remote.removeTracks(trackIds);
183
- }
184
- else if (event.sdpAnswer) {
185
- this.localTrackManager.ongoingRenegotiation = false;
186
- await this.onSdpAnswer(event.sdpAnswer);
187
- this.commandsQueue.processNextCommand();
188
- }
189
- else if (event.candidate) {
190
- await this.onRemoteCandidate(event.candidate);
191
- }
192
- else if (event.endpointAdded) {
193
- const { endpointId, metadataJson } = event.endpointAdded;
194
- if (endpointId === this.getEndpointId())
195
- return;
196
- this.remote.addRemoteEndpoint(endpointId, metadataJson);
197
- }
198
- else if (event.endpointRemoved) {
199
- const { endpointId } = event.endpointRemoved;
200
- if (endpointId === this.local.getEndpoint().id) {
201
- this.cleanUp();
202
- this.emit('disconnected');
203
- return;
204
- }
205
- if (this.getEndpointId() === endpointId)
206
- return;
207
- this.remote.removeRemoteEndpoint(endpointId);
208
- }
209
- else if (event.endpointUpdated) {
210
- const { endpointId, metadataJson } = event.endpointUpdated;
211
- if (this.getEndpointId() === endpointId)
212
- return;
213
- this.remote.updateRemoteEndpoint(endpointId, metadataJson);
214
- }
215
- else if (event.trackUpdated) {
216
- const { endpointId, trackId, metadataJson } = event.trackUpdated;
217
- if (this.getEndpointId() === endpointId)
218
- return;
219
- this.remote.updateRemoteTrack(endpointId, trackId, metadataJson);
220
- }
221
- else if (event.vadNotification) {
222
- const { trackId, status } = event.vadNotification;
223
- this.remote.setRemoteTrackVadStatus(trackId, status);
224
- }
225
- else if (event.error) {
226
- this.logger.warn('signaling error', {
227
- message: event.error.message,
228
- });
229
- this.emit('signalingError', {
230
- message: event.error.message,
231
- });
232
- this.disconnect();
233
- }
234
- else if (event.trackVariantSwitched) {
235
- const { trackId, variant } = event.trackVariantSwitched;
236
- this.remote.setRemoteTrackEncoding(trackId, variant);
237
- }
238
- else if (event.trackVariantEnabled) {
239
- const { trackId, variant } = event.trackVariantEnabled;
240
- this.remote.enableRemoteTrackEncoding(trackId, variant);
241
- }
242
- else if (event.trackVariantDisabled) {
243
- const { trackId, variant } = event.trackVariantDisabled;
244
- this.remote.disableRemoteTrackEncoding(trackId, variant);
245
- }
246
- };
247
- onSdpAnswer = async (data) => {
248
- this.remote.updateMLineIds(data.midToTrackId);
249
- this.local.updateMLineIds(data.midToTrackId);
250
- Object.keys(data.midToTrackId)
251
- .map((mid) => {
252
- if (!mid)
253
- throw new Error('TrackId is not defined');
254
- return mid;
255
- })
256
- .map((mid) => this.local.getTrackByMidOrNull(mid))
257
- .filter((localTrack) => localTrack !== null)
258
- .forEach((localTrack) => {
259
- const trackContext = localTrack.trackContext;
260
- trackContext.negotiationStatus = 'done';
261
- if (trackContext.pendingMetadataUpdate) {
262
- const updateTrackMetadata = MediaEvent_UpdateTrackMetadata.create({
263
- trackId: localTrack.id,
264
- metadataJson: JSON.stringify(trackContext.metadata),
265
- });
266
- this.sendMediaEvent({ updateTrackMetadata });
267
- }
268
- trackContext.pendingMetadataUpdate = false;
269
- });
270
- if (!this.connectionManager)
271
- throw new Error(`There is no active RTCPeerConnection`);
272
- // probably there is no need to reassign it on every onAnswer
273
- this.connectionManager.setOnTrackReady((event) => {
274
- this.onTrackReady(event);
275
- });
276
- try {
277
- await this.connectionManager.setRemoteDescription({ sdp: data.sdp, type: 'answer' });
278
- }
279
- catch (err) {
280
- this.logger.error(err);
281
- }
282
- };
283
- /**
284
- * Adds track that will be sent to the RTC Engine.
285
- * @param track - Audio or video track e.g. from your microphone or camera.
286
- * @param trackMetadata - Any information about this track that other endpoints will
287
- * receive in {@link WebRTCEndpointEvents.endpointAdded}. E.g. this can source of the track - whether it's
288
- * screensharing, webcam or some other media device.
289
- * @param _simulcastConfig - Simulcast configuration parameter. **Currently ignored** - simulcast is disabled
290
- * regardless of the value passed. This is a temporary change until bandwidth estimation is implemented or
291
- * manual track selection support is added. For more information refer to {@link SimulcastConfig}.
292
- * @param _maxBandwidth - maximal bandwidth this track can use. **Currently processed with a threshold check**:
293
- * if the value is a positive number, it will be used; otherwise, it defaults to 0 (unlimited).
294
- * This option has no effect for simulcast and audio tracks.
295
- * For simulcast tracks use `{@link WebRTCEndpoint.setTrackBandwidth}.
296
- * @returns {string} Returns id of added track
297
- * @example
298
- * ```ts
299
- * let localStream: MediaStream = new MediaStream();
300
- * try {
301
- * localAudioStream = await navigator.mediaDevices.getUserMedia(
302
- * AUDIO_CONSTRAINTS
303
- * );
304
- * localAudioStream
305
- * .getTracks()
306
- * .forEach((track) => localStream.addTrack(track));
307
- * } catch (error) {
308
- * this.logger.error("Couldn't get microphone permission:", error);
309
- * }
310
- *
311
- * try {
312
- * localVideoStream = await navigator.mediaDevices.getUserMedia(
313
- * VIDEO_CONSTRAINTS
314
- * );
315
- * localVideoStream
316
- * .getTracks()
317
- * .forEach((track) => localStream.addTrack(track));
318
- * } catch (error) {
319
- * this.logger.error("Couldn't get camera permission:", error);
320
- * }
321
- *
322
- * localStream
323
- * .getTracks()
324
- * .forEach((track) => webrtc.addTrack(track, localStream));
325
- * ```
326
- */
327
- async addTrack(track, trackMetadata, _simulcastConfig = {
328
- enabled: false,
329
- enabledVariants: [],
330
- disabledVariants: [],
331
- }, _maxBandwidth = 0) {
332
- const resolutionNotifier = new Deferred();
333
- const trackId = this.getTrackId(uuidv4());
334
- const stream = new MediaStream();
335
- // TODO: Simulcast is disabled manually, enable it once bandwidth estimation is implemented or we add manual track selection support.
336
- const simulcastConfig = {
337
- enabled: false,
338
- enabledVariants: [],
339
- disabledVariants: [],
340
- };
341
- const maxBandwidth = typeof _maxBandwidth === 'number' && _maxBandwidth > 0 ? _maxBandwidth : 0;
342
- try {
343
- stream.addTrack(track);
344
- this.commandsQueue.pushCommand({
345
- handler: async () => this.localTrackManager.addTrackHandler(trackId, track, stream, trackMetadata, simulcastConfig, maxBandwidth),
346
- parse: () => this.localTrackManager.parseAddTrack(track, simulcastConfig, maxBandwidth),
347
- resolve: 'after-renegotiation',
348
- resolutionNotifier,
349
- });
350
- }
351
- catch (error) {
352
- resolutionNotifier.reject(error);
353
- }
354
- await resolutionNotifier.promise;
355
- this.emit('localTrackAdded', {
356
- trackId,
357
- track,
358
- stream,
359
- trackMetadata,
360
- simulcastConfig,
361
- maxBandwidth,
362
- });
363
- return trackId;
364
- }
365
- /**
366
- * Replaces a track that is being sent to the RTC Engine.
367
- * @param trackId - Audio or video track.
368
- * @param {string} trackId - Id of audio or video track to replace.
369
- * @param {MediaStreamTrack} newTrack
370
- * @returns {Promise<boolean>} success
371
- * @example
372
- * ```ts
373
- * // setup camera
374
- * let localStream: MediaStream = new MediaStream();
375
- * try {
376
- * localVideoStream = await navigator.mediaDevices.getUserMedia(
377
- * VIDEO_CONSTRAINTS
378
- * );
379
- * localVideoStream
380
- * .getTracks()
381
- * .forEach((track) => localStream.addTrack(track));
382
- * } catch (error) {
383
- * this.logger.error("Couldn't get camera permission:", error);
384
- * }
385
- * let oldTrackId;
386
- * localStream
387
- * .getTracks()
388
- * .forEach((track) => trackId = webrtc.addTrack(track, localStream));
389
- *
390
- * // change camera
391
- * const oldTrack = localStream.getVideoTracks()[0];
392
- * let videoDeviceId = "abcd-1234";
393
- * navigator.mediaDevices.getUserMedia({
394
- * video: {
395
- * ...(VIDEO_CONSTRAINTS as {}),
396
- * deviceId: {
397
- * exact: videoDeviceId,
398
- * },
399
- * }
400
- * })
401
- * .then((stream) => {
402
- * let videoTrack = stream.getVideoTracks()[0];
403
- * webrtc.replaceTrack(oldTrackId, videoTrack);
404
- * })
405
- * .catch((error) => {
406
- * this.logger.error('Error switching camera', error);
407
- * })
408
- * ```
409
- */
410
- async replaceTrack(trackId, newTrack) {
411
- const resolutionNotifier = new Deferred();
412
- this.commandsQueue.pushCommand({
413
- handler: () => this.localTrackManager.replaceTrackHandler(this, trackId, newTrack),
414
- resolutionNotifier,
415
- resolve: 'on-handler-resolve',
416
- });
417
- return resolutionNotifier.promise.then(() => {
418
- this.emit('localTrackReplaced', { trackId, track: newTrack });
419
- });
420
- }
421
- /**
422
- * Updates maximum bandwidth for the track identified by trackId.
423
- * This value directly translates to quality of the stream and, in case of video, to the amount of RTP packets being sent.
424
- * In case trackId points at the simulcast track bandwidth is split between all of the variant streams proportionally to their resolution.
425
- *
426
- * @param {string} trackId
427
- * @param {BandwidthLimit} bandwidth in kbps
428
- * @returns {Promise<boolean>} success
429
- */
430
- setTrackBandwidth(trackId, bandwidth) {
431
- if (!this.connectionManager)
432
- throw new Error(`There is no active RTCPeerConnection`);
433
- return this.local.setTrackBandwidth(trackId, bandwidth);
434
- }
435
- /**
436
- * Updates maximum bandwidth for the given simulcast encoding of the given track.
437
- *
438
- * @param {string} trackId - id of the track
439
- * @param {string} rid - rid of the encoding
440
- * @param {BandwidthLimit} bandwidth - desired max bandwidth used by the encoding (in kbps)
441
- * @returns
442
- */
443
- async setEncodingBandwidth(trackId, rid, bandwidth) {
444
- if (!this.connectionManager)
445
- throw new Error(`There is no active RTCPeerConnection`);
446
- return await this.local.setEncodingBandwidth(trackId, rid, bandwidth);
447
- }
448
- /**
449
- * Removes a track from connection that was sent to the RTC Engine.
450
- * @param {string} trackId - Id of audio or video track to remove.
451
- * @example
452
- * ```ts
453
- * // setup camera
454
- * let localStream: MediaStream = new MediaStream();
455
- * try {
456
- * localVideoStream = await navigator.mediaDevices.getUserMedia(
457
- * VIDEO_CONSTRAINTS
458
- * );
459
- * localVideoStream
460
- * .getTracks()
461
- * .forEach((track) => localStream.addTrack(track));
462
- * } catch (error) {
463
- * this.logger.error("Couldn't get camera permission:", error);
464
- * }
465
- *
466
- * let trackId
467
- * localStream
468
- * .getTracks()
469
- * .forEach((track) => trackId = webrtc.addTrack(track, localStream));
470
- *
471
- * // remove track
472
- * webrtc.removeTrack(trackId)
473
- * ```
474
- */
475
- async removeTrack(trackId) {
476
- const resolutionNotifier = new Deferred();
477
- this.commandsQueue.pushCommand({
478
- handler: async () => this.localTrackManager.removeTrackHandler(trackId),
479
- resolutionNotifier,
480
- resolve: 'after-renegotiation',
481
- });
482
- await resolutionNotifier.promise;
483
- this.emit('localTrackRemoved', {
484
- trackId,
485
- });
486
- }
487
- /**
488
- * Sets track variant that server should send to the client library.
489
- *
490
- * The variant will be sent whenever it is available.
491
- * If chosen variant is temporarily unavailable, some other variant
492
- * will be sent until the chosen variant becomes active again.
493
- *
494
- * @param {string} trackId - id of track
495
- * @param {Encoding} variant - variant to receive
496
- * @example
497
- * ```ts
498
- * webrtc.setTargetTrackEncoding(incomingTrackCtx.trackId, "l")
499
- * ```
500
- */
501
- setTargetTrackEncoding(trackId, variant) {
502
- this.remote.setTargetRemoteTrackEncoding(trackId, variant);
503
- }
504
- /**
505
- * Enables track encoding so that it will be sent to the server.
506
- * @param {string} trackId - id of track
507
- * @param {Encoding} encoding - encoding that will be enabled
508
- * @example
509
- * ```ts
510
- * const trackId = webrtc.addTrack(track, stream, {}, {enabled: true, activeEncodings: ["l", "m", "h"]});
511
- * webrtc.disableTrackEncoding(trackId, "l");
512
- * // wait some time
513
- * webrtc.enableTrackEncoding(trackId, "l");
514
- * ```
515
- */
516
- enableTrackEncoding = async (trackId, encoding) => {
517
- await this.local.enableLocalTrackEncoding(trackId, encoding);
518
- };
519
- /**
520
- * Disables track encoding so that it will be no longer sent to the server.
521
- * @param {string} trackId - id of track
522
- * @param {Encoding} encoding - encoding that will be disabled
523
- * @example
524
- * ```ts
525
- * const trackId = webrtc.addTrack(track, stream, {}, {enabled: true, activeEncodings: ["l", "m", "h"]});
526
- * webrtc.disableTrackEncoding(trackId, "l");
527
- * ```
528
- */
529
- disableTrackEncoding = async (trackId, encoding) => {
530
- await this.local.disableLocalTrackEncoding(trackId, encoding);
531
- };
532
- /**
533
- * Updates the metadata for the current endpoint.
534
- * @param metadata - Data about this endpoint that other endpoints will receive upon being added.
535
- *
536
- * If the metadata is different from what is already tracked in the room, the optional
537
- * event `endpointUpdated` will be emitted for other endpoint in the room.
538
- */
539
- updateEndpointMetadata = (metadata) => {
540
- this.local.updateEndpointMetadata(metadata);
541
- };
542
- /**
543
- * Updates the metadata for a specific track.
544
- * @param trackId - trackId (generated in addTrack) of audio or video track.
545
- * @param trackMetadata - Data about this track that other endpoint will receive upon being added.
546
- *
547
- * If the metadata is different from what is already tracked in the room, the optional
548
- * event `trackUpdated` will be emitted for other endpoints in the room.
549
- */
550
- updateTrackMetadata = (trackId, trackMetadata) => {
551
- this.local.updateLocalTrackMetadata(trackId, trackMetadata);
552
- };
553
- /**
554
- * Disconnects from the room. This function should be called when user disconnects from the room
555
- * in a clean way e.g. by clicking a dedicated, custom button `disconnect`.
556
- * As a result there will be generated one more media event that should be
557
- * sent to the RTC Engine. Thanks to it each other endpoint will be notified
558
- * that endpoint was removed in {@link WebRTCEndpointEvents.endpointRemoved},
559
- */
560
- disconnect = () => {
561
- const disconnect = MediaEvent_Disconnect.create();
562
- this.sendMediaEvent({ disconnect });
563
- this.emit('disconnectRequested', {});
564
- this.cleanUp();
565
- };
566
- /**
567
- * Create both reliable and lossy data channel.
568
- * This method must be called before publishData() can be used.
569
- * Emits the 'dataChannelsReady' event when both channels are open and ready.
570
- *
571
- * @example
572
- * ```ts
573
- * const webrtc = new WebRTCEndpoint();
574
- *
575
- * webrtc.on('dataChannelsReady', () => {
576
- * console.log('Data channels ready, can now send data');
577
- * webrtc.publishData(new TextEncoder().encode('Hello'), { reliable: true });
578
- * });
579
- *
580
- * webrtc.connectDataChannels();
581
- * ```
582
- */
583
- connectDataChannels = () => {
584
- return this.dataChannelManager.connect();
585
- };
586
- /**
587
- * Publish data through a data channel.
588
- * The data channels must be created first by calling connectDataChannels() or enabling negotiateOnConnect.
589
- * Throws an error if the channel doesn't exist or isn't ready yet.
590
- *
591
- * @param data - The data to send as Uint8Array
592
- * @param options - Options specifying which channel to use (reliable or lossy)
593
- * @throws Error if the channel doesn't exist or isn't ready
594
- *
595
- * @example
596
- * ```ts
597
- * // Subscribe to incoming data
598
- * webrtc.on('dataReceived', ({ channelType, data }) => {
599
- * console.log(`Received on ${channelType}:`, new TextDecoder().decode(data));
600
- * });
601
- *
602
- * webrtc.on('dataChannelsReady', () => {
603
- * // Send reliable data
604
- * const data = new TextEncoder().encode('Hello World');
605
- * webrtc.publishData(data, { reliable: true });
606
- *
607
- * // Send lossy data for low-latency updates
608
- * const gameState = new Uint8Array([1, 2, 3, 4, 5]);
609
- * webrtc.publishData(gameState, { reliable: false });
610
- * });
611
- *
612
- * webrtc.connectDataChannels();
613
- * ```
614
- */
615
- publishData = (data, options) => {
616
- this.dataChannelManager.publishData(data, options);
617
- };
618
- /**
619
- * Cleans up {@link WebRTCEndpoint} instance.
620
- */
621
- cleanUp = () => {
622
- if (this.connectionManager) {
623
- this.clearConnectionCallbacks?.();
624
- this.connectionManager?.getConnection().close();
625
- this.commandsQueue.cleanUp();
626
- this.localTrackManager.cleanUp();
627
- // Clean up data channels
628
- this.dataChannelManager?.cleanup();
629
- }
630
- this.connectionManager = undefined;
631
- };
632
- getTrackId(uuid) {
633
- return `${this.getEndpointId()}:${uuid}`;
634
- }
635
- sendMediaEvent = (mediaEvent) => {
636
- this.emit('sendMediaEvent', serializePeerMediaEvent(mediaEvent));
637
- };
638
- async createAndSendOffer() {
639
- const connection = this.connectionManager;
640
- if (!connection)
641
- return;
642
- try {
643
- this.localTrackManager.updateSenders();
644
- const offer = await connection.getConnection().createOffer();
645
- if (!this.connectionManager) {
646
- this.logger.warn('RTCPeerConnection stopped or restarted');
647
- return;
648
- }
649
- await connection.getConnection().setLocalDescription(offer);
650
- if (!this.connectionManager) {
651
- this.logger.warn('RTCPeerConnection stopped or restarted');
652
- return;
653
- }
654
- const sdpOffer = this.local.createSdpOfferEvent(offer);
655
- this.sendMediaEvent({ sdpOffer });
656
- this.local.setLocalTrackStatusToOffered();
657
- }
658
- catch (error) {
659
- this.logger.error(error);
660
- }
661
- }
662
- onOfferData = async (offerData) => {
663
- const connectionManager = this.connectionManager ?? this.createNewConnection();
664
- if (offerData.tracksTypes) {
665
- connectionManager.addTransceiversIfNeeded(offerData.tracksTypes);
666
- }
667
- await this.createAndSendOffer();
668
- };
669
- setupConnectionListeners = (connection) => {
670
- const onIceCandidate = (event) => this.onLocalCandidate(event);
671
- const onIceCandidateError = (event) => this.onIceCandidateError(event);
672
- const onConnectionStateChange = (event) => this.onConnectionStateChange(event);
673
- const onIceConnectionStateChange = (event) => this.onIceConnectionStateChange(event);
674
- this.clearConnectionCallbacks = () => {
675
- connection.removeEventListener('icecandidate', onIceCandidate);
676
- connection.removeEventListener('icecandidateerror', onIceCandidateError);
677
- connection.removeEventListener('connectionstatechange', onConnectionStateChange);
678
- connection.removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
679
- };
680
- connection.addEventListener('icecandidate', onIceCandidate);
681
- connection.addEventListener('icecandidateerror', onIceCandidateError);
682
- connection.addEventListener('connectionstatechange', onConnectionStateChange);
683
- connection.addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
684
- };
685
- createNewConnection = () => {
686
- const connectionManager = new ConnectionManager(this.proposedIceServers);
687
- this.localTrackManager.updateConnection(connectionManager);
688
- this.local.updateConnection(connectionManager);
689
- this.setupConnectionListeners(connectionManager.getConnection());
690
- this.commandsQueue.initConnection(connectionManager);
691
- this.local.addAllTracksToConnection();
692
- this.connectionManager = connectionManager;
693
- return connectionManager;
694
- };
695
- onRemoteCandidate = async (candidate) => {
696
- try {
697
- const iceCandidate = new RTCIceCandidate(candidate);
698
- if (!this.connectionManager) {
699
- throw new Error('Received new remote candidate but RTCConnection is undefined');
700
- }
701
- await this.connectionManager.addIceCandidate(iceCandidate);
702
- }
703
- catch (error) {
704
- this.logger.error(error);
705
- }
706
- };
707
- onLocalCandidate = (event) => {
708
- if (!event.candidate)
709
- return;
710
- const candidate = Candidate.create({
711
- candidate: event.candidate.candidate,
712
- sdpMLineIndex: event.candidate.sdpMLineIndex ?? undefined,
713
- sdpMid: event.candidate.sdpMid ?? undefined,
714
- usernameFragment: event.candidate.usernameFragment ?? undefined,
715
- });
716
- this.sendMediaEvent({ candidate });
717
- };
718
- onIceCandidateError = (event) => {
719
- this.logger.warn(event);
720
- };
721
- onConnectionStateChange = (event) => {
722
- switch (this.localTrackManager.connection?.getConnection().connectionState) {
723
- case 'failed':
724
- this.emit('connectionError', {
725
- message: 'RTCPeerConnection failed',
726
- event,
727
- });
728
- break;
729
- }
730
- };
731
- onIceConnectionStateChange = (event) => {
732
- switch (this.localTrackManager.connection?.getConnection().iceConnectionState) {
733
- case 'disconnected':
734
- this.logger.warn('ICE connection: disconnected');
735
- // Requesting renegotiation on ICE connection state failed fixes RTCPeerConnection
736
- // when the user changes their WiFi network.
737
- this.sendMediaEvent({ renegotiateTracks: MediaEvent_RenegotiateTracks.create() });
738
- break;
739
- case 'failed':
740
- this.emit('connectionError', {
741
- message: 'ICE connection failed',
742
- event,
743
- });
744
- break;
745
- }
746
- };
747
- }