@flashphoner/websdk 2.0.222 → 2.0.224

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flashphoner/websdk",
3
- "version": "2.0.222",
3
+ "version": "2.0.224",
4
4
  "description": "Official Flashphoner WebCallServer WebSDK package",
5
5
  "main": "./src/flashphoner-core.js",
6
6
  "types": "./src/flashphoner-core.d.ts",
@@ -819,6 +819,7 @@ var createSession = function (options) {
819
819
  * @param {Array<string>=} options.sipSDP Array of custom SDP params (ex. bandwidth (b=))
820
820
  * @param {Array<string>=} options.sipHeaders Array of custom SIP headers
821
821
  * @param {string=} options.videoContentHint Video content hint for browser ('detail' by default to maintain resolution), {@link Flashphoner.constants.CONTENT_HINT_TYPE}
822
+ * @param {Boolean=} options.useControls Use a standard HTML5 video controls (play, pause, fullscreen). May be a workaround for fullscreen mode to work in Safari 16
822
823
  * @param {Object=} options.logger Call logger options
823
824
  * @param {sdpHook} sdpHook The callback that handles sdp from the server
824
825
  * @returns {Call} Call
@@ -883,6 +884,7 @@ var createSession = function (options) {
883
884
  var sipSDP = options.sipSDP;
884
885
  var sipHeaders = options.sipHeaders;
885
886
  var videoContentHint = options.videoContentHint;
887
+ var useControls = options.useControls;
886
888
  /**
887
889
  * Represents sip call.
888
890
  *
@@ -979,6 +981,7 @@ var createSession = function (options) {
979
981
  connectionConfig: mediaOptions,
980
982
  audioOutputId: audioOutputId,
981
983
  videoContentHint: videoContentHint,
984
+ useControls: useControls,
982
985
  logger: logger
983
986
  }).then(function (newConnection) {
984
987
  mediaConnection = newConnection;
@@ -1117,7 +1120,8 @@ var createSession = function (options) {
1117
1120
  login: cConfig.sipLogin,
1118
1121
  constraints: constraints,
1119
1122
  connectionConfig: mediaOptions,
1120
- audioOutputId: audioOutputId
1123
+ audioOutputId: audioOutputId,
1124
+ useControls: useControls
1121
1125
  }).then(function (newConnection) {
1122
1126
  mediaConnection = newConnection;
1123
1127
  return mediaConnection.setRemoteSdp(sdp);
@@ -1595,6 +1599,7 @@ var createSession = function (options) {
1595
1599
  * @param {string=} options.useCanvasMediaStream EXPERIMENTAL: when publish bind browser's media stream to the canvas. It can be useful for image filtering
1596
1600
  * @param {string=} options.videoContentHint Video content hint for browser ('detail' by default to maintain resolution), {@link Flashphoner.constants.CONTENT_HINT_TYPE}
1597
1601
  * @param {Boolean=} options.unmutePlayOnStart Unmute playback on start. May be used after user gesture only, so set 'unmutePlayOnStart: false' for autoplay
1602
+ * @param {Boolean=} options.useControls Use a standard HTML5 video controls (play, pause, fullscreen). May be a workaround for fullscreen mode to work in Safari 16
1598
1603
  * @param {Object=} options.logger Stream logger options
1599
1604
  * @param {sdpHook} sdpHook The callback that handles sdp from the server
1600
1605
  * @returns {Stream} Stream
@@ -1705,6 +1710,7 @@ var createSession = function (options) {
1705
1710
  var useCanvasMediaStream = options.useCanvasMediaStream;
1706
1711
  var videoContentHint = options.videoContentHint;
1707
1712
  var unmutePlayOnStart = options.unmutePlayOnStart;
1713
+ var useControls = options.useControls;
1708
1714
 
1709
1715
  var audioState_;
1710
1716
  var videoState_;
@@ -1890,6 +1896,7 @@ var createSession = function (options) {
1890
1896
  remoteVideo: remoteVideo,
1891
1897
  playoutDelay: playoutDelay,
1892
1898
  unmutePlayOnStart: unmutePlayOnStart,
1899
+ useControls: useControls,
1893
1900
  logger: logger
1894
1901
  }, streamRefreshHandlers[id_]).then(function (newConnection) {
1895
1902
  mediaConnection = newConnection;
@@ -1979,6 +1986,7 @@ var createSession = function (options) {
1979
1986
  connectionConstraints: mediaConnectionConstraints,
1980
1987
  customStream: constraints && constraints.customStream ? constraints.customStream : false,
1981
1988
  videoContentHint: videoContentHint,
1989
+ useControls: useControls,
1982
1990
  logger: logger
1983
1991
  }).then(function (newConnection) {
1984
1992
  mediaConnection = newConnection;
package/src/util.js CHANGED
@@ -92,7 +92,7 @@ const Browser = {
92
92
  return !!window.chrome && /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) && !/OPR/.test(navigator.userAgent);
93
93
  },
94
94
  isEdge: function () {
95
- return !isIE && !!window.StyleMedia;
95
+ return !this.isIE() && !!window.StyleMedia;
96
96
  },
97
97
  isOpera: function () {
98
98
  return (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
@@ -118,6 +118,33 @@ const Browser = {
118
118
  },
119
119
  isChromiumEdge: function () {
120
120
  return /Chrome/i.test(navigator.userAgent) && /Edg/i.test(navigator.userAgent);
121
+ },
122
+ version: function () {
123
+ var version = navigator.userAgent.match(/version\/(\d+)/i);
124
+ if (version) {
125
+ return version[1];
126
+ } else {
127
+ if (this.isEdge()) {
128
+ version = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
129
+ }
130
+ if (this.isOpera()) {
131
+ version = navigator.userAgent.match(/\b(OPR)\/(\d+)/) || [];
132
+ }
133
+ if (this.isChromiumEdge()) {
134
+ version = navigator.userAgent.match(/\b(Edg)\/(\d+)/) || [];
135
+ }
136
+ if (this.isSamsungBrowser()) {
137
+ version = navigator.userAgent.match(/\b(SamsungBrowser)\/(\d+)/) || [];
138
+ }
139
+ if (this.isChrome()) {
140
+ version = navigator.userAgent.match(/\b(Chrome)\/(\d+)/) || [];
141
+ }
142
+ if (this.isFirefox()) {
143
+ version = navigator.userAgent.match(/\b(Firefox)\/(\d+)/) || [];
144
+ }
145
+ return version[2] || 0;
146
+ }
147
+ return 0;
121
148
  }
122
149
  };
123
150
 
@@ -366,9 +393,9 @@ const stripCodecs = function(sdp, codecs) {
366
393
  const getCurrentCodecAndSampleRate = function(sdp, mediaType) {
367
394
  var rows = sdp.split("\n");
368
395
  var codecPt;
396
+ var ret = {};
369
397
  for (var i = 0; i < rows.length ; i++) {
370
398
  if (codecPt && rows[i].indexOf("a=rtpmap:" + codecPt) != -1) {
371
- var ret = {};
372
399
  ret.name = rows[i].split(" ")[1].split("/")[0];
373
400
  ret.sampleRate = rows[i].split(" ")[1].split("/")[1];
374
401
  return ret;
@@ -378,8 +405,22 @@ const getCurrentCodecAndSampleRate = function(sdp, mediaType) {
378
405
  codecPt = rows[i].split(" ")[3].trim();
379
406
  }
380
407
  }
408
+ // A workaround for empty sdp passed #WCS-3583
409
+ ret.name = "undefined";
410
+ ret.sampleRate = "undefined";
411
+ return ret;
381
412
  };
382
413
 
414
+ const isPromise = function(object) {
415
+ if (object !== null &&
416
+ typeof object === 'object' &&
417
+ typeof object.then === 'function' &&
418
+ typeof object.catch === 'function') {
419
+ return true;
420
+ }
421
+
422
+ return false;
423
+ };
383
424
 
384
425
  module.exports = {
385
426
  isEmptyObject,
@@ -390,5 +431,6 @@ module.exports = {
390
431
  SDP,
391
432
  logger,
392
433
  stripCodecs,
393
- getCurrentCodecAndSampleRate
434
+ getCurrentCodecAndSampleRate,
435
+ isPromise
394
436
  };
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ -'use strict';
2
2
 
3
3
  var browserDetails = require('webrtc-adapter').default.browserDetails;
4
4
  const { v1: uuid_v1 } = require('uuid');
@@ -59,6 +59,8 @@ var createConnection = function (options) {
59
59
  var videoContentHint = options.videoContentHint ? options.videoContentHint : 'detail';
60
60
  // Pass the option to unmute automatically (true by default) #WCS-2425
61
61
  var unmutePlayOnStart = options.unmutePlayOnStart !== undefined ? options.unmutePlayOnStart : true;
62
+ // Use a standard HTML5 video controls if needed (to enable fullscreen in Safari 16 for example) #WCS-3606
63
+ var useControls = options.useControls || false;
62
64
 
63
65
  if (bidirectional) {
64
66
  localVideo = getCacheInstance(localDisplay);
@@ -74,7 +76,7 @@ var createConnection = function (options) {
74
76
  }
75
77
  remoteVideo = getCacheInstance(remoteDisplay);
76
78
  if (!remoteVideo) {
77
- remoteVideo = createVideoElement();
79
+ remoteVideo = createVideoElement(useControls);
78
80
  remoteDisplay.appendChild(remoteVideo);
79
81
  }
80
82
  remoteVideo.id = id + "-remote";
@@ -95,7 +97,7 @@ var createConnection = function (options) {
95
97
  if (cachedVideo) {
96
98
  remoteVideo = cachedVideo;
97
99
  } else {
98
- remoteVideo = createVideoElement();
100
+ remoteVideo = createVideoElement(useControls);
99
101
  display.appendChild(remoteVideo);
100
102
  }
101
103
  remoteVideo.id = id;
@@ -113,9 +115,18 @@ var createConnection = function (options) {
113
115
  setContentHint(localVideo.srcObject, videoContentHint);
114
116
  connection.addStream(localVideo.srcObject);
115
117
  }
118
+ } else {
119
+ // There is a custom video element, get its id if set #WCS-3606
120
+ if (remoteVideo.id) {
121
+ id = remoteVideo.id;
122
+ }
116
123
  }
117
124
  }
118
125
  if (localVideo) {
126
+ // Enable local video controls if option requires #WCS-3606
127
+ if (useControls) {
128
+ enableVideoControls(localVideo);
129
+ }
119
130
  var videoTrack = localVideo.srcObject.getVideoTracks()[0];
120
131
  if (videoTrack) {
121
132
  videoCams.forEach((cam, index) => {
@@ -133,6 +144,12 @@ var createConnection = function (options) {
133
144
  });
134
145
  }
135
146
  }
147
+ if (remoteVideo) {
148
+ // Enable remote video controls if option requires #WCS-3606
149
+ if (useControls) {
150
+ enableVideoControls(remoteVideo);
151
+ }
152
+ }
136
153
  function setContentHint(stream, hint) {
137
154
  stream.getVideoTracks().forEach(function(track) {
138
155
  if(track.contentHint === undefined) {
@@ -456,7 +473,14 @@ var createConnection = function (options) {
456
473
  obj[mediaType] = {};
457
474
  //WCS-1922, currentRemoteDescription - browser compatibilitySection: Chrome 70, FF 57, Safari 11
458
475
  var description = connection.currentRemoteDescription != undefined ? connection.currentRemoteDescription : connection.remoteDescription;
459
- var codec = util.getCurrentCodecAndSampleRate(description.sdp, mediaType);
476
+ // SDP may be null in Safari 12.1 and older, prevent TypeError here #WCS-3583
477
+ var sdp = "";
478
+ if (description && description.sdp) {
479
+ sdp = description.sdp;
480
+ } else {
481
+ logger.debug(LOG_PREFIX, "Can't parse current SDP to detect codec and sampleRate");
482
+ }
483
+ var codec = util.getCurrentCodecAndSampleRate(sdp, mediaType);
460
484
  obj[mediaType]["codec"] = codec.name;
461
485
  obj[mediaType]["codecRate"] = codec.sampleRate;
462
486
  Object.keys(report).forEach(function (key) {
@@ -473,7 +497,13 @@ var createConnection = function (options) {
473
497
  if (!document.fullscreenElement && !document.mozFullScreenElement &&
474
498
  !document.webkitFullscreenElement && !document.msFullscreenElement) {
475
499
  if (video.requestFullscreen) {
476
- video.requestFullscreen();
500
+ var result = video.requestFullscreen();
501
+ // Chromium based browsers return a promise which is rejected although user click is present #WCS-3606
502
+ if (util.isPromise(result)) {
503
+ result.catch(function(e) {
504
+ logger.debug(LOG_PREFIX, e);
505
+ });
506
+ }
477
507
  } else if (video.msRequestFullscreen) {
478
508
  video.msRequestFullscreen();
479
509
  } else if (video.mozRequestFullScreen) {
@@ -482,10 +512,18 @@ var createConnection = function (options) {
482
512
  video.webkitRequestFullscreen();
483
513
  } else if (video.webkitEnterFullscreen) {
484
514
  video.webkitEnterFullscreen();
485
- //hack for iOS safari. Video is getting paused when switching from fullscreen to normal mode.
515
+ // iOS (all versions)/MacOS (since 15) Safari hack: video is paused when leaving fullscreen mode #WCS-3606
516
+ var needRestart = false;
486
517
  video.addEventListener("pause", function () {
487
- video.play();
518
+ if(needRestart) {
519
+ video.play();
520
+ needRestart = false;
521
+ }
488
522
  });
523
+ video.addEventListener("webkitendfullscreen", function () {
524
+ video.play();
525
+ needRestart = true;
526
+ });
489
527
  }
490
528
  } else {
491
529
  if (document.exitFullscreen) {
@@ -861,7 +899,9 @@ var loadOrdinaryVideo = function(display, stream, screenShare, constraints, vide
861
899
  vEl = createVideoElement();
862
900
  display.appendChild(vEl);
863
901
  }
864
- vEl.id = uuid_v1() + LOCAL_CACHED_VIDEO;
902
+ if (!vEl.id) {
903
+ vEl.id = uuid_v1() + LOCAL_CACHED_VIDEO;
904
+ }
865
905
  vEl.srcObject = stream;
866
906
  vEl.onloadedmetadata = function (e) {
867
907
  //WCS-2751 Add screen capture using getDisplayMedia in Safari
@@ -889,7 +929,9 @@ var loadCanvasVideo = function (display, stream, video) {
889
929
  vEl = canvas;
890
930
  }
891
931
  }
892
- vEl.id = uuid_v1() + LOCAL_CACHED_VIDEO;
932
+ if (!vEl.id) {
933
+ vEl.id = uuid_v1() + LOCAL_CACHED_VIDEO;
934
+ }
893
935
 
894
936
  let child = vEl.children[0];
895
937
  child.srcObject = stream;
@@ -1196,13 +1238,16 @@ function getCacheInstance(display) {
1196
1238
  }
1197
1239
  }
1198
1240
 
1199
- function createVideoElement() {
1241
+ function createVideoElement(useControls = false) {
1200
1242
  let video = document.createElement('video');
1201
1243
  // Prepare video tag to auto play and add specific Safari tweaks #WCS-2425
1202
1244
  video.muted = true;
1203
- if(util.Browser.isSafariWebRTC()) {
1204
- video.setAttribute("playsinline", "");
1205
- video.setAttribute("webkit-playsinline", "");
1245
+ if (util.Browser.isSafariWebRTC()) {
1246
+ video.setAttribute("playsinline", "");
1247
+ video.setAttribute("webkit-playsinline", "");
1248
+ }
1249
+ if (useControls) {
1250
+ enableVideoControls(video);
1206
1251
  }
1207
1252
  return(video);
1208
1253
  }
@@ -1228,6 +1273,12 @@ function removeVideoElement(video) {
1228
1273
  }
1229
1274
  }
1230
1275
 
1276
+ function enableVideoControls(video) {
1277
+ if(video && !video.controls) {
1278
+ video.setAttribute("controls", "controls");
1279
+ }
1280
+ }
1281
+
1231
1282
  /**
1232
1283
  * Check WebRTC available
1233
1284
  *
@@ -1398,12 +1449,10 @@ var playFirstSound = function () {
1398
1449
  return false;
1399
1450
  };
1400
1451
 
1401
- var playFirstVideo = function (display, isLocal, src) {
1452
+ var playFirstVideo = function (display, isLocal, src, useControls = false) {
1402
1453
  return new Promise(function (resolve, reject) {
1403
1454
  if (!getCacheInstance(display)) {
1404
- var video = document.createElement('video');
1405
- video.setAttribute("playsinline", "");
1406
- video.setAttribute("webkit-playsinline", "");
1455
+ var video = createVideoElement(useControls);
1407
1456
  //Mute video tag to prevent local audio playback in Safari #WCS-3430
1408
1457
  video.muted = true;
1409
1458
  video.id = uuid_v1() + (isLocal ? LOCAL_CACHED_VIDEO : REMOTE_CACHED_VIDEO);
@@ -1414,6 +1463,7 @@ var playFirstVideo = function (display, isLocal, src) {
1414
1463
  video.src = src;
1415
1464
  video.play().then(function () {
1416
1465
  display.appendChild(video);
1466
+ video.removeAttribute("src");
1417
1467
  resolve();
1418
1468
  }).catch(function () {
1419
1469
  //WCS-2375. fixed autoplay in ios safari
@@ -1421,6 +1471,7 @@ var playFirstVideo = function (display, isLocal, src) {
1421
1471
  video.muted = true;
1422
1472
  video.play().then(function () {
1423
1473
  display.appendChild(video);
1474
+ video.removeAttribute("src");
1424
1475
  resolve();
1425
1476
  });
1426
1477
  //WCS-2375. low power mode suspends video play