@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.1.3 → 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 +217 -164
- 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 +10 -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
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
export class SettingBase {
|
|
7
7
|
id: string;
|
|
8
8
|
description: string;
|
|
9
|
+
useUrlParams: boolean;
|
|
10
|
+
_urlParams: Record<string, string>;
|
|
9
11
|
_label: string;
|
|
10
12
|
_value: unknown;
|
|
11
13
|
onChange: (changedValue: unknown, setting: SettingBase) => void;
|
|
@@ -16,9 +18,13 @@ export class SettingBase {
|
|
|
16
18
|
label: string,
|
|
17
19
|
description: string,
|
|
18
20
|
defaultSettingValue: unknown,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
22
|
+
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
|
|
23
|
+
/* Do nothing, to be overridden. */
|
|
24
|
+
}
|
|
21
25
|
) {
|
|
26
|
+
this.parseURLParams();
|
|
27
|
+
|
|
22
28
|
this.onChange = defaultOnChangeListener;
|
|
23
29
|
|
|
24
30
|
this.onChangeEmit = () => {
|
|
@@ -62,4 +68,57 @@ export class SettingBase {
|
|
|
62
68
|
this.onChange(this._value, this);
|
|
63
69
|
this.onChangeEmit(this._value);
|
|
64
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Persist the setting value in URL.
|
|
74
|
+
*/
|
|
75
|
+
public updateURLParams() {
|
|
76
|
+
if (this.useUrlParams) {
|
|
77
|
+
// set url params
|
|
78
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
79
|
+
const valueString = this.getValueAsString();
|
|
80
|
+
let set = false;
|
|
81
|
+
for (const [name, _value] of urlParams) {
|
|
82
|
+
if (name.toLowerCase() == this.id.toLowerCase()) {
|
|
83
|
+
urlParams.set(name, valueString);
|
|
84
|
+
set = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!set) {
|
|
89
|
+
urlParams.set(this.id, valueString);
|
|
90
|
+
}
|
|
91
|
+
window.history.replaceState(
|
|
92
|
+
{},
|
|
93
|
+
'',
|
|
94
|
+
urlParams.toString() !== '' ? `${location.pathname}?${urlParams}` : `${location.pathname}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Allows sub types to provide their value for the url search params.
|
|
101
|
+
*/
|
|
102
|
+
protected getValueAsString(): string {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private parseURLParams(): void {
|
|
107
|
+
this._urlParams = {};
|
|
108
|
+
const params = new URLSearchParams(window.location.search);
|
|
109
|
+
for (const [name, value] of params) {
|
|
110
|
+
this._urlParams[name.toLowerCase()] = value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected hasURLParam(name: string): boolean {
|
|
115
|
+
return name.toLowerCase() in this._urlParams;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected getURLParam(name: string): string {
|
|
119
|
+
if (this.hasURLParam(name)) {
|
|
120
|
+
return this._urlParams[name.toLowerCase()];
|
|
121
|
+
}
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
65
124
|
}
|
|
@@ -6,12 +6,9 @@ import { SettingBase } from './SettingBase';
|
|
|
6
6
|
/**
|
|
7
7
|
* A boolean flag setting object with a text label.
|
|
8
8
|
*/
|
|
9
|
-
export class SettingFlag<
|
|
10
|
-
CustomIds extends string = FlagsIds
|
|
11
|
-
> extends SettingBase {
|
|
9
|
+
export class SettingFlag<CustomIds extends string = FlagsIds> extends SettingBase {
|
|
12
10
|
id: FlagsIds | CustomIds;
|
|
13
11
|
onChangeEmit: (changedValue: boolean) => void;
|
|
14
|
-
useUrlParams: boolean;
|
|
15
12
|
|
|
16
13
|
constructor(
|
|
17
14
|
id: FlagsIds | CustomIds,
|
|
@@ -19,60 +16,25 @@ export class SettingFlag<
|
|
|
19
16
|
description: string,
|
|
20
17
|
defaultFlagValue: boolean,
|
|
21
18
|
useUrlParams: boolean,
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
20
|
+
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
|
|
21
|
+
/* Do nothing, to be overridden. */
|
|
22
|
+
}
|
|
24
23
|
) {
|
|
25
24
|
super(id, label, description, defaultFlagValue, defaultOnChangeListener);
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
if (!useUrlParams || !urlParams.has(this.id)) {
|
|
26
|
+
if (!useUrlParams || !this.hasURLParam(this.id)) {
|
|
29
27
|
this.flag = defaultFlagValue;
|
|
30
28
|
} else {
|
|
31
29
|
// parse flag from url parameters
|
|
32
|
-
const urlParamFlag = this.
|
|
33
|
-
this.flag = urlParamFlag;
|
|
30
|
+
const urlParamFlag = this.getURLParam(this.id);
|
|
31
|
+
this.flag = urlParamFlag.toLowerCase() != 'false';
|
|
34
32
|
}
|
|
35
33
|
this.useUrlParams = useUrlParams;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @returns True if the url parameters contains /?id, but False if /?id=false
|
|
41
|
-
*/
|
|
42
|
-
getUrlParamFlag(): boolean {
|
|
43
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
44
|
-
if (urlParams.has(this.id)) {
|
|
45
|
-
if (
|
|
46
|
-
urlParams.get(this.id) === 'false' ||
|
|
47
|
-
urlParams.get(this.id) === 'False'
|
|
48
|
-
) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Persist the setting value in URL.
|
|
58
|
-
*/
|
|
59
|
-
public updateURLParams() {
|
|
60
|
-
if (this.useUrlParams) {
|
|
61
|
-
// set url params
|
|
62
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
63
|
-
if (this.flag === true) {
|
|
64
|
-
urlParams.set(this.id, 'true');
|
|
65
|
-
} else {
|
|
66
|
-
urlParams.set(this.id, 'false');
|
|
67
|
-
}
|
|
68
|
-
window.history.replaceState(
|
|
69
|
-
{},
|
|
70
|
-
'',
|
|
71
|
-
urlParams.toString() !== ''
|
|
72
|
-
? `${location.pathname}?${urlParams}`
|
|
73
|
-
: `${location.pathname}`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
36
|
+
protected getValueAsString(): string {
|
|
37
|
+
return this.flag ? 'true' : 'false';
|
|
76
38
|
}
|
|
77
39
|
|
|
78
40
|
/**
|
|
@@ -6,15 +6,12 @@ import { SettingBase } from './SettingBase';
|
|
|
6
6
|
/**
|
|
7
7
|
* A number setting object with a text label. Min and max limit the range of allowed values.
|
|
8
8
|
*/
|
|
9
|
-
export class SettingNumber<
|
|
10
|
-
CustomIds extends string = NumericParametersIds
|
|
11
|
-
> extends SettingBase {
|
|
9
|
+
export class SettingNumber<CustomIds extends string = NumericParametersIds> extends SettingBase {
|
|
12
10
|
_min: number;
|
|
13
11
|
_max: number;
|
|
14
12
|
|
|
15
13
|
id: NumericParametersIds | CustomIds;
|
|
16
14
|
onChangeEmit: (changedValue: number) => void;
|
|
17
|
-
useUrlParams: boolean;
|
|
18
15
|
|
|
19
16
|
constructor(
|
|
20
17
|
id: NumericParametersIds | CustomIds,
|
|
@@ -24,8 +21,10 @@ export class SettingNumber<
|
|
|
24
21
|
max: number,
|
|
25
22
|
defaultNumber: number,
|
|
26
23
|
useUrlParams: boolean,
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
25
|
+
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
|
|
26
|
+
/* Do nothing, to be overridden. */
|
|
27
|
+
}
|
|
29
28
|
) {
|
|
30
29
|
super(id, label, description, defaultNumber, defaultOnChangeListener);
|
|
31
30
|
|
|
@@ -33,34 +32,17 @@ export class SettingNumber<
|
|
|
33
32
|
this._max = max;
|
|
34
33
|
|
|
35
34
|
// attempt to read the number from the url params
|
|
36
|
-
|
|
37
|
-
if (!useUrlParams || !urlParams.has(this.id)) {
|
|
35
|
+
if (!useUrlParams || !this.hasURLParam(this.id)) {
|
|
38
36
|
this.number = defaultNumber;
|
|
39
37
|
} else {
|
|
40
|
-
const parsedValue = Number.parseFloat(
|
|
41
|
-
this.number = Number.isNaN(parsedValue)
|
|
42
|
-
? defaultNumber
|
|
43
|
-
: parsedValue;
|
|
38
|
+
const parsedValue = Number.parseFloat(this.getURLParam(this.id));
|
|
39
|
+
this.number = Number.isNaN(parsedValue) ? defaultNumber : parsedValue;
|
|
44
40
|
}
|
|
45
41
|
this.useUrlParams = useUrlParams;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
public updateURLParams(): void {
|
|
52
|
-
if (this.useUrlParams) {
|
|
53
|
-
// set url params like ?id=number
|
|
54
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
55
|
-
urlParams.set(this.id, this.number.toString());
|
|
56
|
-
window.history.replaceState(
|
|
57
|
-
{},
|
|
58
|
-
'',
|
|
59
|
-
urlParams.toString() !== ''
|
|
60
|
-
? `${location.pathname}?${urlParams}`
|
|
61
|
-
: `${location.pathname}`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
44
|
+
protected getValueAsString(): string {
|
|
45
|
+
return this.number.toString();
|
|
64
46
|
}
|
|
65
47
|
|
|
66
48
|
/**
|
|
@@ -6,13 +6,10 @@ import { SettingBase } from './SettingBase';
|
|
|
6
6
|
/**
|
|
7
7
|
* An Option setting object with a text label. Allows you to specify an array of options and select one of them.
|
|
8
8
|
*/
|
|
9
|
-
export class SettingOption<
|
|
10
|
-
CustomIds extends string = OptionParametersIds
|
|
11
|
-
> extends SettingBase {
|
|
9
|
+
export class SettingOption<CustomIds extends string = OptionParametersIds> extends SettingBase {
|
|
12
10
|
id: OptionParametersIds | CustomIds;
|
|
13
11
|
onChangeEmit: (changedValue: string) => void;
|
|
14
12
|
_options: Array<string>;
|
|
15
|
-
useUrlParams: boolean;
|
|
16
13
|
|
|
17
14
|
constructor(
|
|
18
15
|
id: OptionParametersIds | CustomIds,
|
|
@@ -21,49 +18,23 @@ export class SettingOption<
|
|
|
21
18
|
defaultTextValue: string,
|
|
22
19
|
options: Array<string>,
|
|
23
20
|
useUrlParams: boolean,
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
22
|
+
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
|
|
23
|
+
/* Do nothing, to be overridden. */
|
|
24
|
+
}
|
|
26
25
|
) {
|
|
27
26
|
super(id, label, description, defaultTextValue, defaultOnChangeListener);
|
|
28
27
|
|
|
29
28
|
this.options = options;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
? this.getUrlParamText()
|
|
34
|
-
: defaultTextValue;
|
|
29
|
+
const stringToMatch: string = this.hasURLParam(this.id)
|
|
30
|
+
? this.getURLParam(this.id)
|
|
31
|
+
: defaultTextValue;
|
|
35
32
|
this.selected = stringToMatch;
|
|
36
33
|
this.useUrlParams = useUrlParams;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* @returns The text value parsed from the url if the url parameters contains /?id=value, but empty string if just /?id or no url param found.
|
|
42
|
-
*/
|
|
43
|
-
getUrlParamText(): string {
|
|
44
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
45
|
-
if (urlParams.has(this.id)) {
|
|
46
|
-
return urlParams.get(this.id) ?? '';
|
|
47
|
-
}
|
|
48
|
-
return '';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Persist the setting value in URL.
|
|
53
|
-
*/
|
|
54
|
-
public updateURLParams() {
|
|
55
|
-
if (this.useUrlParams) {
|
|
56
|
-
// set url params
|
|
57
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
58
|
-
urlParams.set(this.id, this.selected);
|
|
59
|
-
window.history.replaceState(
|
|
60
|
-
{},
|
|
61
|
-
'',
|
|
62
|
-
urlParams.toString() !== ''
|
|
63
|
-
? `${location.pathname}?${urlParams}`
|
|
64
|
-
: `${location.pathname}`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
36
|
+
protected getValueAsString(): string {
|
|
37
|
+
return this.selected;
|
|
67
38
|
}
|
|
68
39
|
|
|
69
40
|
/**
|
|
@@ -103,19 +74,15 @@ export class SettingOption<
|
|
|
103
74
|
public set selected(value: string) {
|
|
104
75
|
// A user may not specify the full possible value so we instead use the closest match.
|
|
105
76
|
// eg ?xxx=H264 would select 'H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f'
|
|
106
|
-
let filteredList = this.options.filter(
|
|
107
|
-
(option: string) => option.indexOf(value) !== -1
|
|
108
|
-
);
|
|
77
|
+
let filteredList = this.options.filter((option: string) => option.indexOf(value) !== -1);
|
|
109
78
|
if (filteredList.length) {
|
|
110
79
|
this.value = filteredList[0];
|
|
111
80
|
return;
|
|
112
|
-
}
|
|
81
|
+
}
|
|
113
82
|
|
|
114
83
|
// A user has specified a codec with a fmtp string but this codec + fmtp line isn't available.
|
|
115
84
|
// in that case, just use the codec
|
|
116
|
-
filteredList = this.options.filter(
|
|
117
|
-
(option: string) => option.indexOf(value.split(' ')[0]) !== -1
|
|
118
|
-
);
|
|
85
|
+
filteredList = this.options.filter((option: string) => option.indexOf(value.split(' ')[0]) !== -1);
|
|
119
86
|
if (filteredList.length) {
|
|
120
87
|
this.value = filteredList[0];
|
|
121
88
|
return;
|
|
@@ -6,9 +6,7 @@ import { SettingBase } from './SettingBase';
|
|
|
6
6
|
/**
|
|
7
7
|
* A text setting object with a text label.
|
|
8
8
|
*/
|
|
9
|
-
export class SettingText<
|
|
10
|
-
CustomIds extends string = TextParametersIds
|
|
11
|
-
> extends SettingBase {
|
|
9
|
+
export class SettingText<CustomIds extends string = TextParametersIds> extends SettingBase {
|
|
12
10
|
id: TextParametersIds | CustomIds;
|
|
13
11
|
onChangeEmit: (changedValue: string) => void;
|
|
14
12
|
useUrlParams: boolean;
|
|
@@ -19,50 +17,24 @@ export class SettingText<
|
|
|
19
17
|
description: string,
|
|
20
18
|
defaultTextValue: string,
|
|
21
19
|
useUrlParams: boolean,
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
21
|
+
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
|
|
22
|
+
/* Do nothing, to be overridden. */
|
|
23
|
+
}
|
|
24
24
|
) {
|
|
25
25
|
super(id, label, description, defaultTextValue, defaultOnChangeListener);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
if (!useUrlParams || !urlParams.has(this.id)) {
|
|
27
|
+
if (!useUrlParams || !this.hasURLParam(this.id)) {
|
|
29
28
|
this.text = defaultTextValue;
|
|
30
29
|
} else {
|
|
31
30
|
// parse flag from url parameters
|
|
32
|
-
|
|
33
|
-
this.text = urlParamFlag;
|
|
31
|
+
this.text = this.getURLParam(this.id);
|
|
34
32
|
}
|
|
35
33
|
this.useUrlParams = useUrlParams;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @returns The text value parsed from the url if the url parameters contains /?id=value, but empty string if just /?id or no url param found.
|
|
41
|
-
*/
|
|
42
|
-
getUrlParamText(): string {
|
|
43
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
44
|
-
if (urlParams.has(this.id)) {
|
|
45
|
-
return urlParams.get(this.id) ?? '';
|
|
46
|
-
}
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Persist the setting value in URL.
|
|
52
|
-
*/
|
|
53
|
-
public updateURLParams() {
|
|
54
|
-
if (this.useUrlParams) {
|
|
55
|
-
// set url params
|
|
56
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
57
|
-
urlParams.set(this.id, this.text);
|
|
58
|
-
window.history.replaceState(
|
|
59
|
-
{},
|
|
60
|
-
'',
|
|
61
|
-
urlParams.toString() !== ''
|
|
62
|
-
? `${location.pathname}?${urlParams}`
|
|
63
|
-
: `${location.pathname}`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
36
|
+
protected getValueAsString(): string {
|
|
37
|
+
return this.text;
|
|
66
38
|
}
|
|
67
39
|
|
|
68
40
|
/**
|
|
@@ -39,10 +39,7 @@ export class DataChannelController {
|
|
|
39
39
|
this.datachannelOptions.ordered = true;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
this.dataChannel = this.peerConnection.createDataChannel(
|
|
43
|
-
this.label,
|
|
44
|
-
this.datachannelOptions
|
|
45
|
-
);
|
|
42
|
+
this.dataChannel = this.peerConnection.createDataChannel(this.label, this.datachannelOptions);
|
|
46
43
|
this.setupDataChannel();
|
|
47
44
|
}
|
|
48
45
|
|
|
@@ -51,8 +48,7 @@ export class DataChannelController {
|
|
|
51
48
|
this.dataChannel.binaryType = 'arraybuffer';
|
|
52
49
|
this.dataChannel.onopen = (ev: Event) => this.handleOnOpen(ev);
|
|
53
50
|
this.dataChannel.onclose = (ev: Event) => this.handleOnClose(ev);
|
|
54
|
-
this.dataChannel.onmessage = (ev: MessageEvent) =>
|
|
55
|
-
this.handleOnMessage(ev);
|
|
51
|
+
this.dataChannel.onmessage = (ev: MessageEvent) => this.handleOnMessage(ev);
|
|
56
52
|
this.dataChannel.onerror = (ev: MessageEvent) => this.handleOnError(ev);
|
|
57
53
|
}
|
|
58
54
|
|
|
@@ -60,11 +56,7 @@ export class DataChannelController {
|
|
|
60
56
|
* Handles when the Data Channel is opened
|
|
61
57
|
*/
|
|
62
58
|
handleOnOpen(ev: Event) {
|
|
63
|
-
Logger.
|
|
64
|
-
Logger.GetStackTrace(),
|
|
65
|
-
`Data Channel (${this.label}) opened.`,
|
|
66
|
-
7
|
|
67
|
-
);
|
|
59
|
+
Logger.Info(`Data Channel (${this.label}) opened.`);
|
|
68
60
|
this.onOpen(this.dataChannel?.label, ev);
|
|
69
61
|
}
|
|
70
62
|
|
|
@@ -72,11 +64,7 @@ export class DataChannelController {
|
|
|
72
64
|
* Handles when the Data Channel is closed
|
|
73
65
|
*/
|
|
74
66
|
handleOnClose(ev: Event) {
|
|
75
|
-
Logger.
|
|
76
|
-
Logger.GetStackTrace(),
|
|
77
|
-
`Data Channel (${this.label}) closed.`,
|
|
78
|
-
7
|
|
79
|
-
);
|
|
67
|
+
Logger.Info(`Data Channel (${this.label}) closed.`);
|
|
80
68
|
this.onClose(this.dataChannel?.label, ev);
|
|
81
69
|
}
|
|
82
70
|
|
|
@@ -86,11 +74,7 @@ export class DataChannelController {
|
|
|
86
74
|
*/
|
|
87
75
|
handleOnMessage(event: MessageEvent) {
|
|
88
76
|
// Higher log level to prevent log spam with messages received
|
|
89
|
-
Logger.
|
|
90
|
-
Logger.GetStackTrace(),
|
|
91
|
-
`Data Channel (${this.label}) message: ${event}`,
|
|
92
|
-
8
|
|
93
|
-
);
|
|
77
|
+
Logger.Info(`Data Channel (${this.label}) message: ${event}`);
|
|
94
78
|
}
|
|
95
79
|
|
|
96
80
|
/**
|
|
@@ -98,11 +82,7 @@ export class DataChannelController {
|
|
|
98
82
|
* @param event - Error Event
|
|
99
83
|
*/
|
|
100
84
|
handleOnError(event: MessageEvent) {
|
|
101
|
-
Logger.
|
|
102
|
-
Logger.GetStackTrace(),
|
|
103
|
-
`Data Channel (${this.label}) error: ${event}`,
|
|
104
|
-
7
|
|
105
|
-
);
|
|
85
|
+
Logger.Info(`Data Channel (${this.label}) error: ${event}`);
|
|
106
86
|
this.onError(this.dataChannel?.label, event);
|
|
107
87
|
}
|
|
108
88
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
DataChannelLatencyTestResult,
|
|
9
9
|
DataChannelLatencyTestSeq,
|
|
10
10
|
DataChannelLatencyTestTimestamp
|
|
11
|
-
} from
|
|
11
|
+
} from './DataChannelLatencyTestResults';
|
|
12
12
|
|
|
13
13
|
export type DataChannelLatencyTestConfig = {
|
|
14
14
|
// test duration in milliseconds
|
|
@@ -19,7 +19,7 @@ export type DataChannelLatencyTestConfig = {
|
|
|
19
19
|
requestSize: number;
|
|
20
20
|
//response filler size
|
|
21
21
|
responseSize: number;
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
|
|
24
24
|
export type DataChannelLatencyTestSink = (request: DataChannelLatencyTestRequest) => void;
|
|
25
25
|
export type DataChannelLatencyTestResultCallback = (result: DataChannelLatencyTestResult) => void;
|
|
@@ -45,13 +45,16 @@ export class DataChannelLatencyTestController {
|
|
|
45
45
|
}
|
|
46
46
|
this.startTime = Date.now();
|
|
47
47
|
this.records.clear();
|
|
48
|
-
this.interval = setInterval(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
this.interval = setInterval(
|
|
49
|
+
(() => {
|
|
50
|
+
if (Date.now() - this.startTime >= config.duration) {
|
|
51
|
+
this.stop();
|
|
52
|
+
} else {
|
|
53
|
+
this.sendRequest(config.requestSize, config.responseSize);
|
|
54
|
+
}
|
|
55
|
+
}).bind(this),
|
|
56
|
+
Math.floor(1000 / config.rps)
|
|
57
|
+
);
|
|
55
58
|
return true;
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -67,27 +70,33 @@ export class DataChannelLatencyTestController {
|
|
|
67
70
|
const resultRecords = new Map(this.records);
|
|
68
71
|
return {
|
|
69
72
|
records: resultRecords,
|
|
70
|
-
dataChannelRtt: Math.ceil(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
dataChannelRtt: Math.ceil(
|
|
74
|
+
Array.from(this.records.values()).reduce((acc, next) => {
|
|
75
|
+
return acc + (next.playerReceivedTimestamp - next.playerSentTimestamp);
|
|
76
|
+
}, 0) / this.records.size
|
|
77
|
+
),
|
|
78
|
+
playerToStreamerTime: Math.ceil(
|
|
79
|
+
Array.from(this.records.values()).reduce((acc, next) => {
|
|
80
|
+
return acc + (next.streamerReceivedTimestamp - next.playerSentTimestamp);
|
|
81
|
+
}, 0) / this.records.size
|
|
82
|
+
),
|
|
83
|
+
streamerToPlayerTime: Math.ceil(
|
|
84
|
+
Array.from(this.records.values()).reduce((acc, next) => {
|
|
85
|
+
return acc + (next.playerReceivedTimestamp - next.streamerSentTimestamp);
|
|
86
|
+
}, 0) / this.records.size
|
|
87
|
+
),
|
|
79
88
|
exportLatencyAsCSV: () => {
|
|
80
|
-
let csv =
|
|
89
|
+
let csv = 'Timestamp;RTT;PlayerToStreamer;StreamerToPlayer;\n';
|
|
81
90
|
resultRecords.forEach((record) => {
|
|
82
|
-
csv += record.playerSentTimestamp +
|
|
83
|
-
csv +=
|
|
84
|
-
csv +=
|
|
85
|
-
csv +=
|
|
86
|
-
csv +=
|
|
87
|
-
})
|
|
91
|
+
csv += record.playerSentTimestamp + ';';
|
|
92
|
+
csv += record.playerReceivedTimestamp - record.playerSentTimestamp + ';';
|
|
93
|
+
csv += record.streamerReceivedTimestamp - record.playerSentTimestamp + ';';
|
|
94
|
+
csv += record.playerReceivedTimestamp - record.streamerSentTimestamp + ';';
|
|
95
|
+
csv += '\n';
|
|
96
|
+
});
|
|
88
97
|
return csv;
|
|
89
98
|
}
|
|
90
|
-
}
|
|
99
|
+
};
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
isRunning() {
|
|
@@ -99,10 +108,7 @@ export class DataChannelLatencyTestController {
|
|
|
99
108
|
return;
|
|
100
109
|
}
|
|
101
110
|
if (!response) {
|
|
102
|
-
Logger.Error(
|
|
103
|
-
Logger.GetStackTrace(),
|
|
104
|
-
"Undefined response from server"
|
|
105
|
-
);
|
|
111
|
+
Logger.Error('Undefined response from server');
|
|
106
112
|
return;
|
|
107
113
|
}
|
|
108
114
|
const record = this.records.get(response.Seq);
|
|
@@ -122,8 +128,7 @@ export class DataChannelLatencyTestController {
|
|
|
122
128
|
return {
|
|
123
129
|
Seq: this.seq++,
|
|
124
130
|
FillResponseSize: responseSize,
|
|
125
|
-
Filler: requestSize ?
|
|
126
|
-
}
|
|
131
|
+
Filler: requestSize ? 'A'.repeat(requestSize) : ''
|
|
132
|
+
};
|
|
127
133
|
}
|
|
128
|
-
|
|
129
134
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Data Channel Latency Test types
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* Unix epoch
|
|
10
9
|
*/
|
|
@@ -22,7 +21,7 @@ export type DataChannelLatencyTestRequest = {
|
|
|
22
21
|
Seq: DataChannelLatencyTestSeq;
|
|
23
22
|
FillResponseSize: number;
|
|
24
23
|
Filler: string;
|
|
25
|
-
}
|
|
24
|
+
};
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
27
|
* Response from the Streamer
|
|
@@ -32,15 +31,15 @@ export type DataChannelLatencyTestResponse = {
|
|
|
32
31
|
Filler: string;
|
|
33
32
|
ReceivedTimestamp: DataChannelLatencyTestTimestamp;
|
|
34
33
|
SentTimestamp: DataChannelLatencyTestTimestamp;
|
|
35
|
-
}
|
|
34
|
+
};
|
|
36
35
|
|
|
37
36
|
export type DataChannelLatencyTestResult = {
|
|
38
|
-
records: Map<DataChannelLatencyTestSeq, DataChannelLatencyTestRecord
|
|
39
|
-
dataChannelRtt: number
|
|
40
|
-
playerToStreamerTime: number
|
|
41
|
-
streamerToPlayerTime: number
|
|
42
|
-
exportLatencyAsCSV: () => string
|
|
43
|
-
}
|
|
37
|
+
records: Map<DataChannelLatencyTestSeq, DataChannelLatencyTestRecord>;
|
|
38
|
+
dataChannelRtt: number;
|
|
39
|
+
playerToStreamerTime: number;
|
|
40
|
+
streamerToPlayerTime: number;
|
|
41
|
+
exportLatencyAsCSV: () => string;
|
|
42
|
+
};
|
|
44
43
|
|
|
45
44
|
export class DataChannelLatencyTestRecord {
|
|
46
45
|
seq: DataChannelLatencyTestSeq;
|
|
@@ -63,5 +62,4 @@ export class DataChannelLatencyTestRecord {
|
|
|
63
62
|
this.streamerSentTimestamp = response.SentTimestamp;
|
|
64
63
|
this.responseFillerSize = response.Filler ? response.Filler.length : 0;
|
|
65
64
|
}
|
|
66
|
-
|
|
67
65
|
}
|
|
@@ -18,10 +18,8 @@ export class DataChannelSender {
|
|
|
18
18
|
|
|
19
19
|
canSend(): boolean {
|
|
20
20
|
return (
|
|
21
|
-
this.dataChannelProvider.getDataChannelInstance().dataChannel !==
|
|
22
|
-
|
|
23
|
-
this.dataChannelProvider.getDataChannelInstance().dataChannel
|
|
24
|
-
.readyState == 'open'
|
|
21
|
+
this.dataChannelProvider.getDataChannelInstance().dataChannel !== undefined &&
|
|
22
|
+
this.dataChannelProvider.getDataChannelInstance().dataChannel.readyState == 'open'
|
|
25
23
|
);
|
|
26
24
|
}
|
|
27
25
|
|
|
@@ -31,22 +29,14 @@ export class DataChannelSender {
|
|
|
31
29
|
*/
|
|
32
30
|
sendData(data: ArrayBuffer) {
|
|
33
31
|
// reset the afk inactivity
|
|
34
|
-
const dataChannelInstance =
|
|
35
|
-
this.dataChannelProvider.getDataChannelInstance();
|
|
32
|
+
const dataChannelInstance = this.dataChannelProvider.getDataChannelInstance();
|
|
36
33
|
|
|
37
34
|
if (dataChannelInstance.dataChannel.readyState == 'open') {
|
|
38
35
|
dataChannelInstance.dataChannel.send(data);
|
|
39
|
-
Logger.
|
|
40
|
-
Logger.GetStackTrace(),
|
|
41
|
-
`Message Sent: ${new Uint8Array(data)}`,
|
|
42
|
-
6
|
|
43
|
-
);
|
|
36
|
+
Logger.Info(`Message Sent: ${new Uint8Array(data)}`);
|
|
44
37
|
this.resetAfkWarningTimerOnDataSend();
|
|
45
38
|
} else {
|
|
46
|
-
Logger.Error(
|
|
47
|
-
Logger.GetStackTrace(),
|
|
48
|
-
`Message Failed: ${new Uint8Array(data)}`
|
|
49
|
-
);
|
|
39
|
+
Logger.Error(`Message Failed: ${new Uint8Array(data)}`);
|
|
50
40
|
}
|
|
51
41
|
}
|
|
52
42
|
|