@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.5
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/.cspell.json +48 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +8 -0
- package/.prettierignore +0 -0
- package/.prettierrc.json +6 -0
- package/dist/lib-pixelstreamingfrontend.esm.js +1 -0
- package/dist/lib-pixelstreamingfrontend.js +1 -0
- package/jest.config.js +18 -0
- package/package.json +48 -0
- package/readme.md +15 -0
- package/src/AFK/AFKController.test.ts +162 -0
- package/src/AFK/AFKController.ts +158 -0
- package/src/Config/Config.test.ts +222 -0
- package/src/Config/Config.ts +970 -0
- package/src/Config/SettingBase.ts +65 -0
- package/src/Config/SettingFlag.ts +99 -0
- package/src/Config/SettingNumber.ts +111 -0
- package/src/Config/SettingOption.ts +124 -0
- package/src/Config/SettingText.ts +82 -0
- package/src/DataChannel/DataChannelController.ts +138 -0
- package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
- package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
- package/src/DataChannel/DataChannelSender.ts +59 -0
- package/src/DataChannel/InitialSettings.ts +61 -0
- package/src/DataChannel/LatencyTestResults.ts +76 -0
- package/src/FreezeFrame/FreezeFrame.ts +114 -0
- package/src/FreezeFrame/FreezeFrameController.ts +114 -0
- package/src/Inputs/FakeTouchController.ts +199 -0
- package/src/Inputs/GamepadController.ts +314 -0
- package/src/Inputs/GamepadTypes.ts +10 -0
- package/src/Inputs/HoveringMouseEvents.ts +192 -0
- package/src/Inputs/IMouseEvents.ts +64 -0
- package/src/Inputs/ITouchController.ts +29 -0
- package/src/Inputs/InputClassesFactory.ts +140 -0
- package/src/Inputs/KeyboardController.ts +354 -0
- package/src/Inputs/LockedMouseEvents.ts +287 -0
- package/src/Inputs/MouseButtons.ts +25 -0
- package/src/Inputs/MouseController.ts +362 -0
- package/src/Inputs/SpecialKeyCodes.ts +16 -0
- package/src/Inputs/TouchController.ts +208 -0
- package/src/Inputs/XRGamepadController.ts +126 -0
- package/src/PeerConnectionController/AggregatedStats.ts +311 -0
- package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
- package/src/PeerConnectionController/CandidateStat.ts +13 -0
- package/src/PeerConnectionController/CodecStats.ts +19 -0
- package/src/PeerConnectionController/DataChannelStats.ts +17 -0
- package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
- package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
- package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
- package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
- package/src/PeerConnectionController/SessionStats.ts +10 -0
- package/src/PeerConnectionController/StreamStats.ts +11 -0
- package/src/PixelStreaming/PixelStreaming.test.ts +626 -0
- package/src/PixelStreaming/PixelStreaming.ts +851 -0
- package/src/UI/OnScreenKeyboard.ts +97 -0
- package/src/UeInstanceMessage/ResponseController.ts +47 -0
- package/src/UeInstanceMessage/SendMessageController.ts +154 -0
- package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
- package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
- package/src/Util/CoordinateConverter.ts +289 -0
- package/src/Util/EventEmitter.ts +611 -0
- package/src/Util/EventListenerTracker.ts +29 -0
- package/src/Util/FileUtil.ts +140 -0
- package/src/Util/RTCUtils.ts +41 -0
- package/src/Util/WebGLUtils.ts +49 -0
- package/src/Util/WebXRUtils.ts +25 -0
- package/src/VideoPlayer/StreamController.ts +89 -0
- package/src/VideoPlayer/VideoPlayer.ts +246 -0
- package/src/WebRtcPlayer/WebRtcPlayerController.ts +2158 -0
- package/src/WebXR/WebXRController.ts +319 -0
- package/src/__test__/mockMediaStream.ts +124 -0
- package/src/__test__/mockRTCPeerConnection.ts +347 -0
- package/src/__test__/mockRTCRtpReceiver.ts +22 -0
- package/src/__test__/mockWebSocket.ts +136 -0
- package/src/pixelstreamingfrontend.ts +46 -0
- package/tsconfig.jest.json +8 -0
- package/tsconfig.json +24 -0
- package/types/AFK/AFKController.d.ts +39 -0
- package/types/Config/Config.d.ts +218 -0
- package/types/Config/SettingBase.d.ts +30 -0
- package/types/Config/SettingFlag.d.ts +33 -0
- package/types/Config/SettingNumber.d.ts +45 -0
- package/types/Config/SettingOption.d.ts +43 -0
- package/types/Config/SettingText.d.ts +29 -0
- package/types/DataChannel/DataChannelController.d.ts +59 -0
- package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
- package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
- package/types/DataChannel/DataChannelSender.d.ts +21 -0
- package/types/DataChannel/InitialSettings.d.ts +44 -0
- package/types/DataChannel/LatencyTestResults.d.ts +31 -0
- package/types/FreezeFrame/FreezeFrame.d.ts +36 -0
- package/types/FreezeFrame/FreezeFrameController.d.ts +37 -0
- package/types/Inputs/FakeTouchController.d.ts +61 -0
- package/types/Inputs/GamepadController.d.ts +85 -0
- package/types/Inputs/GamepadTypes.d.ts +8 -0
- package/types/Inputs/HoveringMouseEvents.d.ts +56 -0
- package/types/Inputs/IMouseEvents.d.ts +53 -0
- package/types/Inputs/ITouchController.d.ts +24 -0
- package/types/Inputs/InputClassesFactory.d.ts +54 -0
- package/types/Inputs/KeyboardController.d.ts +62 -0
- package/types/Inputs/LockedMouseEvents.d.ts +80 -0
- package/types/Inputs/MouseButtons.d.ts +22 -0
- package/types/Inputs/MouseController.d.ts +75 -0
- package/types/Inputs/SpecialKeyCodes.d.ts +14 -0
- package/types/Inputs/TouchController.d.ts +53 -0
- package/types/Inputs/XRGamepadController.d.ts +15 -0
- package/types/PeerConnectionController/AggregatedStats.d.ts +77 -0
- package/types/PeerConnectionController/CandidatePairStats.d.ts +15 -0
- package/types/PeerConnectionController/CandidateStat.d.ts +11 -0
- package/types/PeerConnectionController/CodecStats.d.ts +14 -0
- package/types/PeerConnectionController/DataChannelStats.d.ts +15 -0
- package/types/PeerConnectionController/InboundRTPStats.d.ts +141 -0
- package/types/PeerConnectionController/InboundTrackStats.d.ts +32 -0
- package/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -0
- package/types/PeerConnectionController/PeerConnectionController.d.ts +132 -0
- package/types/PeerConnectionController/SessionStats.d.ts +8 -0
- package/types/PeerConnectionController/StreamStats.d.ts +9 -0
- package/types/PixelStreaming/PixelStreaming.d.ts +259 -0
- package/types/UI/OnScreenKeyboard.d.ts +31 -0
- package/types/UeInstanceMessage/ResponseController.d.ts +19 -0
- package/types/UeInstanceMessage/SendMessageController.d.ts +18 -0
- package/types/UeInstanceMessage/StreamMessageController.d.ts +29 -0
- package/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -0
- package/types/Util/CoordinateConverter.d.ts +100 -0
- package/types/Util/EventEmitter.d.ts +422 -0
- package/types/Util/EventListenerTracker.d.ts +14 -0
- package/types/Util/FileUtil.d.ts +32 -0
- package/types/Util/RTCUtils.d.ts +8 -0
- package/types/Util/WebGLUtils.d.ts +4 -0
- package/types/Util/WebXRUtils.d.ts +9 -0
- package/types/VideoPlayer/StreamController.d.ts +24 -0
- package/types/VideoPlayer/VideoPlayer.d.ts +78 -0
- package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +377 -0
- package/types/WebXR/WebXRController.d.ts +26 -0
- package/types/pixelstreamingfrontend.d.ts +22 -0
- package/webpack.common.js +35 -0
- package/webpack.dev.js +35 -0
- package/webpack.prod.js +36 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
|
|
4
|
+
import { Controller } from './GamepadTypes';
|
|
5
|
+
import { WebXRUtils } from '../Util/WebXRUtils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The class that handles the functionality of xrgamepads and controllers
|
|
9
|
+
*/
|
|
10
|
+
export class XRGamepadController {
|
|
11
|
+
controllers: Array<Controller>;
|
|
12
|
+
toStreamerMessagesProvider: StreamMessageController;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param toStreamerMessagesProvider - Stream message instance
|
|
16
|
+
*/
|
|
17
|
+
constructor(toStreamerMessagesProvider: StreamMessageController) {
|
|
18
|
+
this.toStreamerMessagesProvider = toStreamerMessagesProvider;
|
|
19
|
+
this.controllers = [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
updateStatus(
|
|
23
|
+
source: XRInputSource,
|
|
24
|
+
frame: XRFrame,
|
|
25
|
+
refSpace: XRReferenceSpace
|
|
26
|
+
) {
|
|
27
|
+
if (source.gamepad) {
|
|
28
|
+
const gamepadPose = frame.getPose(source.gripSpace, refSpace);
|
|
29
|
+
if (!gamepadPose) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let system = 0;
|
|
34
|
+
if (source.profiles.includes('htc-vive')) {
|
|
35
|
+
system = 1;
|
|
36
|
+
} else if (source.profiles.includes('oculus-touch')) {
|
|
37
|
+
system = 2;
|
|
38
|
+
}
|
|
39
|
+
// TODO (william.belcher): Add other profiles (Quest, Microsoft Mixed Reality, etc)
|
|
40
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get('XRSystem')([
|
|
41
|
+
system
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// Default: AnyHand (2)
|
|
45
|
+
let handedness = 2;
|
|
46
|
+
switch (source.handedness) {
|
|
47
|
+
case 'left':
|
|
48
|
+
handedness = 0;
|
|
49
|
+
break;
|
|
50
|
+
case 'right':
|
|
51
|
+
handedness = 1;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Send controller transform
|
|
56
|
+
const matrix = gamepadPose.transform.matrix;
|
|
57
|
+
const mat = [];
|
|
58
|
+
for (let i = 0; i < 16; i++) {
|
|
59
|
+
mat[i] = new Float32Array([matrix[i]])[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// prettier-ignore
|
|
63
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get('XRControllerTransform')([
|
|
64
|
+
mat[0], mat[4], mat[8], mat[12],
|
|
65
|
+
mat[1], mat[5], mat[9], mat[13],
|
|
66
|
+
mat[2], mat[6], mat[10], mat[14],
|
|
67
|
+
mat[3], mat[7], mat[11], mat[15],
|
|
68
|
+
handedness
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
// Handle controller buttons and axes
|
|
72
|
+
if (this.controllers[handedness] === undefined) {
|
|
73
|
+
this.controllers[handedness] = {
|
|
74
|
+
prevState: undefined,
|
|
75
|
+
currentState: undefined,
|
|
76
|
+
id: undefined
|
|
77
|
+
};
|
|
78
|
+
this.controllers[handedness].prevState =
|
|
79
|
+
WebXRUtils.deepCopyGamepad(source.gamepad);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.controllers[handedness].currentState =
|
|
83
|
+
WebXRUtils.deepCopyGamepad(source.gamepad);
|
|
84
|
+
|
|
85
|
+
const controller = this.controllers[handedness];
|
|
86
|
+
const currState = controller.currentState;
|
|
87
|
+
const prevState = controller.prevState;
|
|
88
|
+
// Iterate over buttons
|
|
89
|
+
for (let i = 0; i < currState.buttons.length; i++) {
|
|
90
|
+
const currButton = currState.buttons[i];
|
|
91
|
+
const prevButton = prevState.buttons[i];
|
|
92
|
+
|
|
93
|
+
if (currButton.pressed) {
|
|
94
|
+
// press
|
|
95
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get(
|
|
96
|
+
'XRButtonPressed'
|
|
97
|
+
)([handedness, i, prevButton.pressed ? 1 : 0]);
|
|
98
|
+
} else if (!currButton.pressed && prevButton.pressed) {
|
|
99
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get(
|
|
100
|
+
'XRButtonReleased'
|
|
101
|
+
)([handedness, i, 0]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (currButton.touched && !currButton.pressed) {
|
|
105
|
+
// press
|
|
106
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get(
|
|
107
|
+
'XRButtonPressed'
|
|
108
|
+
)([handedness, 3, prevButton.touched ? 1 : 0]);
|
|
109
|
+
} else if (!currButton.touched && prevButton.touched) {
|
|
110
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get(
|
|
111
|
+
'XRButtonReleased'
|
|
112
|
+
)([handedness, 3, 0]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Iterate over gamepad axes
|
|
117
|
+
for (let i = 0; i < currState.axes.length; i++) {
|
|
118
|
+
this.toStreamerMessagesProvider.toStreamerHandlers.get(
|
|
119
|
+
'XRAnalog'
|
|
120
|
+
)([handedness, i, currState.axes[i]]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.controllers[handedness].prevState = currState;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
InboundRTPStats,
|
|
5
|
+
InboundVideoStats,
|
|
6
|
+
InboundAudioStats
|
|
7
|
+
} from './InboundRTPStats';
|
|
8
|
+
import { InboundTrackStats } from './InboundTrackStats';
|
|
9
|
+
import { DataChannelStats } from './DataChannelStats';
|
|
10
|
+
import { CandidateStat } from './CandidateStat';
|
|
11
|
+
import { CandidatePairStats } from './CandidatePairStats';
|
|
12
|
+
import { OutBoundRTPStats, OutBoundVideoStats } from './OutBoundRTPStats';
|
|
13
|
+
import { SessionStats } from './SessionStats';
|
|
14
|
+
import { StreamStats } from './StreamStats';
|
|
15
|
+
import { CodecStats } from './CodecStats';
|
|
16
|
+
import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The Aggregated Stats that is generated from the RTC Stats Report
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
type RTCStatsTypePS = RTCStatsType | 'stream' | 'media-playout';
|
|
23
|
+
export class AggregatedStats {
|
|
24
|
+
inboundVideoStats: InboundVideoStats;
|
|
25
|
+
inboundAudioStats: InboundAudioStats;
|
|
26
|
+
lastVideoStats: InboundVideoStats;
|
|
27
|
+
lastAudioStats: InboundAudioStats;
|
|
28
|
+
candidatePair: CandidatePairStats;
|
|
29
|
+
DataChannelStats: DataChannelStats;
|
|
30
|
+
localCandidates: Array<CandidateStat>;
|
|
31
|
+
remoteCandidates: Array<CandidateStat>;
|
|
32
|
+
outBoundVideoStats: OutBoundVideoStats;
|
|
33
|
+
sessionStats: SessionStats;
|
|
34
|
+
streamStats: StreamStats;
|
|
35
|
+
codecs: Map<string, string>;
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this.inboundVideoStats = new InboundVideoStats();
|
|
39
|
+
this.inboundAudioStats = new InboundAudioStats();
|
|
40
|
+
this.candidatePair = new CandidatePairStats();
|
|
41
|
+
this.DataChannelStats = new DataChannelStats();
|
|
42
|
+
this.outBoundVideoStats = new OutBoundVideoStats();
|
|
43
|
+
this.sessionStats = new SessionStats();
|
|
44
|
+
this.streamStats = new StreamStats();
|
|
45
|
+
this.codecs = new Map<string, string>();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gather all the information from the RTC Peer Connection Report
|
|
50
|
+
* @param rtcStatsReport - RTC Stats Report
|
|
51
|
+
*/
|
|
52
|
+
processStats(rtcStatsReport: RTCStatsReport) {
|
|
53
|
+
this.localCandidates = new Array<CandidateStat>();
|
|
54
|
+
this.remoteCandidates = new Array<CandidateStat>();
|
|
55
|
+
|
|
56
|
+
rtcStatsReport.forEach((stat) => {
|
|
57
|
+
const type: RTCStatsTypePS = stat.type;
|
|
58
|
+
|
|
59
|
+
switch (type) {
|
|
60
|
+
case 'candidate-pair':
|
|
61
|
+
this.handleCandidatePair(stat);
|
|
62
|
+
break;
|
|
63
|
+
case 'certificate':
|
|
64
|
+
break;
|
|
65
|
+
case 'codec':
|
|
66
|
+
this.handleCodec(stat);
|
|
67
|
+
break;
|
|
68
|
+
case 'data-channel':
|
|
69
|
+
this.handleDataChannel(stat);
|
|
70
|
+
break;
|
|
71
|
+
case 'inbound-rtp':
|
|
72
|
+
this.handleInBoundRTP(stat);
|
|
73
|
+
break;
|
|
74
|
+
case 'local-candidate':
|
|
75
|
+
this.handleLocalCandidate(stat);
|
|
76
|
+
break;
|
|
77
|
+
case 'media-source':
|
|
78
|
+
break;
|
|
79
|
+
case 'media-playout':
|
|
80
|
+
break;
|
|
81
|
+
case 'outbound-rtp':
|
|
82
|
+
break;
|
|
83
|
+
case 'peer-connection':
|
|
84
|
+
break;
|
|
85
|
+
case 'remote-candidate':
|
|
86
|
+
this.handleRemoteCandidate(stat);
|
|
87
|
+
break;
|
|
88
|
+
case 'remote-inbound-rtp':
|
|
89
|
+
break;
|
|
90
|
+
case 'remote-outbound-rtp':
|
|
91
|
+
this.handleRemoteOutBound(stat);
|
|
92
|
+
break;
|
|
93
|
+
case 'track':
|
|
94
|
+
this.handleTrack(stat);
|
|
95
|
+
break;
|
|
96
|
+
case 'transport':
|
|
97
|
+
break;
|
|
98
|
+
case 'stream':
|
|
99
|
+
this.handleStream(stat);
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
Logger.Error(Logger.GetStackTrace(), 'unhandled Stat Type');
|
|
103
|
+
Logger.Log(Logger.GetStackTrace(), stat);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Process stream stats data from webrtc
|
|
111
|
+
*
|
|
112
|
+
* @param stat - the stats coming in from webrtc
|
|
113
|
+
*/
|
|
114
|
+
handleStream(stat: StreamStats) {
|
|
115
|
+
this.streamStats = stat;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Process the Ice Candidate Pair Data
|
|
120
|
+
* @param stat - the stats coming in from ice candidates
|
|
121
|
+
*/
|
|
122
|
+
handleCandidatePair(stat: CandidatePairStats) {
|
|
123
|
+
this.candidatePair.bytesReceived = stat.bytesReceived;
|
|
124
|
+
this.candidatePair.bytesSent = stat.bytesSent;
|
|
125
|
+
this.candidatePair.localCandidateId = stat.localCandidateId;
|
|
126
|
+
this.candidatePair.remoteCandidateId = stat.remoteCandidateId;
|
|
127
|
+
this.candidatePair.nominated = stat.nominated;
|
|
128
|
+
this.candidatePair.readable = stat.readable;
|
|
129
|
+
this.candidatePair.selected = stat.selected;
|
|
130
|
+
this.candidatePair.writable = stat.writable;
|
|
131
|
+
this.candidatePair.state = stat.state;
|
|
132
|
+
this.candidatePair.currentRoundTripTime = stat.currentRoundTripTime;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Process the Data Channel Data
|
|
137
|
+
* @param stat - the stats coming in from the data channel
|
|
138
|
+
*/
|
|
139
|
+
handleDataChannel(stat: DataChannelStats) {
|
|
140
|
+
this.DataChannelStats.bytesReceived = stat.bytesReceived;
|
|
141
|
+
this.DataChannelStats.bytesSent = stat.bytesSent;
|
|
142
|
+
this.DataChannelStats.dataChannelIdentifier =
|
|
143
|
+
stat.dataChannelIdentifier;
|
|
144
|
+
this.DataChannelStats.id = stat.id;
|
|
145
|
+
this.DataChannelStats.label = stat.label;
|
|
146
|
+
this.DataChannelStats.messagesReceived = stat.messagesReceived;
|
|
147
|
+
this.DataChannelStats.messagesSent = stat.messagesSent;
|
|
148
|
+
this.DataChannelStats.protocol = stat.protocol;
|
|
149
|
+
this.DataChannelStats.state = stat.state;
|
|
150
|
+
this.DataChannelStats.timestamp = stat.timestamp;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Process the Local Ice Candidate Data
|
|
155
|
+
* @param stat - local stats
|
|
156
|
+
*/
|
|
157
|
+
handleLocalCandidate(stat: CandidateStat) {
|
|
158
|
+
const localCandidate = new CandidateStat();
|
|
159
|
+
localCandidate.label = 'local-candidate';
|
|
160
|
+
localCandidate.address = stat.address;
|
|
161
|
+
localCandidate.port = stat.port;
|
|
162
|
+
localCandidate.protocol = stat.protocol;
|
|
163
|
+
localCandidate.candidateType = stat.candidateType;
|
|
164
|
+
localCandidate.id = stat.id;
|
|
165
|
+
this.localCandidates.push(localCandidate);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Process the Remote Ice Candidate Data
|
|
170
|
+
* @param stat - ice candidate stats
|
|
171
|
+
*/
|
|
172
|
+
handleRemoteCandidate(stat: CandidateStat) {
|
|
173
|
+
const RemoteCandidate = new CandidateStat();
|
|
174
|
+
RemoteCandidate.label = 'local-candidate';
|
|
175
|
+
RemoteCandidate.address = stat.address;
|
|
176
|
+
RemoteCandidate.port = stat.port;
|
|
177
|
+
RemoteCandidate.protocol = stat.protocol;
|
|
178
|
+
RemoteCandidate.id = stat.id;
|
|
179
|
+
RemoteCandidate.candidateType = stat.candidateType;
|
|
180
|
+
this.remoteCandidates.push(RemoteCandidate);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Process the Inbound RTP Audio and Video Data
|
|
185
|
+
* @param stat - inbound rtp stats
|
|
186
|
+
*/
|
|
187
|
+
handleInBoundRTP(stat: InboundRTPStats) {
|
|
188
|
+
switch (stat.kind) {
|
|
189
|
+
case 'video':
|
|
190
|
+
// Need to convert to unknown first to remove an error around
|
|
191
|
+
// InboundVideoStats having the bitrate member which isn't found on
|
|
192
|
+
// the InboundRTPStats
|
|
193
|
+
this.inboundVideoStats = stat as unknown as InboundVideoStats;
|
|
194
|
+
|
|
195
|
+
if (this.lastVideoStats != undefined) {
|
|
196
|
+
this.inboundVideoStats.bitrate =
|
|
197
|
+
(8 *
|
|
198
|
+
(this.inboundVideoStats.bytesReceived -
|
|
199
|
+
this.lastVideoStats.bytesReceived)) /
|
|
200
|
+
(this.inboundVideoStats.timestamp -
|
|
201
|
+
this.lastVideoStats.timestamp);
|
|
202
|
+
this.inboundVideoStats.bitrate = Math.floor(
|
|
203
|
+
this.inboundVideoStats.bitrate
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
this.lastVideoStats = { ...this.inboundVideoStats };
|
|
207
|
+
break;
|
|
208
|
+
case 'audio':
|
|
209
|
+
// Need to convert to unknown first to remove an error around
|
|
210
|
+
// InboundAudioStats having the bitrate member which isn't found on
|
|
211
|
+
// the InboundRTPStats
|
|
212
|
+
this.inboundAudioStats = stat as unknown as InboundAudioStats;
|
|
213
|
+
|
|
214
|
+
if (this.lastAudioStats != undefined) {
|
|
215
|
+
this.inboundAudioStats.bitrate =
|
|
216
|
+
(8 *
|
|
217
|
+
(this.inboundAudioStats.bytesReceived -
|
|
218
|
+
this.lastAudioStats.bytesReceived)) /
|
|
219
|
+
(this.inboundAudioStats.timestamp -
|
|
220
|
+
this.lastAudioStats.timestamp);
|
|
221
|
+
this.inboundAudioStats.bitrate = Math.floor(
|
|
222
|
+
this.inboundAudioStats.bitrate
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
this.lastAudioStats = { ...this.inboundAudioStats };
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
Logger.Log(Logger.GetStackTrace(), 'Kind is not handled');
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Process the outbound RTP Audio and Video Data
|
|
235
|
+
* @param stat - remote outbound stats
|
|
236
|
+
*/
|
|
237
|
+
handleRemoteOutBound(stat: OutBoundRTPStats) {
|
|
238
|
+
switch (stat.kind) {
|
|
239
|
+
case 'video':
|
|
240
|
+
this.outBoundVideoStats.bytesSent = stat.bytesSent;
|
|
241
|
+
this.outBoundVideoStats.id = stat.id;
|
|
242
|
+
this.outBoundVideoStats.localId = stat.localId;
|
|
243
|
+
this.outBoundVideoStats.packetsSent = stat.packetsSent;
|
|
244
|
+
this.outBoundVideoStats.remoteTimestamp = stat.remoteTimestamp;
|
|
245
|
+
this.outBoundVideoStats.timestamp = stat.timestamp;
|
|
246
|
+
break;
|
|
247
|
+
case 'audio':
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
default:
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Process the Inbound Video Track Data
|
|
257
|
+
* @param stat - video track stats
|
|
258
|
+
*/
|
|
259
|
+
handleTrack(stat: InboundTrackStats) {
|
|
260
|
+
// we only want to extract stats from the video track
|
|
261
|
+
if (
|
|
262
|
+
stat.type === 'track' &&
|
|
263
|
+
(stat.trackIdentifier === 'video_label' || stat.kind === 'video')
|
|
264
|
+
) {
|
|
265
|
+
this.inboundVideoStats.framesDropped = stat.framesDropped;
|
|
266
|
+
this.inboundVideoStats.framesReceived = stat.framesReceived;
|
|
267
|
+
this.inboundVideoStats.frameHeight = stat.frameHeight;
|
|
268
|
+
this.inboundVideoStats.frameWidth = stat.frameWidth;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
handleCodec(stat: CodecStats) {
|
|
273
|
+
const codecId = stat.id;
|
|
274
|
+
const codecType = `${stat.mimeType
|
|
275
|
+
.replace('video/', '')
|
|
276
|
+
.replace('audio/', '')}${
|
|
277
|
+
stat.sdpFmtpLine ? ` ${stat.sdpFmtpLine}` : ''
|
|
278
|
+
}`;
|
|
279
|
+
this.codecs.set(codecId, codecType);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
handleSessionStatistics(
|
|
283
|
+
videoStartTime: number,
|
|
284
|
+
inputController: boolean | null,
|
|
285
|
+
videoEncoderAvgQP: number
|
|
286
|
+
) {
|
|
287
|
+
const deltaTime = Date.now() - videoStartTime;
|
|
288
|
+
this.sessionStats.runTime = new Date(deltaTime)
|
|
289
|
+
.toISOString()
|
|
290
|
+
.substr(11, 8)
|
|
291
|
+
.toString();
|
|
292
|
+
|
|
293
|
+
const controlsStreamInput =
|
|
294
|
+
inputController === null
|
|
295
|
+
? 'Not sent yet'
|
|
296
|
+
: inputController
|
|
297
|
+
? 'true'
|
|
298
|
+
: 'false';
|
|
299
|
+
this.sessionStats.controlsStreamInput = controlsStreamInput;
|
|
300
|
+
|
|
301
|
+
this.sessionStats.videoEncoderAvgQP = videoEncoderAvgQP;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Check if a value coming in from our stats is actually a number
|
|
306
|
+
* @param value - the number to be checked
|
|
307
|
+
*/
|
|
308
|
+
isNumber(value: unknown): boolean {
|
|
309
|
+
return typeof value === 'number' && isFinite(value);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ICE Candidate Pair Stats collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class CandidatePairStats {
|
|
7
|
+
bytesReceived: number;
|
|
8
|
+
bytesSent: number;
|
|
9
|
+
localCandidateId: string;
|
|
10
|
+
remoteCandidateId: string;
|
|
11
|
+
nominated: boolean;
|
|
12
|
+
readable: boolean;
|
|
13
|
+
writable: boolean;
|
|
14
|
+
selected: boolean;
|
|
15
|
+
state: string;
|
|
16
|
+
currentRoundTripTime: number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ICE Candidate Stat collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class CandidateStat {
|
|
7
|
+
label: string;
|
|
8
|
+
id: string;
|
|
9
|
+
address: string;
|
|
10
|
+
candidateType: string;
|
|
11
|
+
port: number;
|
|
12
|
+
protocol: 'tcp' | 'udp';
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Codec Stats collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class CodecStats {
|
|
7
|
+
/* common stats */
|
|
8
|
+
clockRate: number;
|
|
9
|
+
id: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
payloadType: number;
|
|
12
|
+
sdpFmtpLine: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
transportId: string;
|
|
15
|
+
type: string;
|
|
16
|
+
|
|
17
|
+
/* audio specific stats */
|
|
18
|
+
channels: number;
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data Channel Stats collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class DataChannelStats {
|
|
7
|
+
bytesReceived: number;
|
|
8
|
+
bytesSent: number;
|
|
9
|
+
dataChannelIdentifier: number;
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
messagesReceived: number;
|
|
13
|
+
messagesSent: number;
|
|
14
|
+
protocol: string;
|
|
15
|
+
state: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inbound Audio Stats collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class InboundAudioStats {
|
|
7
|
+
audioLevel: number;
|
|
8
|
+
bytesReceived: number;
|
|
9
|
+
codecId: string;
|
|
10
|
+
concealedSamples: number;
|
|
11
|
+
concealmentEvents: number;
|
|
12
|
+
fecPacketsDiscarded: number;
|
|
13
|
+
fecPacketsReceived: number;
|
|
14
|
+
headerBytesReceived: number;
|
|
15
|
+
id: string;
|
|
16
|
+
insertedSamplesForDeceleration: number;
|
|
17
|
+
jitter: number;
|
|
18
|
+
jitterBufferDelay: number;
|
|
19
|
+
jitterBufferEmittedCount: number;
|
|
20
|
+
jitterBufferMinimumDelay: number;
|
|
21
|
+
jitterBufferTargetDelay: number;
|
|
22
|
+
kind: string;
|
|
23
|
+
lastPacketReceivedTimestamp: number;
|
|
24
|
+
mediaType: string;
|
|
25
|
+
mid: string;
|
|
26
|
+
packetsDiscarded: number;
|
|
27
|
+
packetsLost: number;
|
|
28
|
+
packetsReceived: number;
|
|
29
|
+
removedSamplesForAcceleration: number;
|
|
30
|
+
silentConcealedSamples: number;
|
|
31
|
+
ssrc: number;
|
|
32
|
+
timestamp: number;
|
|
33
|
+
totalAudioEnergy: number;
|
|
34
|
+
totalSamplesDuration: number;
|
|
35
|
+
totalSamplesReceived: number;
|
|
36
|
+
trackIdentifier: string;
|
|
37
|
+
transportId: string;
|
|
38
|
+
type: string;
|
|
39
|
+
|
|
40
|
+
/* additional, custom stats */
|
|
41
|
+
bitrate: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Inbound Video Stats collected from the RTC Stats Report
|
|
46
|
+
*/
|
|
47
|
+
export class InboundVideoStats {
|
|
48
|
+
bytesReceived: number;
|
|
49
|
+
codecId: string;
|
|
50
|
+
firCount: number;
|
|
51
|
+
frameHeight: number;
|
|
52
|
+
frameWidth: number;
|
|
53
|
+
framesAssembledFromMultiplePackets: number;
|
|
54
|
+
framesDecoded: number;
|
|
55
|
+
framesDropped: number;
|
|
56
|
+
framesPerSecond: number;
|
|
57
|
+
framesReceived: number;
|
|
58
|
+
freezeCount: number;
|
|
59
|
+
googTimingFrameInfo: string;
|
|
60
|
+
headerBytesReceived: number;
|
|
61
|
+
id: string;
|
|
62
|
+
jitter: number;
|
|
63
|
+
jitterBufferDelay: number;
|
|
64
|
+
jitterBufferEmittedCount: number;
|
|
65
|
+
keyFramesDecoded: number;
|
|
66
|
+
kind: string;
|
|
67
|
+
lastPacketReceivedTimestamp: number;
|
|
68
|
+
mediaType: string;
|
|
69
|
+
mid: string;
|
|
70
|
+
nackCount: number;
|
|
71
|
+
packetsLost: number;
|
|
72
|
+
packetsReceived: number;
|
|
73
|
+
pauseCount: number;
|
|
74
|
+
pliCount: number;
|
|
75
|
+
ssrc: number;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
totalAssemblyTime: number;
|
|
78
|
+
totalDecodeTime: number;
|
|
79
|
+
totalFreezesDuration: number;
|
|
80
|
+
totalInterFrameDelay: number;
|
|
81
|
+
totalPausesDuration: number;
|
|
82
|
+
totalProcessingDelay: number;
|
|
83
|
+
totalSquaredInterFrameDelay: number;
|
|
84
|
+
trackIdentifier: string;
|
|
85
|
+
transportId: string;
|
|
86
|
+
type: string;
|
|
87
|
+
|
|
88
|
+
/* additional, custom stats */
|
|
89
|
+
bitrate: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Inbound Stats collected from the RTC Stats Report
|
|
94
|
+
*/
|
|
95
|
+
export class InboundRTPStats {
|
|
96
|
+
/* common stats */
|
|
97
|
+
bytesReceived: number;
|
|
98
|
+
codecId: string;
|
|
99
|
+
headerBytesReceived: number;
|
|
100
|
+
id: string;
|
|
101
|
+
jitter: number;
|
|
102
|
+
jitterBufferDelay: number;
|
|
103
|
+
jitterBufferEmittedCount: number;
|
|
104
|
+
kind: string;
|
|
105
|
+
lastPacketReceivedTimestamp: number;
|
|
106
|
+
mediaType: string;
|
|
107
|
+
mid: string;
|
|
108
|
+
packetsLost: number;
|
|
109
|
+
packetsReceived: number;
|
|
110
|
+
ssrc: number;
|
|
111
|
+
timestamp: number;
|
|
112
|
+
trackIdentifier: string;
|
|
113
|
+
transportId: string;
|
|
114
|
+
type: string;
|
|
115
|
+
|
|
116
|
+
/* audio specific stats */
|
|
117
|
+
audioLevel: number;
|
|
118
|
+
concealedSamples: number;
|
|
119
|
+
concealmentEvents: number;
|
|
120
|
+
fecPacketsDiscarded: number;
|
|
121
|
+
fecPacketsReceived: number;
|
|
122
|
+
insertedSamplesForDeceleration: number;
|
|
123
|
+
jitterBufferMinimumDelay: number;
|
|
124
|
+
jitterBufferTargetDelay: number;
|
|
125
|
+
packetsDiscarded: number;
|
|
126
|
+
removedSamplesForAcceleration: number;
|
|
127
|
+
silentConcealedSamples: number;
|
|
128
|
+
totalAudioEnergy: number;
|
|
129
|
+
totalSamplesDuration: number;
|
|
130
|
+
totalSamplesReceived: number;
|
|
131
|
+
|
|
132
|
+
/* video specific stats */
|
|
133
|
+
firCount: number;
|
|
134
|
+
frameHeight: number;
|
|
135
|
+
frameWidth: number;
|
|
136
|
+
framesAssembledFromMultiplePackets: number;
|
|
137
|
+
framesDecoded: number;
|
|
138
|
+
framesDropped: number;
|
|
139
|
+
framesPerSecond: number;
|
|
140
|
+
framesReceived: number;
|
|
141
|
+
freezeCount: number;
|
|
142
|
+
googTimingFrameInfo: string;
|
|
143
|
+
keyFramesDecoded: number;
|
|
144
|
+
nackCount: number;
|
|
145
|
+
pauseCount: number;
|
|
146
|
+
pliCount: number;
|
|
147
|
+
totalAssemblyTime: number;
|
|
148
|
+
totalDecodeTime: number;
|
|
149
|
+
totalFreezesDuration: number;
|
|
150
|
+
totalInterFrameDelay: number;
|
|
151
|
+
totalPausesDuration: number;
|
|
152
|
+
totalProcessingDelay: number;
|
|
153
|
+
totalSquaredInterFrameDelay: number;
|
|
154
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inbound Track Stats collected from the RTC Stats Report
|
|
5
|
+
*/
|
|
6
|
+
export class InboundTrackStats {
|
|
7
|
+
type: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
trackIdentifier: string;
|
|
10
|
+
receiveToCompositeMs: number;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
bytesReceived: number;
|
|
13
|
+
framesDecoded: number;
|
|
14
|
+
packetsLost: number;
|
|
15
|
+
bytesReceivedStart: number;
|
|
16
|
+
framesDecodedStart: number;
|
|
17
|
+
timestampStart: number;
|
|
18
|
+
bitrate: number;
|
|
19
|
+
lowBitrate: number;
|
|
20
|
+
highBitrate: number;
|
|
21
|
+
avgBitrate: number;
|
|
22
|
+
framerate: number;
|
|
23
|
+
lowFramerate: number;
|
|
24
|
+
highFramerate: number;
|
|
25
|
+
averageFrameRate: number;
|
|
26
|
+
framesDropped: number;
|
|
27
|
+
framesReceived: number;
|
|
28
|
+
framesDroppedPercentage: number;
|
|
29
|
+
frameHeight: number;
|
|
30
|
+
frameWidth: number;
|
|
31
|
+
frameHeightStart: number;
|
|
32
|
+
frameWidthStart: number;
|
|
33
|
+
jitter: number;
|
|
34
|
+
}
|