@reactoo/watchtogether-sdk-js 2.8.0 → 2.8.1-5.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -234,6 +234,7 @@ class RoomSession {
234
234
  this.enableDtx = false;
235
235
  this.simulcast = false;
236
236
 
237
+
237
238
  this.defaultSimulcastSettings = {
238
239
  "default" : {
239
240
  mode: "controlled", // controlled, manual, browserControlled
@@ -274,6 +275,7 @@ class RoomSession {
274
275
  this.sessionId = null;
275
276
  this.apisecret = null;
276
277
  this.ws = null;
278
+ this.wsOpen = false;
277
279
 
278
280
  // helper flags
279
281
 
@@ -291,6 +293,7 @@ class RoomSession {
291
293
  this.requestMuteStatusTimeout = 100;
292
294
  this._statsInterval = 3000;
293
295
  this._statsIntervalId = null;
296
+ this._sendMessageWebsocketTimeout = 30000;
294
297
  this._sendMessageTimeout = 10000;
295
298
  this._keepAlivePeriod = 25000;
296
299
  this._longPollTimeout = 60000;
@@ -313,6 +316,7 @@ class RoomSession {
313
316
  this.#enableDebug();
314
317
  }
315
318
 
319
+
316
320
  }
317
321
 
318
322
  #httpAPICall = function(url, options) {
@@ -501,6 +505,7 @@ class RoomSession {
501
505
  id,
502
506
  userId,
503
507
  role,
508
+ mid,
504
509
  fullUserId: display,
505
510
  constructId: this.constructId,
506
511
  track: track,
@@ -540,6 +545,7 @@ class RoomSession {
540
545
  role,
541
546
  fullUserId: display,
542
547
  constructId: this.constructId,
548
+ mid: null,
543
549
  track: null,
544
550
  source: null,
545
551
  adding: false,
@@ -882,6 +888,23 @@ class RoomSession {
882
888
  }
883
889
  }
884
890
 
891
+ sendMessageViaDataChannel(label = this.defaultDataChannelLabel, data) {
892
+ if(this.#publisherHandle) {
893
+ if(label === null) {
894
+ Object.keys(this.#publisherHandle.webrtcStuff.dataChannel).forEach((key) => {
895
+ const channel = this.#publisherHandle.webrtcStuff.dataChannel[key];
896
+ channel.send(JSON.stringify(data));
897
+ })
898
+ }
899
+ else {
900
+ const channel = this.#publisherHandle.webrtcStuff.dataChannel[label];
901
+ if(channel) {
902
+ channel.send(JSON.stringify(data));
903
+ }
904
+ }
905
+ }
906
+ }
907
+
885
908
  async sendMessage(handleId, message = {body: 'Example Body'}, dontWait = false, dontResolveOnAck = false, retry = 0) {
886
909
  return this.#send({
887
910
  "janus": "message",
@@ -923,6 +946,9 @@ class RoomSession {
923
946
  }
924
947
  return json;
925
948
  })
949
+ .catch(e => {
950
+ return Promise.reject({error: e, requestData});
951
+ })
926
952
  }
927
953
 
928
954
  async #sendWebsockets(request = {}, ignoreResponse = false, dontResolveOnAck = false, retry = 0) {
@@ -936,21 +962,36 @@ class RoomSession {
936
962
  };
937
963
  this._log(requestData);
938
964
  const op = () => new Promise((resolve, reject) => {
965
+
939
966
  let messageTimeoutId = null;
940
- let abortResponse = () => {
941
- this._abortController.signal.removeEventListener('abort', abortResponse);
967
+
968
+ let cleanup = () => {
942
969
  clearTimeout(messageTimeoutId);
970
+ this._abortController.signal.removeEventListener('abort', abortResponse);
943
971
  this.ws.removeEventListener('message', parseResponse);
972
+ };
973
+
974
+ let abortResponse = () => {
975
+ cleanup();
944
976
  reject({type: 'warning', id: 4, message: 'connection cancelled'})
945
977
  };
946
978
 
979
+ // we may get multiple responses from parallel requests, so we need to make sure we only resolve the one we sent
980
+
947
981
  let parseResponse = (event) => {
948
- let json = JSON.parse(event.data);
982
+ let json;
983
+ try {
984
+ json = JSON.parse(event.data);
985
+ }
986
+ catch (e) {}
987
+
988
+ if(!json) {
989
+ return;
990
+ }
991
+
949
992
  let r_transaction = json['transaction'];
950
993
  if (r_transaction === transaction && (!dontResolveOnAck || json['janus'] !== 'ack')) {
951
- clearTimeout(messageTimeoutId);
952
- this._abortController.signal.removeEventListener('abort', abortResponse);
953
- this.ws.removeEventListener('message', parseResponse);
994
+ cleanup();
954
995
  if (json['janus'] === 'error') {
955
996
  if (json?.error?.code == 403) {
956
997
  this.disconnect();
@@ -967,31 +1008,78 @@ class RoomSession {
967
1008
  this.ws.send(JSON.stringify(requestData));
968
1009
  }
969
1010
  resolve();
970
- } else {
1011
+ }
1012
+ else {
971
1013
  if (this.ws && this.ws.readyState === 1) {
1014
+
972
1015
  this.ws.addEventListener('message', parseResponse);
1016
+
973
1017
  messageTimeoutId = setTimeout(() => {
974
- this.ws.removeEventListener('message', parseResponse);
975
- this._abortController.signal.removeEventListener('abort', abortResponse);
1018
+ cleanup();
976
1019
  reject({type: 'warning', id: 6, message: 'send timeout', data: requestData});
977
1020
  }, this._sendMessageTimeout);
1021
+
978
1022
  this._abortController.signal.addEventListener('abort', abortResponse);
1023
+
979
1024
  this.ws.send(JSON.stringify(requestData));
980
- } else {
1025
+ }
1026
+ else {
1027
+ cleanup();
981
1028
  reject({type: 'warning', id: 7, message: 'No connection to WebSockets', data: requestData});
982
1029
  }
983
1030
  }
984
1031
  })
985
1032
 
986
1033
  return op().catch(e => {
987
- if (e.id === 17) {
1034
+ if (e.id === 4 ) {
988
1035
  return Promise.reject(e);
989
1036
  }
990
- else if(e.id === 29 && retry > 0) {
991
- return wait(this._sendMessageTimeout).then(() => this.#send(request, ignoreResponse, dontResolveOnAck, retry - 1));
1037
+ else if(e.id === 7 && this.isSupposeToBeConnected) {
1038
+
1039
+ return new Promise((resolve, reject) => {
1040
+
1041
+ let establishConnectionTimeoutId = null;
1042
+
1043
+ const cleanup = () => {
1044
+ clearTimeout(establishConnectionTimeoutId);
1045
+ this._abortController.signal.removeEventListener('abort', abort);
1046
+ this.off('webSocketsConnectionOpened', connection);
1047
+ };
1048
+
1049
+ const connection = () => {
1050
+ cleanup();
1051
+ resolve(this.#sendWebsockets(request, ignoreResponse, dontResolveOnAck, retry - 1));
1052
+ };
1053
+ const timeout = () => {
1054
+ cleanup();
1055
+ reject({type: 'warning', id: 46, message: 'WebSocket connection did not open during wait period', data: requestData});
1056
+ };
1057
+ const abort = () => {
1058
+ cleanup();
1059
+ reject({type: 'warning', id: 47, message: 'WebSocket connection was aborted', data: requestData});
1060
+ };
1061
+
1062
+
1063
+ if(this.wsOpen) {
1064
+
1065
+ this._log('Retrying failed WebSocket request', requestData);
1066
+
1067
+ resolve(this.#sendWebsockets(request, ignoreResponse, dontResolveOnAck, retry - 1));
1068
+ }
1069
+ else {
1070
+
1071
+ this._log('Waiting for WebSocket connection to open before sending request', requestData);
1072
+
1073
+ this._abortController.signal.addEventListener('abort', abort);
1074
+ establishConnectionTimeoutId = setTimeout(timeout, this._sendMessageWebsocketTimeout);
1075
+ this.once('webSocketsConnectionOpened', connection);
1076
+ }
1077
+
1078
+ })
1079
+
992
1080
  }
993
- else if(retry > 0) {
994
- return this.#send(request, ignoreResponse, dontResolveOnAck, retry - 1);
1081
+ else if(retry > 0 && this.isSupposeToBeConnected) {
1082
+ return this.#sendWebsockets(request, ignoreResponse, dontResolveOnAck, retry - 1);
995
1083
  }
996
1084
  else {
997
1085
  return Promise.reject(e);
@@ -1034,6 +1122,9 @@ class RoomSession {
1034
1122
 
1035
1123
  #connectionClosed() {
1036
1124
 
1125
+ this.wsOpen = false;
1126
+ this.emit('webSocketsConnectionClosed');
1127
+
1037
1128
  if (!this.isConnected || this.isConnecting || this.isDisconnecting) {
1038
1129
  return;
1039
1130
  }
@@ -1041,7 +1132,7 @@ class RoomSession {
1041
1132
  this.#reconnect()
1042
1133
  .catch(e => {
1043
1134
  if(e.type !== 'warning') {
1044
- this.disconnect();
1135
+ this.disconnect().catch(() => {});
1045
1136
  }
1046
1137
  this.emit('error', e)
1047
1138
  });
@@ -1112,7 +1203,12 @@ class RoomSession {
1112
1203
  if (type === "trickle") {
1113
1204
  let candidate = json["candidate"];
1114
1205
  let config = handle.webrtcStuff;
1115
- if (config.pc && config.remoteSdp) {
1206
+ if (config.pc &&
1207
+ config.remoteSdp &&
1208
+ !config.isIceRestarting &&
1209
+ config.pc.iceConnectionState !== 'closed' &&
1210
+ config.pc.iceConnectionState !== 'failed' &&
1211
+ config.pc.iceConnectionState !== 'disconnected') {
1116
1212
 
1117
1213
  if (!candidate || candidate.completed === true) {
1118
1214
  config.pc.addIceCandidate(null).catch((e) => {
@@ -1380,8 +1476,6 @@ class RoomSession {
1380
1476
  config.dataChannelOpen = this.defaultDataChannelLabel === data?.label && data?.state === 'open';
1381
1477
  }
1382
1478
 
1383
-
1384
-
1385
1479
  if (handleId === this.handleId && this.defaultDataChannelLabel === data?.label) {
1386
1480
  this._isDataChannelOpen = data?.state === 'open';
1387
1481
  this.emit('dataChannel', data?.state === 'open');
@@ -1394,19 +1488,8 @@ class RoomSession {
1394
1488
  type: 'warning',
1395
1489
  id: 12,
1396
1490
  message: 'data event warning',
1397
- data: [handleId, data]
1491
+ data: [handleId, data, data.error?.message, data.error?.errorDetail]
1398
1492
  });
1399
-
1400
- if (handle) {
1401
- let config = handle.webrtcStuff;
1402
- if(this.defaultDataChannelLabel === data.label) {
1403
- config.dataChannelOpen = false;
1404
- }
1405
- }
1406
- if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {
1407
- this._isDataChannelOpen = false;
1408
- this.emit('dataChannel', false);
1409
- }
1410
1493
  }
1411
1494
 
1412
1495
  if (handleId === this.handleId && type === 'message') {
@@ -1422,6 +1505,18 @@ class RoomSession {
1422
1505
  this.emit('data', d);
1423
1506
  }
1424
1507
 
1508
+ if(handleId !== this.handleId && type === 'message') {
1509
+ let d = null;
1510
+
1511
+ try {
1512
+ d = JSON.parse(data)
1513
+ } catch (e) {
1514
+ this.emit('error', {type: 'warning', id: 45, message: 'data message parse error', data: [handleId, e]});
1515
+ return;
1516
+ }
1517
+ this.emit('dataMessage', d);
1518
+ }
1519
+
1425
1520
  }
1426
1521
 
1427
1522
  async #createHandle() {
@@ -1711,6 +1806,10 @@ class RoomSession {
1711
1806
  this.ws.addEventListener('message', this.__handleWsEventsBoundFn);
1712
1807
 
1713
1808
  this.ws.onopen = () => {
1809
+
1810
+ this.wsOpen = true;
1811
+ this.emit('webSocketsConnectionOpened');
1812
+
1714
1813
  this._abortController.signal.removeEventListener('abort', abortConnect);
1715
1814
  if(!reclaim) {
1716
1815
  this.#send({"janus": "create"})
@@ -1824,10 +1923,10 @@ class RoomSession {
1824
1923
  }
1825
1924
  let _rejectTimeout = () => {
1826
1925
  cleanup();
1827
- reject({type: 'warning', id: 44, message: 'Connection timeout'})
1926
+ reject({type: 'error', id: 44, message: 'Connection timeout'})
1828
1927
  }
1829
1928
  this.once('joined', _resolve);
1830
- timeoutId = setTimeout(_rejectTimeout, 10000)
1929
+ timeoutId = setTimeout(_rejectTimeout, 15000)
1831
1930
  this._abortController.signal.addEventListener('abort', _rejectAbort);
1832
1931
  })
1833
1932
  }
@@ -1875,8 +1974,6 @@ class RoomSession {
1875
1974
  enableDtx = false
1876
1975
  ) {
1877
1976
 
1878
- this.isSupposeToBeConnected = true;
1879
-
1880
1977
  if (this.isConnecting) {
1881
1978
  this.emit('error', {type: 'warning', id: 23, message: 'connection already in progress'});
1882
1979
  return
@@ -1886,6 +1983,8 @@ class RoomSession {
1886
1983
  await this.disconnect();
1887
1984
  }
1888
1985
 
1986
+ this.isSupposeToBeConnected = true;
1987
+
1889
1988
  this._abortController = new AbortController();
1890
1989
 
1891
1990
  this.sessionId = null;
@@ -1936,6 +2035,9 @@ class RoomSession {
1936
2035
  await this.#waitForConnectEvent();
1937
2036
 
1938
2037
  } catch (error) {
2038
+ if(error?.type !== 'warning') {
2039
+ this.disconnect().catch(() => {});
2040
+ }
1939
2041
  this.emit('error', error);
1940
2042
  } finally {
1941
2043
  this.isConnecting = false;
@@ -1975,14 +2077,34 @@ class RoomSession {
1975
2077
  await this.#destroyHandle(this.#subscriberHandle?.handleId, false);
1976
2078
 
1977
2079
  this.#wipeListeners();
1978
- if (this.ws && this.ws.readyState === 1) {
2080
+
2081
+ if(!this.useWebsockets) {
2082
+
2083
+ if(this.token) {
2084
+ await this.#send({"janus": "destroy"}, true);
2085
+ }
2086
+ }
2087
+
2088
+ else if (this.ws && this.ws.readyState === 1) {
1979
2089
  await this.#send({"janus": "destroy"}, true);
1980
2090
  this.ws.close();
1981
2091
  }
1982
2092
 
1983
2093
  this.#subscriberJoinPromise = null;
2094
+
1984
2095
  this.sessionId = null;
2096
+ this.server = null;
2097
+ this.protocol = null;
2098
+ this.iceServers = null;
2099
+ this.token = null;
2100
+ this.roomId = null;
2101
+ this.pin = null;
2102
+ this.id = null;
2103
+ this.display = null;
2104
+ this.userId = null;
2105
+
1985
2106
  this.isPublished = false;
2107
+ this.isConnecting = false;
1986
2108
  this.isConnected = false;
1987
2109
  this.isDisconnecting = false;
1988
2110
  this.emit('publishing', false);
@@ -2111,7 +2233,7 @@ class RoomSession {
2111
2233
 
2112
2234
  let descriptions = [];
2113
2235
  let transceivers = config.pc.getTransceivers();
2114
- Object.keys(config.streamMap[this.id]).forEach(source => {
2236
+ Object.keys(config.streamMap[this.id]??{}).forEach(source => {
2115
2237
  const simulcastConfigForSource = this.#findSimulcastConfig(source, this.simulcastSettings);
2116
2238
  config.streamMap[this.id][source].forEach(trackId => {
2117
2239
  let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId)
@@ -2157,6 +2279,7 @@ class RoomSession {
2157
2279
  }
2158
2280
  this.emit('iceState', [handleId, handleId === this.#publisherHandle?.handleId, config.pc.iceConnectionState]);
2159
2281
  };
2282
+
2160
2283
  config.pc.onicecandidate = (event) => {
2161
2284
  if (event.candidate == null || (adapter.browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0)) {
2162
2285
  config.iceDone = true;
@@ -2409,9 +2532,6 @@ class RoomSession {
2409
2532
 
2410
2533
  config.isIceRestarting = true;
2411
2534
 
2412
- // removing this so we can cache ice candidates again
2413
- config.remoteSdp = null;
2414
-
2415
2535
  if (this.handleId === handleId) {
2416
2536
  this._log('Performing local ICE restart');
2417
2537
  let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);
@@ -2604,6 +2724,9 @@ class RoomSession {
2604
2724
  }
2605
2725
 
2606
2726
  #publishRemote(handleId, jsep) {
2727
+
2728
+ console.log('picaaaa');
2729
+
2607
2730
  let handle = this.#getHandle(handleId);
2608
2731
  if (!handle) {
2609
2732
  return Promise.reject({
@@ -2682,7 +2805,7 @@ class RoomSession {
2682
2805
  if (!config.stream) {
2683
2806
  return;
2684
2807
  }
2685
- let sourceTrackIds = (config.streamMap[this.id][source] || []);
2808
+ let sourceTrackIds = (config.streamMap[this.id]?.[source] || []);
2686
2809
  let remainingTracks = [];
2687
2810
  for(let i = 0; i < sourceTrackIds.length; i++) {
2688
2811
  let foundTrack = config.tracks.find(t => t.id === sourceTrackIds[i]);
@@ -2752,7 +2875,7 @@ class RoomSession {
2752
2875
  let existingTracks = [...(config.streamMap?.[this.id]?.[source] || [])];
2753
2876
 
2754
2877
  if(stream?.getTracks().length) {
2755
- if(!config.streamMap[this.id]) {
2878
+ if(!config.streamMap?.[this.id]) {
2756
2879
  config.streamMap[this.id] = {};
2757
2880
  }
2758
2881
  config.streamMap[this.id][source] = stream?.getTracks()?.map(track => track.id) || [];
@@ -2772,8 +2895,8 @@ class RoomSession {
2772
2895
  } catch (e) {
2773
2896
  this._log(e);
2774
2897
  }
2775
- config.stream.removeTrack(oldAudioStream);
2776
- config?.tracks.splice(oldAudioStreamIndex, 1);
2898
+ config?.stream?.removeTrack(oldAudioStream);
2899
+ config?.tracks?.splice(oldAudioStreamIndex, 1);
2777
2900
  }
2778
2901
 
2779
2902
  // remove old video track related to this source
@@ -2860,7 +2983,7 @@ class RoomSession {
2860
2983
  // we possibly created new transceivers, so we need to get them again
2861
2984
 
2862
2985
  transceivers = config.pc.getTransceivers();
2863
- existingTracks = [...(config.streamMap[this.id][source] || [])];
2986
+ existingTracks = [...(config.streamMap[this.id]?.[source] || [])];
2864
2987
  if(!audioTransceiver) {
2865
2988
  audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && existingTracks.includes(transceiver.sender.track.id))
2866
2989
  }
@@ -2919,7 +3042,7 @@ class RoomSession {
2919
3042
  }
2920
3043
 
2921
3044
  this.isMuted = [];
2922
- for(const source of Object.keys(config.streamMap[this.id])) {
3045
+ for(const source of Object.keys(config.streamMap[this.id]??{})) {
2923
3046
  const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
2924
3047
  const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
2925
3048
  this.isMuted.push({type: 'audio', value: !audioTrack || !audioTrack.enabled, source, mid: transceivers.find(transceiver => transceiver.sender?.track?.kind === 'audio' && transceiver.sender?.track?.id === audioTrack?.id)?.mid});
@@ -3003,7 +3126,7 @@ class RoomSession {
3003
3126
  let transceivers = config.pc?.getTransceivers();
3004
3127
  let transceiver = null;
3005
3128
  if(source) {
3006
- transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (config.streamMap[this.id][source] || []).includes(t.sender.track.id));
3129
+ transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (config.streamMap[this.id]?.[source] || []).includes(t.sender.track.id));
3007
3130
  }
3008
3131
  else {
3009
3132
  transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "audio" && (mid ? t.mid === mid : true));
@@ -3013,7 +3136,7 @@ class RoomSession {
3013
3136
  }
3014
3137
 
3015
3138
  this.isMuted = [];
3016
- for(const source of Object.keys(config.streamMap[this.id])) {
3139
+ for(const source of Object.keys(config.streamMap[this.id]??{})) {
3017
3140
  const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
3018
3141
  const audioTransceiver = transceivers?.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === audioTrack?.id);
3019
3142
  const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
@@ -3040,7 +3163,7 @@ class RoomSession {
3040
3163
  let transceivers = config.pc?.getTransceivers();
3041
3164
  let transceiver = null;
3042
3165
  if(source) {
3043
- transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (config.streamMap[this.id][source] || []).includes(t.sender.track.id));
3166
+ transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (config.streamMap[this.id]?.[source] || []).includes(t.sender.track.id));
3044
3167
  }
3045
3168
  else {
3046
3169
  transceiver = transceivers?.find(t => t.sender && t.sender.track && t.receiver.track.kind === "video" && (mid ? t.mid === mid : true));
@@ -3049,7 +3172,7 @@ class RoomSession {
3049
3172
  transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;
3050
3173
  }
3051
3174
  this.isMuted = [];
3052
- for(const source of Object.keys(config.streamMap[this.id])) {
3175
+ for(const source of Object.keys(config.streamMap[this.id]??{})) {
3053
3176
  const audioTrack = config.stream?.getAudioTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
3054
3177
  const audioTransceiver = transceivers?.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === audioTrack?.id);
3055
3178
  const videoTrack = config.stream?.getVideoTracks()?.find(track => config.streamMap[this.id][source].includes(track.id));
@@ -3172,7 +3295,7 @@ class RoomSession {
3172
3295
  let config = handle.webrtcStuff;
3173
3296
  let descriptions = [];
3174
3297
  let transceivers = config.pc.getTransceivers();
3175
- Object.keys(config.streamMap[this.id]).forEach(source => {
3298
+ Object.keys(config.streamMap[this.id]??{}).forEach(source => {
3176
3299
  const simulcastConfigForSource = this.#findSimulcastConfig(source, this.simulcastSettings);
3177
3300
  config.streamMap[this.id][source].forEach(trackId => {
3178
3301
  let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId)