@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.
- package/.eslintrc.js +1 -1
- package/.prettierrc.json +1 -0
- package/dist/lib-pixelstreamingfrontend.esm.js +1 -1
- package/dist/lib-pixelstreamingfrontend.js +1 -1
- package/package.json +6 -5
- package/src/AFK/AFKController.ts +10 -32
- package/src/Config/Config.ts +179 -201
- package/src/Config/SettingBase.ts +61 -2
- package/src/Config/SettingFlag.ts +10 -48
- package/src/Config/SettingNumber.ts +10 -28
- package/src/Config/SettingOption.ts +13 -46
- package/src/Config/SettingText.ts +9 -37
- package/src/DataChannel/DataChannelController.ts +6 -26
- package/src/DataChannel/DataChannelLatencyTestController.ts +38 -33
- package/src/DataChannel/DataChannelLatencyTestResults.ts +8 -10
- package/src/DataChannel/DataChannelSender.ts +5 -15
- package/src/DataChannel/LatencyTestResults.ts +5 -15
- package/src/FreezeFrame/FreezeFrame.ts +7 -19
- package/src/FreezeFrame/FreezeFrameController.ts +3 -14
- package/src/Inputs/GamepadController.ts +123 -221
- package/src/Inputs/GamepadTypes.ts +23 -0
- package/src/Inputs/IInputController.ts +17 -0
- package/src/Inputs/InputClassesFactory.ts +38 -45
- package/src/Inputs/KeyCodes.ts +114 -0
- package/src/Inputs/KeyboardController.ts +49 -232
- package/src/Inputs/MouseController.ts +71 -297
- package/src/Inputs/MouseControllerHovering.ts +118 -0
- package/src/Inputs/MouseControllerLocked.ts +194 -0
- package/src/Inputs/TouchController.ts +49 -105
- package/src/Inputs/TouchControllerFake.ts +132 -0
- package/src/Inputs/XRGamepadController.ts +35 -44
- package/src/PeerConnectionController/AggregatedStats.ts +26 -54
- package/src/PeerConnectionController/CandidatePairStats.ts +1 -1
- package/src/PeerConnectionController/CandidateStat.ts +1 -1
- package/src/PeerConnectionController/PeerConnectionController.ts +177 -162
- package/src/PixelStreaming/PixelStreaming.ts +174 -226
- package/src/UI/OnScreenKeyboard.ts +14 -9
- package/src/UeInstanceMessage/ResponseController.ts +6 -15
- package/src/UeInstanceMessage/SendMessageController.ts +16 -18
- package/src/UeInstanceMessage/StreamMessageController.ts +3 -12
- package/src/UeInstanceMessage/ToStreamerMessagesController.ts +3 -9
- package/src/Util/EventEmitter.ts +17 -22
- package/src/Util/FileUtil.ts +11 -34
- package/src/Util/IURLSearchParams.ts +25 -0
- package/src/Util/InputCoordTranslator.ts +73 -0
- package/src/Util/RTCUtils.ts +23 -15
- package/src/VideoPlayer/StreamController.ts +6 -23
- package/src/VideoPlayer/VideoPlayer.ts +9 -30
- package/src/WebRtcPlayer/WebRtcPlayerController.ts +328 -690
- package/src/WebXR/WebXRController.ts +82 -94
- package/src/pixelstreamingfrontend.ts +6 -10
- package/types/AFK/AFKController.d.ts +0 -1
- package/types/Config/Config.d.ts +6 -5
- package/types/Config/SettingBase.d.ts +13 -0
- package/types/Config/SettingFlag.d.ts +1 -10
- package/types/Config/SettingNumber.d.ts +1 -5
- package/types/Config/SettingOption.d.ts +1 -10
- package/types/Config/SettingText.d.ts +1 -9
- package/types/DataChannel/DataChannelLatencyTestController.d.ts +1 -1
- package/types/Inputs/GamepadController.d.ts +22 -46
- package/types/Inputs/GamepadTypes.d.ts +7 -0
- package/types/Inputs/IInputController.d.ts +16 -0
- package/types/Inputs/InputClassesFactory.d.ts +7 -8
- package/types/Inputs/KeyCodes.d.ts +5 -0
- package/types/Inputs/KeyboardController.d.ts +17 -45
- package/types/Inputs/MouseController.d.ts +33 -68
- package/types/Inputs/MouseControllerHovering.d.ts +26 -0
- package/types/Inputs/MouseControllerLocked.d.ts +31 -0
- package/types/Inputs/TouchController.d.ts +19 -44
- package/types/Inputs/TouchControllerFake.d.ts +29 -0
- package/types/Inputs/XRGamepadController.d.ts +0 -7
- package/types/PeerConnectionController/PeerConnectionController.d.ts +4 -1
- package/types/PixelStreaming/PixelStreaming.d.ts +14 -2
- package/types/UI/OnScreenKeyboard.d.ts +2 -2
- package/types/Util/EventEmitter.d.ts +1 -1
- package/types/Util/IURLSearchParams.d.ts +9 -0
- package/types/Util/InputCoordTranslator.d.ts +29 -0
- package/types/VideoPlayer/StreamController.d.ts +0 -2
- package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +19 -17
- package/types/pixelstreamingfrontend.d.ts +1 -1
- package/src/Inputs/FakeTouchController.ts +0 -199
- package/src/Inputs/HoveringMouseEvents.ts +0 -192
- package/src/Inputs/IMouseEvents.ts +0 -64
- package/src/Inputs/ITouchController.ts +0 -29
- package/src/Inputs/LockedMouseEvents.ts +0 -287
- package/src/Util/CoordinateConverter.ts +0 -290
- package/src/Util/EventListenerTracker.ts +0 -29
- package/types/Inputs/FakeTouchController.d.ts +0 -61
- package/types/Inputs/HoveringMouseEvents.d.ts +0 -56
- package/types/Inputs/IMouseEvents.d.ts +0 -53
- package/types/Inputs/ITouchController.d.ts +0 -24
- package/types/Inputs/LockedMouseEvents.d.ts +0 -80
- package/types/Util/CoordinateConverter.d.ts +0 -100
- 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.
|
|
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
|
-
|
|
53
|
-
this.peerConnection.
|
|
54
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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(
|
|
372
|
-
|
|
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 (
|
|
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
|
-
|
|
409
|
-
|
|
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
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
463
|
+
async setupVideoSender(hasVideoReceiver: boolean) {
|
|
464
|
+
// set the media send options
|
|
465
|
+
const mediaSendOptions: MediaStreamConstraints = {
|
|
466
|
+
video: true
|
|
467
|
+
};
|
|
497
468
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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);
|