@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.1.4 → 0.2.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.
Files changed (94) hide show
  1. package/.eslintrc.js +1 -1
  2. package/.prettierrc.json +1 -0
  3. package/dist/lib-pixelstreamingfrontend.esm.js +1 -1
  4. package/dist/lib-pixelstreamingfrontend.js +1 -1
  5. package/package.json +6 -5
  6. package/src/AFK/AFKController.ts +10 -32
  7. package/src/Config/Config.ts +179 -201
  8. package/src/Config/SettingBase.ts +61 -2
  9. package/src/Config/SettingFlag.ts +10 -48
  10. package/src/Config/SettingNumber.ts +10 -28
  11. package/src/Config/SettingOption.ts +13 -46
  12. package/src/Config/SettingText.ts +9 -37
  13. package/src/DataChannel/DataChannelController.ts +6 -26
  14. package/src/DataChannel/DataChannelLatencyTestController.ts +38 -33
  15. package/src/DataChannel/DataChannelLatencyTestResults.ts +8 -10
  16. package/src/DataChannel/DataChannelSender.ts +5 -15
  17. package/src/DataChannel/LatencyTestResults.ts +5 -15
  18. package/src/FreezeFrame/FreezeFrame.ts +7 -19
  19. package/src/FreezeFrame/FreezeFrameController.ts +3 -14
  20. package/src/Inputs/GamepadController.ts +123 -221
  21. package/src/Inputs/GamepadTypes.ts +23 -0
  22. package/src/Inputs/IInputController.ts +17 -0
  23. package/src/Inputs/InputClassesFactory.ts +38 -45
  24. package/src/Inputs/KeyCodes.ts +114 -0
  25. package/src/Inputs/KeyboardController.ts +49 -232
  26. package/src/Inputs/MouseController.ts +71 -297
  27. package/src/Inputs/MouseControllerHovering.ts +118 -0
  28. package/src/Inputs/MouseControllerLocked.ts +194 -0
  29. package/src/Inputs/TouchController.ts +49 -105
  30. package/src/Inputs/TouchControllerFake.ts +132 -0
  31. package/src/Inputs/XRGamepadController.ts +35 -44
  32. package/src/PeerConnectionController/AggregatedStats.ts +26 -54
  33. package/src/PeerConnectionController/CandidatePairStats.ts +1 -1
  34. package/src/PeerConnectionController/CandidateStat.ts +1 -1
  35. package/src/PeerConnectionController/PeerConnectionController.ts +177 -162
  36. package/src/PixelStreaming/PixelStreaming.ts +174 -226
  37. package/src/UI/OnScreenKeyboard.ts +14 -9
  38. package/src/UeInstanceMessage/ResponseController.ts +6 -15
  39. package/src/UeInstanceMessage/SendMessageController.ts +16 -18
  40. package/src/UeInstanceMessage/StreamMessageController.ts +3 -12
  41. package/src/UeInstanceMessage/ToStreamerMessagesController.ts +3 -9
  42. package/src/Util/EventEmitter.ts +17 -22
  43. package/src/Util/FileUtil.ts +11 -34
  44. package/src/Util/IURLSearchParams.ts +25 -0
  45. package/src/Util/InputCoordTranslator.ts +73 -0
  46. package/src/Util/RTCUtils.ts +23 -15
  47. package/src/VideoPlayer/StreamController.ts +6 -23
  48. package/src/VideoPlayer/VideoPlayer.ts +9 -30
  49. package/src/WebRtcPlayer/WebRtcPlayerController.ts +328 -690
  50. package/src/WebXR/WebXRController.ts +82 -94
  51. package/src/pixelstreamingfrontend.ts +6 -10
  52. package/types/AFK/AFKController.d.ts +0 -1
  53. package/types/Config/Config.d.ts +6 -5
  54. package/types/Config/SettingBase.d.ts +13 -0
  55. package/types/Config/SettingFlag.d.ts +1 -10
  56. package/types/Config/SettingNumber.d.ts +1 -5
  57. package/types/Config/SettingOption.d.ts +1 -10
  58. package/types/Config/SettingText.d.ts +1 -9
  59. package/types/DataChannel/DataChannelLatencyTestController.d.ts +1 -1
  60. package/types/Inputs/GamepadController.d.ts +22 -46
  61. package/types/Inputs/GamepadTypes.d.ts +7 -0
  62. package/types/Inputs/IInputController.d.ts +16 -0
  63. package/types/Inputs/InputClassesFactory.d.ts +7 -8
  64. package/types/Inputs/KeyCodes.d.ts +5 -0
  65. package/types/Inputs/KeyboardController.d.ts +17 -45
  66. package/types/Inputs/MouseController.d.ts +33 -68
  67. package/types/Inputs/MouseControllerHovering.d.ts +26 -0
  68. package/types/Inputs/MouseControllerLocked.d.ts +31 -0
  69. package/types/Inputs/TouchController.d.ts +19 -44
  70. package/types/Inputs/TouchControllerFake.d.ts +29 -0
  71. package/types/Inputs/XRGamepadController.d.ts +0 -7
  72. package/types/PeerConnectionController/PeerConnectionController.d.ts +4 -1
  73. package/types/PixelStreaming/PixelStreaming.d.ts +14 -2
  74. package/types/UI/OnScreenKeyboard.d.ts +2 -2
  75. package/types/Util/EventEmitter.d.ts +1 -1
  76. package/types/Util/IURLSearchParams.d.ts +9 -0
  77. package/types/Util/InputCoordTranslator.d.ts +29 -0
  78. package/types/VideoPlayer/StreamController.d.ts +0 -2
  79. package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +19 -17
  80. package/types/pixelstreamingfrontend.d.ts +1 -1
  81. package/src/Inputs/FakeTouchController.ts +0 -199
  82. package/src/Inputs/HoveringMouseEvents.ts +0 -192
  83. package/src/Inputs/IMouseEvents.ts +0 -64
  84. package/src/Inputs/ITouchController.ts +0 -29
  85. package/src/Inputs/LockedMouseEvents.ts +0 -287
  86. package/src/Util/CoordinateConverter.ts +0 -290
  87. package/src/Util/EventListenerTracker.ts +0 -29
  88. package/types/Inputs/FakeTouchController.d.ts +0 -61
  89. package/types/Inputs/HoveringMouseEvents.d.ts +0 -56
  90. package/types/Inputs/IMouseEvents.d.ts +0 -53
  91. package/types/Inputs/ITouchController.d.ts +0 -24
  92. package/types/Inputs/LockedMouseEvents.d.ts +0 -80
  93. package/types/Util/CoordinateConverter.d.ts +0 -100
  94. package/types/Util/EventListenerTracker.d.ts +0 -14
@@ -23,11 +23,7 @@ export class PeerConnectionController {
23
23
  * @param options - Peer connection Options
24
24
  * @param config - The config for our PS experience.
25
25
  */
26
- constructor(
27
- options: RTCConfiguration,
28
- config: Config,
29
- preferredCodec: string
30
- ) {
26
+ constructor(options: RTCConfiguration, config: Config, preferredCodec: string) {
31
27
  this.config = config;
32
28
  this.createPeerConnection(options, preferredCodec);
33
29
  }
@@ -36,26 +32,18 @@ export class PeerConnectionController {
36
32
  // Set the ICE transport to relay if TURN enabled
37
33
  if (this.config.isFlagEnabled(Flags.ForceTURN)) {
38
34
  options.iceTransportPolicy = 'relay';
39
- Logger.Log(
40
- Logger.GetStackTrace(),
41
- 'Forcing TURN usage by setting ICE Transport Policy in peer connection config.'
42
- );
35
+ Logger.Info('Forcing TURN usage by setting ICE Transport Policy in peer connection config.');
43
36
  }
44
37
 
45
38
  // build a new peer connection with the options
46
39
  this.peerConnection = new RTCPeerConnection(options);
47
- this.peerConnection.onsignalingstatechange = (ev: Event) =>
48
- this.handleSignalStateChange(ev);
40
+ this.peerConnection.onsignalingstatechange = (ev: Event) => this.handleSignalStateChange(ev);
49
41
  this.peerConnection.oniceconnectionstatechange = (ev: Event) =>
50
42
  this.handleIceConnectionStateChange(ev);
51
- this.peerConnection.onicegatheringstatechange = (ev: Event) =>
52
- this.handleIceGatheringStateChange(ev);
53
- this.peerConnection.ontrack = (ev: RTCTrackEvent) =>
54
- this.handleOnTrack(ev);
55
- this.peerConnection.onicecandidate = (ev: RTCPeerConnectionIceEvent) =>
56
- this.handleIceCandidate(ev);
57
- this.peerConnection.ondatachannel = (ev: RTCDataChannelEvent) =>
58
- this.handleDataChannel(ev);
43
+ this.peerConnection.onicegatheringstatechange = (ev: Event) => this.handleIceGatheringStateChange(ev);
44
+ this.peerConnection.ontrack = (ev: RTCTrackEvent) => this.handleOnTrack(ev);
45
+ this.peerConnection.onicecandidate = (ev: RTCPeerConnectionIceEvent) => this.handleIceCandidate(ev);
46
+ this.peerConnection.ondatachannel = (ev: RTCDataChannelEvent) => this.handleDataChannel(ev);
59
47
  this.aggregatedStats = new AggregatedStats();
60
48
  this.preferredCodec = preferredCodec;
61
49
  this.updateCodecSelection = true;
@@ -66,26 +54,24 @@ export class PeerConnectionController {
66
54
  * @param offerOptions - RTC Offer Options
67
55
  */
68
56
  async createOffer(offerOptions: RTCOfferOptions, config: Config) {
69
- Logger.Log(Logger.GetStackTrace(), 'Create Offer', 6);
57
+ Logger.Info('Create Offer');
70
58
 
71
- const isLocalhostConnection =
72
- location.hostname === 'localhost' ||
73
- location.hostname === '127.0.0.1';
59
+ const isLocalhostConnection = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
74
60
  const isHttpsConnection = location.protocol === 'https:';
75
61
  let useMic = config.isFlagEnabled(Flags.UseMic);
76
- if (useMic && !(isLocalhostConnection || isHttpsConnection)) {
62
+ let useCamera = config.isFlagEnabled(Flags.UseCamera);
63
+ if ((useMic || useCamera) && !(isLocalhostConnection || isHttpsConnection)) {
77
64
  useMic = false;
65
+ useCamera = false;
78
66
  Logger.Error(
79
- Logger.GetStackTrace(),
80
- 'Microphone access in the browser will not work if you are not on HTTPS or localhost. Disabling mic access.'
67
+ 'Microphone and Webcam access in the browser will not work if you are not on HTTPS or localhost. Disabling mic and webcam access.'
81
68
  );
82
69
  Logger.Error(
83
- Logger.GetStackTrace(),
84
70
  "For testing you can enable HTTP microphone access Chrome by visiting chrome://flags/ and enabling 'unsafely-treat-insecure-origin-as-secure'"
85
71
  );
86
72
  }
87
73
 
88
- this.setupTransceiversAsync(useMic).finally(() => {
74
+ this.setupTransceiversAsync(useMic, useCamera).finally(() => {
89
75
  this.peerConnection
90
76
  ?.createOffer(offerOptions)
91
77
  .then((offer: RTCSessionDescriptionInit) => {
@@ -104,31 +90,32 @@ export class PeerConnectionController {
104
90
  *
105
91
  */
106
92
  async receiveOffer(offer: RTCSessionDescriptionInit, config: Config) {
107
-
108
- Logger.Log(Logger.GetStackTrace(), 'Receive Offer', 6);
93
+ Logger.Info('Receive Offer');
109
94
 
110
95
  this.peerConnection?.setRemoteDescription(offer).then(() => {
111
96
  const isLocalhostConnection =
112
- location.hostname === 'localhost' ||
113
- location.hostname === '127.0.0.1';
97
+ location.hostname === 'localhost' || location.hostname === '127.0.0.1';
114
98
  const isHttpsConnection = location.protocol === 'https:';
115
99
  let useMic = config.isFlagEnabled(Flags.UseMic);
116
- if (useMic && !(isLocalhostConnection || isHttpsConnection)) {
100
+ let useCamera = config.isFlagEnabled(Flags.UseCamera);
101
+ if ((useMic || useCamera) && !(isLocalhostConnection || isHttpsConnection)) {
117
102
  useMic = false;
103
+ useCamera = false;
118
104
  Logger.Error(
119
- Logger.GetStackTrace(),
120
- 'Microphone access in the browser will not work if you are not on HTTPS or localhost. Disabling mic access.'
105
+ 'Microphone and Webcam access in the browser will not work if you are not on HTTPS or localhost. Disabling mic and webcam access.'
121
106
  );
122
107
  Logger.Error(
123
- Logger.GetStackTrace(),
124
108
  "For testing you can enable HTTP microphone access Chrome by visiting chrome://flags/ and enabling 'unsafely-treat-insecure-origin-as-secure'"
125
109
  );
126
110
  }
127
111
 
128
112
  // Add our list of preferred codecs, in order of preference
129
- this.config.setOptionSettingOptions(OptionParameters.PreferredCodec, this.fuzzyIntersectUEAndBrowserCodecs(offer));
113
+ this.config.setOptionSettingOptions(
114
+ OptionParameters.PreferredCodec,
115
+ this.fuzzyIntersectUEAndBrowserCodecs(offer)
116
+ );
130
117
 
131
- this.setupTransceiversAsync(useMic).finally(() => {
118
+ this.setupTransceiversAsync(useMic, useCamera).finally(() => {
132
119
  this.peerConnection
133
120
  ?.createAnswer()
134
121
  .then((Answer: RTCSessionDescriptionInit) => {
@@ -136,21 +123,16 @@ export class PeerConnectionController {
136
123
  return this.peerConnection?.setLocalDescription(Answer);
137
124
  })
138
125
  .then(() => {
139
- this.onSendWebRTCAnswer(
140
- this.peerConnection?.currentLocalDescription
141
- );
126
+ this.onSendWebRTCAnswer(this.peerConnection?.currentLocalDescription);
142
127
  })
143
128
  .catch(() => {
144
- Logger.Error(
145
- Logger.GetStackTrace(),
146
- 'createAnswer() failed'
147
- );
129
+ Logger.Error('createAnswer() failed');
148
130
  });
149
131
  });
150
132
  });
151
133
  }
152
134
 
153
- /**
135
+ /**
154
136
  * Set the Remote Descriptor from the signaling server to the RTC Peer Connection
155
137
  * @param answer - RTC Session Descriptor from the Signaling Server
156
138
  */
@@ -158,7 +140,10 @@ export class PeerConnectionController {
158
140
  this.peerConnection?.setRemoteDescription(answer);
159
141
 
160
142
  // Add our list of preferred codecs, in order of preference
161
- this.config.setOptionSettingOptions(OptionParameters.PreferredCodec, this.fuzzyIntersectUEAndBrowserCodecs(answer));
143
+ this.config.setOptionSettingOptions(
144
+ OptionParameters.PreferredCodec,
145
+ this.fuzzyIntersectUEAndBrowserCodecs(answer)
146
+ );
162
147
  }
163
148
 
164
149
  /**
@@ -178,9 +163,7 @@ export class PeerConnectionController {
178
163
  if (this.updateCodecSelection && !!this.aggregatedStats.inboundVideoStats.codecId) {
179
164
  this.config.setOptionSettingValue(
180
165
  OptionParameters.PreferredCodec,
181
- this.aggregatedStats.codecs.get(
182
- this.aggregatedStats.inboundVideoStats.codecId
183
- )
166
+ this.aggregatedStats.codecs.get(this.aggregatedStats.inboundVideoStats.codecId)
184
167
  );
185
168
  }
186
169
  });
@@ -217,9 +200,7 @@ export class PeerConnectionController {
217
200
  }
218
201
 
219
202
  // Force mono or stereo based on whether ?forceMono was passed or not
220
- audioSDP += this.config.isFlagEnabled(Flags.ForceMonoAudio)
221
- ? 'stereo=0;'
222
- : 'stereo=1;';
203
+ audioSDP += this.config.isFlagEnabled(Flags.ForceMonoAudio) ? 'stereo=0;' : 'stereo=1;';
223
204
 
224
205
  // enable in-band forward error correction for opus audio
225
206
  audioSDP += 'useinbandfec=1';
@@ -235,16 +216,14 @@ export class PeerConnectionController {
235
216
  * @param iceCandidate - RTC Ice Candidate from the Signaling Server
236
217
  */
237
218
  handleOnIce(iceCandidate: RTCIceCandidate) {
238
- Logger.Log(Logger.GetStackTrace(), 'peerconnection handleOnIce', 6);
219
+ Logger.Info('peerconnection handleOnIce');
239
220
 
240
221
  // // if forcing TURN, reject any candidates not relay
241
222
  if (this.config.isFlagEnabled(Flags.ForceTURN)) {
242
223
  // check if no relay address is found, if so, we are assuming it means no TURN server
243
224
  if (iceCandidate.candidate.indexOf('relay') < 0) {
244
225
  Logger.Info(
245
- Logger.GetStackTrace(),
246
- `Dropping candidate because it was not TURN relay. | Type= ${iceCandidate.type} | Protocol= ${iceCandidate.protocol} | Address=${iceCandidate.address} | Port=${iceCandidate.port} |`,
247
- 6
226
+ `Dropping candidate because it was not TURN relay. | Type= ${iceCandidate.type} | Protocol= ${iceCandidate.protocol} | Address=${iceCandidate.address} | Port=${iceCandidate.port} |`
248
227
  );
249
228
  return;
250
229
  }
@@ -258,11 +237,7 @@ export class PeerConnectionController {
258
237
  * @param state - Signaling Server State Change Event
259
238
  */
260
239
  handleSignalStateChange(state: Event) {
261
- Logger.Log(
262
- Logger.GetStackTrace(),
263
- 'signaling state change: ' + state,
264
- 6
265
- );
240
+ Logger.Info('signaling state change: ' + state);
266
241
  }
267
242
 
268
243
  /**
@@ -270,11 +245,7 @@ export class PeerConnectionController {
270
245
  * @param state - Ice Connection State
271
246
  */
272
247
  handleIceConnectionStateChange(state: Event) {
273
- Logger.Log(
274
- Logger.GetStackTrace(),
275
- 'ice connection state change: ' + state,
276
- 6
277
- );
248
+ Logger.Info('ice connection state change: ' + state);
278
249
  this.onIceConnectionStateChange(state);
279
250
  }
280
251
 
@@ -283,11 +254,7 @@ export class PeerConnectionController {
283
254
  * @param state - Ice Gathering State Change
284
255
  */
285
256
  handleIceGatheringStateChange(state: Event) {
286
- Logger.Log(
287
- Logger.GetStackTrace(),
288
- 'ice gathering state change: ' + JSON.stringify(state),
289
- 6
290
- );
257
+ Logger.Info('ice gathering state change: ' + JSON.stringify(state));
291
258
  }
292
259
 
293
260
  /**
@@ -364,23 +331,25 @@ export class PeerConnectionController {
364
331
  * @param sdp The remote sdp
365
332
  * @returns The intersection between browser supported codecs and ue supported codecs.
366
333
  */
367
- fuzzyIntersectUEAndBrowserCodecs(sdp: RTCSessionDescriptionInit) : string[] {
334
+ fuzzyIntersectUEAndBrowserCodecs(sdp: RTCSessionDescriptionInit): string[] {
368
335
  // We want to build an array of all supported codecs on both sides
369
336
  const allSupportedCodecs: Array<string> = new Array<string>();
370
337
  const allUECodecs: string[] = this.parseAvailableCodecs(sdp);
371
- const allBrowserCodecs: string[] = this.config.getSettingOption(OptionParameters.PreferredCodec).options;
372
- for(const ueCodec of allUECodecs) {
338
+ const allBrowserCodecs: string[] = this.config.getSettingOption(
339
+ OptionParameters.PreferredCodec
340
+ ).options;
341
+ for (const ueCodec of allUECodecs) {
373
342
  // Check if browser codecs directly matches UE codec (with parameters and everything)
374
- if(allBrowserCodecs.includes(ueCodec)) {
343
+ if (allBrowserCodecs.includes(ueCodec)) {
375
344
  allSupportedCodecs.push(ueCodec);
376
345
  continue;
377
346
  }
378
347
  // Otherwise check if browser codec at least contains a match for the UE codec name (without parameters).
379
348
  else {
380
- const ueCodecNameAndParams: string[] = ueCodec.split(" ");
349
+ const ueCodecNameAndParams: string[] = ueCodec.split(' ');
381
350
  const ueCodecName = ueCodecNameAndParams[0];
382
- for(const browserCodec of allBrowserCodecs) {
383
- if(browserCodec.includes(ueCodecName)) {
351
+ for (const browserCodec of allBrowserCodecs) {
352
+ if (browserCodec.includes(ueCodecName)) {
384
353
  // We pass browser codec here as they option contain extra parameters.
385
354
  allSupportedCodecs.push(browserCodec);
386
355
  break;
@@ -394,22 +363,31 @@ export class PeerConnectionController {
394
363
  /**
395
364
  * Setup tracks on the RTC Peer Connection
396
365
  * @param useMic - is mic in use
366
+ * @param useCamera - is webcam in use
397
367
  */
398
- async setupTransceiversAsync(useMic: boolean) {
399
-
400
- // Setup a transceiver for receiving video (if we need to)
368
+ async setupTransceiversAsync(useMic: boolean, useCamera: boolean) {
401
369
  let hasVideoReceiver = false;
402
370
  for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
403
- if (transceiver && transceiver.receiver && transceiver.receiver.track && transceiver.receiver.track.kind === 'video') {
371
+ if (
372
+ transceiver &&
373
+ transceiver.receiver &&
374
+ transceiver.receiver.track &&
375
+ transceiver.receiver.track.kind === 'video'
376
+ ) {
404
377
  hasVideoReceiver = true;
405
378
  break;
406
379
  }
407
380
  }
408
- if(!hasVideoReceiver) {
409
- this.peerConnection?.addTransceiver('video', { direction: 'recvonly' });
381
+
382
+ // Setup a transceiver for sending webcam video to UE and receiving video from UE
383
+ if (!useCamera) {
384
+ if (!hasVideoReceiver) {
385
+ this.peerConnection?.addTransceiver('video', { direction: 'recvonly' });
386
+ }
387
+ } else {
388
+ await this.setupVideoSender(hasVideoReceiver);
410
389
  }
411
390
 
412
- // We can only set preferred codec on Chrome
413
391
  if (RTCRtpReceiver.getCapabilities && this.preferredCodec != '') {
414
392
  for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
415
393
  if (
@@ -419,51 +397,52 @@ export class PeerConnectionController {
419
397
  transceiver.receiver.track.kind === 'video' &&
420
398
  transceiver.setCodecPreferences
421
399
  ) {
400
+ // Get our preferred codec from the codecs options drop down
422
401
  const preferredRTPCodec = this.preferredCodec.split(' ');
423
- const codecs = [
424
- {
425
- mimeType:
426
- 'video/' + preferredRTPCodec[0] /* Name */,
427
- clockRate: 90000,
428
- sdpFmtpLine: preferredRTPCodec[1] /* sdpFmtpLine */
429
- ? preferredRTPCodec[1]
430
- : ''
402
+ const preferredRTCRtpCodecCapability: RTCRtpCodecCapability = {
403
+ mimeType: 'video/' + preferredRTPCodec[0] /* Name */,
404
+ clockRate: 90000 /* All current video formats in browsers have 90khz clock rate */,
405
+ sdpFmtpLine: preferredRTPCodec[1] ? preferredRTPCodec[1] : ''
406
+ };
407
+
408
+ // Populate a list of codecs we will support with our preferred one in the first position
409
+ const ourSupportedCodecs: Array<RTCRtpCodecCapability> = [preferredRTCRtpCodecCapability];
410
+
411
+ // Go through all codecs the browser supports and add them to the list (in any order)
412
+ RTCRtpReceiver.getCapabilities('video').codecs.forEach(
413
+ (browserSupportedCodec: RTCRtpCodecCapability) => {
414
+ // Don't add our preferred codec again, but add everything else
415
+ if (browserSupportedCodec.mimeType != preferredRTCRtpCodecCapability.mimeType) {
416
+ ourSupportedCodecs.push(browserSupportedCodec);
417
+ } else if (
418
+ browserSupportedCodec?.sdpFmtpLine !=
419
+ preferredRTCRtpCodecCapability?.sdpFmtpLine
420
+ ) {
421
+ ourSupportedCodecs.push(browserSupportedCodec);
422
+ }
431
423
  }
432
- ];
433
-
434
- this.config
435
- .getSettingOption(OptionParameters.PreferredCodec)
436
- .options.filter((option) => {
437
- // Remove the preferred codec from the list of possible codecs as we've set it already
438
- return option != this.preferredCodec;
439
- })
440
- .forEach((option) => {
441
- // Amend the rest of the browsers supported codecs
442
- const altCodec = option.split(' ');
443
- codecs.push({
444
- mimeType: 'video/' + altCodec[0] /* Name */,
445
- clockRate: 90000,
446
- sdpFmtpLine: altCodec[1] /* sdpFmtpLine */
447
- ? altCodec[1]
448
- : ''
449
- });
450
- });
424
+ );
451
425
 
452
- for (const codec of codecs) {
453
- if (codec.sdpFmtpLine === '') {
426
+ for (const codec of ourSupportedCodecs) {
427
+ if (codec?.sdpFmtpLine === undefined || codec.sdpFmtpLine === '') {
454
428
  // We can't dynamically add members to the codec, so instead remove the field if it's empty
455
429
  delete codec.sdpFmtpLine;
456
430
  }
457
431
  }
458
432
 
459
- transceiver.setCodecPreferences(codecs);
433
+ transceiver.setCodecPreferences(ourSupportedCodecs);
460
434
  }
461
435
  }
462
436
  }
463
437
 
464
438
  let hasAudioReceiver = false;
465
439
  for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
466
- if (transceiver && transceiver.receiver && transceiver.receiver.track && transceiver.receiver.track.kind === 'audio') {
440
+ if (
441
+ transceiver &&
442
+ transceiver.receiver &&
443
+ transceiver.receiver.track &&
444
+ transceiver.receiver.track.kind === 'audio'
445
+ ) {
467
446
  hasAudioReceiver = true;
468
447
  break;
469
448
  }
@@ -471,62 +450,101 @@ export class PeerConnectionController {
471
450
 
472
451
  // Setup a transceiver for sending mic audio to UE and receiving audio from UE
473
452
  if (!useMic) {
474
- if(!hasAudioReceiver) {
453
+ if (!hasAudioReceiver) {
475
454
  this.peerConnection?.addTransceiver('audio', {
476
455
  direction: 'recvonly'
477
456
  });
478
457
  }
479
458
  } else {
480
- // set the audio options based on mic usage
481
- const audioOptions = {
482
- autoGainControl: false,
483
- channelCount: 1,
484
- echoCancellation: false,
485
- latency: 0,
486
- noiseSuppression: false,
487
- sampleRate: 48000,
488
- sampleSize: 16,
489
- volume: 1.0
490
- }
459
+ await this.setupAudioSender(hasAudioReceiver);
460
+ }
461
+ }
491
462
 
492
- // set the media send options
493
- const mediaSendOptions: MediaStreamConstraints = {
494
- video: false,
495
- audio: audioOptions
496
- };
463
+ async setupVideoSender(hasVideoReceiver: boolean) {
464
+ // set the media send options
465
+ const mediaSendOptions: MediaStreamConstraints = {
466
+ video: true
467
+ };
497
468
 
498
- // Note using mic on android chrome requires SSL or chrome://flags/ "unsafely-treat-insecure-origin-as-secure"
499
- const stream = await navigator.mediaDevices.getUserMedia(
500
- mediaSendOptions
501
- );
502
- if (stream) {
503
- if (hasAudioReceiver) {
504
- for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
505
- if (RTCUtils.canTransceiverReceiveAudio(transceiver)) {
506
- for (const track of stream.getTracks()) {
507
- if (track.kind && track.kind == 'audio') {
508
- transceiver.sender.replaceTrack(track);
509
- transceiver.direction = 'sendrecv';
510
- }
469
+ // Note using webcam on android chrome requires SSL or chrome://flags/ "unsafely-treat-insecure-origin-as-secure"
470
+ const stream = await navigator.mediaDevices.getUserMedia(mediaSendOptions);
471
+
472
+ if (stream) {
473
+ if (hasVideoReceiver) {
474
+ for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
475
+ if (RTCUtils.canTransceiverReceiveVideo(transceiver)) {
476
+ for (const track of stream.getTracks()) {
477
+ if (track.kind && track.kind == 'video') {
478
+ transceiver.sender.replaceTrack(track);
479
+ transceiver.direction = 'sendrecv';
511
480
  }
512
481
  }
513
482
  }
514
- } else {
515
- for (const track of stream.getTracks()) {
516
- if (track.kind && track.kind == 'audio') {
517
- this.peerConnection?.addTransceiver(track, {
518
- direction: 'sendrecv'
519
- });
483
+ }
484
+ } else {
485
+ for (const track of stream.getTracks()) {
486
+ if (track.kind && track.kind == 'video') {
487
+ this.peerConnection?.addTransceiver(track, {
488
+ direction: 'sendrecv'
489
+ });
490
+ }
491
+ }
492
+ }
493
+ } else {
494
+ if (!hasVideoReceiver) {
495
+ this.peerConnection?.addTransceiver('video', { direction: 'recvonly' });
496
+ }
497
+ }
498
+ }
499
+
500
+ async setupAudioSender(hasAudioReceiver: boolean) {
501
+ // set the audio options based on mic usage
502
+ const audioOptions = {
503
+ autoGainControl: false,
504
+ channelCount: 1,
505
+ echoCancellation: false,
506
+ latency: 0,
507
+ noiseSuppression: false,
508
+ sampleRate: 48000,
509
+ sampleSize: 16,
510
+ volume: 1.0
511
+ };
512
+
513
+ // set the media send options
514
+ const mediaSendOptions: MediaStreamConstraints = {
515
+ video: false,
516
+ audio: audioOptions
517
+ };
518
+
519
+ // Note using mic on android chrome requires SSL or chrome://flags/ "unsafely-treat-insecure-origin-as-secure"
520
+ const stream = await navigator.mediaDevices.getUserMedia(mediaSendOptions);
521
+ if (stream) {
522
+ if (hasAudioReceiver) {
523
+ for (const transceiver of this.peerConnection?.getTransceivers() ?? []) {
524
+ if (RTCUtils.canTransceiverReceiveAudio(transceiver)) {
525
+ for (const track of stream.getTracks()) {
526
+ if (track.kind && track.kind == 'audio') {
527
+ transceiver.sender.replaceTrack(track);
528
+ transceiver.direction = 'sendrecv';
529
+ }
520
530
  }
521
531
  }
522
532
  }
523
533
  } else {
524
- if(!hasAudioReceiver) {
525
- this.peerConnection?.addTransceiver('audio', {
526
- direction: 'recvonly'
527
- });
534
+ for (const track of stream.getTracks()) {
535
+ if (track.kind && track.kind == 'audio') {
536
+ this.peerConnection?.addTransceiver(track, {
537
+ direction: 'sendrecv'
538
+ });
539
+ }
528
540
  }
529
541
  }
542
+ } else {
543
+ if (!hasAudioReceiver) {
544
+ this.peerConnection?.addTransceiver('audio', {
545
+ direction: 'recvonly'
546
+ });
547
+ }
530
548
  }
531
549
  }
532
550
 
@@ -571,12 +589,9 @@ export class PeerConnectionController {
571
589
  // Default Functionality: Do Nothing
572
590
  }
573
591
 
574
- parseAvailableCodecs(
575
- rtcSessionDescription: RTCSessionDescriptionInit
576
- ): Array<string> {
592
+ parseAvailableCodecs(rtcSessionDescription: RTCSessionDescriptionInit): Array<string> {
577
593
  // No point in updating the available codecs if on FF
578
- if (!RTCRtpReceiver.getCapabilities)
579
- return ['Only available on Chrome'];
594
+ if (!RTCRtpReceiver.getCapabilities) return ['Only available on Chrome'];
580
595
 
581
596
  const ueSupportedCodecs: Array<string> = [];
582
597
  const sections = splitSections(rtcSessionDescription.sdp);