@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.4.7 → 1.0.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 (218) hide show
  1. package/.lintstagedrc.mjs +7 -0
  2. package/dist/{commonjs → cjs}/Config/Config.js +8 -0
  3. package/dist/cjs/Config/Config.js.map +1 -0
  4. package/dist/{commonjs → cjs}/Config/SettingBase.js +1 -3
  5. package/dist/cjs/Config/SettingBase.js.map +1 -0
  6. package/dist/{commonjs → cjs}/Config/SettingFlag.js +1 -3
  7. package/dist/cjs/Config/SettingFlag.js.map +1 -0
  8. package/dist/{commonjs → cjs}/Config/SettingNumber.js +1 -3
  9. package/dist/cjs/Config/SettingNumber.js.map +1 -0
  10. package/dist/{commonjs → cjs}/Config/SettingOption.js +2 -6
  11. package/dist/cjs/Config/SettingOption.js.map +1 -0
  12. package/dist/{commonjs → cjs}/Config/SettingText.js +1 -3
  13. package/dist/cjs/Config/SettingText.js.map +1 -0
  14. package/dist/{commonjs → cjs}/Inputs/GamepadController.js +0 -2
  15. package/dist/cjs/Inputs/GamepadController.js.map +1 -0
  16. package/dist/{commonjs → cjs}/PeerConnectionController/AggregatedStats.js +103 -45
  17. package/dist/cjs/PeerConnectionController/AggregatedStats.js.map +1 -0
  18. package/dist/{commonjs → cjs}/PeerConnectionController/InboundRTPStats.js.map +1 -1
  19. package/dist/cjs/PeerConnectionController/LatencyCalculator.js +290 -0
  20. package/dist/cjs/PeerConnectionController/LatencyCalculator.js.map +1 -0
  21. package/dist/cjs/PeerConnectionController/OutBoundRTPStats.js +21 -0
  22. package/dist/cjs/PeerConnectionController/OutBoundRTPStats.js.map +1 -0
  23. package/dist/{commonjs → cjs}/PeerConnectionController/PeerConnectionController.js +53 -19
  24. package/dist/cjs/PeerConnectionController/PeerConnectionController.js.map +1 -0
  25. package/dist/{commonjs → cjs}/PixelStreaming/PixelStreaming.js +24 -3
  26. package/dist/cjs/PixelStreaming/PixelStreaming.js.map +1 -0
  27. package/dist/{commonjs → cjs}/Util/EventEmitter.js +38 -1
  28. package/dist/cjs/Util/EventEmitter.js.map +1 -0
  29. package/dist/{commonjs → cjs}/WebRtcPlayer/WebRtcPlayerController.js +41 -5
  30. package/dist/cjs/WebRtcPlayer/WebRtcPlayerController.js.map +1 -0
  31. package/dist/cjs/__test__/mockMediaStream.js +100 -0
  32. package/dist/cjs/__test__/mockMediaStream.js.map +1 -0
  33. package/dist/cjs/__test__/mockRTCPeerConnection.js +252 -0
  34. package/dist/cjs/__test__/mockRTCPeerConnection.js.map +1 -0
  35. package/dist/cjs/__test__/mockRTCRtpReceiver.js +26 -0
  36. package/dist/cjs/__test__/mockRTCRtpReceiver.js.map +1 -0
  37. package/dist/cjs/__test__/mockWebSocket.js +109 -0
  38. package/dist/cjs/__test__/mockWebSocket.js.map +1 -0
  39. package/dist/{commonjs → cjs}/pixelstreamingfrontend.js +4 -2
  40. package/dist/{commonjs → cjs}/pixelstreamingfrontend.js.map +1 -1
  41. package/dist/esm/Config/Config.js +8 -0
  42. package/dist/esm/Config/Config.js.map +1 -1
  43. package/dist/esm/Config/SettingBase.js +1 -3
  44. package/dist/esm/Config/SettingBase.js.map +1 -1
  45. package/dist/esm/Config/SettingFlag.js +1 -3
  46. package/dist/esm/Config/SettingFlag.js.map +1 -1
  47. package/dist/esm/Config/SettingNumber.js +1 -3
  48. package/dist/esm/Config/SettingNumber.js.map +1 -1
  49. package/dist/esm/Config/SettingOption.js +2 -6
  50. package/dist/esm/Config/SettingOption.js.map +1 -1
  51. package/dist/esm/Config/SettingText.js +1 -3
  52. package/dist/esm/Config/SettingText.js.map +1 -1
  53. package/dist/esm/Inputs/GamepadController.js +0 -2
  54. package/dist/esm/Inputs/GamepadController.js.map +1 -1
  55. package/dist/esm/PeerConnectionController/AggregatedStats.js +104 -46
  56. package/dist/esm/PeerConnectionController/AggregatedStats.js.map +1 -1
  57. package/dist/esm/PeerConnectionController/InboundRTPStats.js.map +1 -1
  58. package/dist/esm/PeerConnectionController/LatencyCalculator.js +284 -0
  59. package/dist/esm/PeerConnectionController/LatencyCalculator.js.map +1 -0
  60. package/dist/esm/PeerConnectionController/OutBoundRTPStats.js +8 -4
  61. package/dist/esm/PeerConnectionController/OutBoundRTPStats.js.map +1 -1
  62. package/dist/esm/PeerConnectionController/PeerConnectionController.js +52 -18
  63. package/dist/esm/PeerConnectionController/PeerConnectionController.js.map +1 -1
  64. package/dist/esm/PixelStreaming/PixelStreaming.js +25 -4
  65. package/dist/esm/PixelStreaming/PixelStreaming.js.map +1 -1
  66. package/dist/esm/Util/EventEmitter.js +33 -0
  67. package/dist/esm/Util/EventEmitter.js.map +1 -1
  68. package/dist/esm/WebRtcPlayer/WebRtcPlayerController.js +42 -6
  69. package/dist/esm/WebRtcPlayer/WebRtcPlayerController.js.map +1 -1
  70. package/dist/esm/__test__/mockMediaStream.js +92 -0
  71. package/dist/esm/__test__/mockMediaStream.js.map +1 -0
  72. package/dist/esm/__test__/mockRTCPeerConnection.js +242 -0
  73. package/dist/esm/__test__/mockRTCPeerConnection.js.map +1 -0
  74. package/dist/esm/__test__/mockRTCRtpReceiver.js +21 -0
  75. package/dist/esm/__test__/mockRTCRtpReceiver.js.map +1 -0
  76. package/dist/esm/__test__/mockWebSocket.js +103 -0
  77. package/dist/esm/__test__/mockWebSocket.js.map +1 -0
  78. package/dist/esm/pixelstreamingfrontend.js +2 -1
  79. package/dist/esm/pixelstreamingfrontend.js.map +1 -1
  80. package/dist/types/Config/Config.d.ts +2 -0
  81. package/dist/types/PeerConnectionController/AggregatedStats.d.ts +18 -7
  82. package/dist/types/PeerConnectionController/InboundRTPStats.d.ts +88 -85
  83. package/dist/types/PeerConnectionController/LatencyCalculator.d.ts +87 -0
  84. package/dist/types/PeerConnectionController/OutBoundRTPStats.d.ts +46 -12
  85. package/dist/types/PeerConnectionController/PeerConnectionController.d.ts +17 -3
  86. package/dist/types/PixelStreaming/PixelStreaming.d.ts +17 -3
  87. package/dist/types/Util/EventEmitter.d.ts +41 -1
  88. package/dist/types/VideoPlayer/VideoPlayer.d.ts +1 -1
  89. package/dist/types/WebRtcPlayer/WebRtcPlayerController.d.ts +4 -1
  90. package/dist/types/__test__/mockMediaStream.d.ts +49 -0
  91. package/dist/types/__test__/mockRTCPeerConnection.d.ts +134 -0
  92. package/dist/types/__test__/mockRTCRtpReceiver.d.ts +3 -0
  93. package/dist/types/__test__/mockWebSocket.d.ts +33 -0
  94. package/dist/types/pixelstreamingfrontend.d.ts +2 -1
  95. package/eslint.config.mjs +52 -0
  96. package/package.json +47 -45
  97. package/src/Config/Config.ts +30 -0
  98. package/src/Config/SettingBase.ts +1 -1
  99. package/src/Config/SettingFlag.ts +1 -1
  100. package/src/Config/SettingNumber.ts +1 -1
  101. package/src/Config/SettingOption.ts +2 -2
  102. package/src/Config/SettingText.ts +1 -1
  103. package/src/Inputs/GamepadController.ts +2 -2
  104. package/src/PeerConnectionController/AggregatedStats.ts +111 -52
  105. package/src/PeerConnectionController/InboundRTPStats.ts +88 -85
  106. package/src/PeerConnectionController/LatencyCalculator.ts +392 -0
  107. package/src/PeerConnectionController/OutBoundRTPStats.ts +46 -12
  108. package/src/PeerConnectionController/PeerConnectionController.ts +72 -19
  109. package/src/PixelStreaming/PixelStreaming.ts +34 -4
  110. package/src/Util/EventEmitter.ts +60 -0
  111. package/src/VideoPlayer/VideoPlayer.ts +1 -1
  112. package/src/WebRtcPlayer/WebRtcPlayerController.ts +53 -7
  113. package/src/__test__/mockRTCPeerConnection.ts +1 -1
  114. package/src/pixelstreamingfrontend.ts +2 -1
  115. package/{tsconfig.json → tsconfig.base.json} +5 -3
  116. package/tsconfig.cjs.json +3 -5
  117. package/tsconfig.esm.json +3 -3
  118. package/tsconfig.jest.json +6 -6
  119. package/.eslintignore +0 -12
  120. package/.eslintrc.js +0 -20
  121. package/.prettierrc.json +0 -7
  122. package/dist/commonjs/Config/Config.js.map +0 -1
  123. package/dist/commonjs/Config/SettingBase.js.map +0 -1
  124. package/dist/commonjs/Config/SettingFlag.js.map +0 -1
  125. package/dist/commonjs/Config/SettingNumber.js.map +0 -1
  126. package/dist/commonjs/Config/SettingOption.js.map +0 -1
  127. package/dist/commonjs/Config/SettingText.js.map +0 -1
  128. package/dist/commonjs/Inputs/GamepadController.js.map +0 -1
  129. package/dist/commonjs/PeerConnectionController/AggregatedStats.js.map +0 -1
  130. package/dist/commonjs/PeerConnectionController/OutBoundRTPStats.js +0 -17
  131. package/dist/commonjs/PeerConnectionController/OutBoundRTPStats.js.map +0 -1
  132. package/dist/commonjs/PeerConnectionController/PeerConnectionController.js.map +0 -1
  133. package/dist/commonjs/PixelStreaming/PixelStreaming.js.map +0 -1
  134. package/dist/commonjs/Util/EventEmitter.js.map +0 -1
  135. package/dist/commonjs/WebRtcPlayer/WebRtcPlayerController.js.map +0 -1
  136. /package/dist/{commonjs → cjs}/AFK/AFKController.js +0 -0
  137. /package/dist/{commonjs → cjs}/AFK/AFKController.js.map +0 -0
  138. /package/dist/{commonjs → cjs}/DataChannel/DataChannelController.js +0 -0
  139. /package/dist/{commonjs → cjs}/DataChannel/DataChannelController.js.map +0 -0
  140. /package/dist/{commonjs → cjs}/DataChannel/DataChannelLatencyTestController.js +0 -0
  141. /package/dist/{commonjs → cjs}/DataChannel/DataChannelLatencyTestController.js.map +0 -0
  142. /package/dist/{commonjs → cjs}/DataChannel/DataChannelLatencyTestResults.js +0 -0
  143. /package/dist/{commonjs → cjs}/DataChannel/DataChannelLatencyTestResults.js.map +0 -0
  144. /package/dist/{commonjs → cjs}/DataChannel/DataChannelSender.js +0 -0
  145. /package/dist/{commonjs → cjs}/DataChannel/DataChannelSender.js.map +0 -0
  146. /package/dist/{commonjs → cjs}/DataChannel/InitialSettings.js +0 -0
  147. /package/dist/{commonjs → cjs}/DataChannel/InitialSettings.js.map +0 -0
  148. /package/dist/{commonjs → cjs}/DataChannel/LatencyTestResults.js +0 -0
  149. /package/dist/{commonjs → cjs}/DataChannel/LatencyTestResults.js.map +0 -0
  150. /package/dist/{commonjs → cjs}/FreezeFrame/FreezeFrame.js +0 -0
  151. /package/dist/{commonjs → cjs}/FreezeFrame/FreezeFrame.js.map +0 -0
  152. /package/dist/{commonjs → cjs}/FreezeFrame/FreezeFrameController.js +0 -0
  153. /package/dist/{commonjs → cjs}/FreezeFrame/FreezeFrameController.js.map +0 -0
  154. /package/dist/{commonjs → cjs}/Inputs/GamepadTypes.js +0 -0
  155. /package/dist/{commonjs → cjs}/Inputs/GamepadTypes.js.map +0 -0
  156. /package/dist/{commonjs → cjs}/Inputs/IInputController.js +0 -0
  157. /package/dist/{commonjs → cjs}/Inputs/IInputController.js.map +0 -0
  158. /package/dist/{commonjs → cjs}/Inputs/InputClassesFactory.js +0 -0
  159. /package/dist/{commonjs → cjs}/Inputs/InputClassesFactory.js.map +0 -0
  160. /package/dist/{commonjs → cjs}/Inputs/KeyCodes.js +0 -0
  161. /package/dist/{commonjs → cjs}/Inputs/KeyCodes.js.map +0 -0
  162. /package/dist/{commonjs → cjs}/Inputs/KeyboardController.js +0 -0
  163. /package/dist/{commonjs → cjs}/Inputs/KeyboardController.js.map +0 -0
  164. /package/dist/{commonjs → cjs}/Inputs/MouseButtons.js +0 -0
  165. /package/dist/{commonjs → cjs}/Inputs/MouseButtons.js.map +0 -0
  166. /package/dist/{commonjs → cjs}/Inputs/MouseController.js +0 -0
  167. /package/dist/{commonjs → cjs}/Inputs/MouseController.js.map +0 -0
  168. /package/dist/{commonjs → cjs}/Inputs/MouseControllerHovering.js +0 -0
  169. /package/dist/{commonjs → cjs}/Inputs/MouseControllerHovering.js.map +0 -0
  170. /package/dist/{commonjs → cjs}/Inputs/MouseControllerLocked.js +0 -0
  171. /package/dist/{commonjs → cjs}/Inputs/MouseControllerLocked.js.map +0 -0
  172. /package/dist/{commonjs → cjs}/Inputs/SpecialKeyCodes.js +0 -0
  173. /package/dist/{commonjs → cjs}/Inputs/SpecialKeyCodes.js.map +0 -0
  174. /package/dist/{commonjs → cjs}/Inputs/TouchController.js +0 -0
  175. /package/dist/{commonjs → cjs}/Inputs/TouchController.js.map +0 -0
  176. /package/dist/{commonjs → cjs}/Inputs/TouchControllerFake.js +0 -0
  177. /package/dist/{commonjs → cjs}/Inputs/TouchControllerFake.js.map +0 -0
  178. /package/dist/{commonjs → cjs}/Inputs/XRGamepadController.js +0 -0
  179. /package/dist/{commonjs → cjs}/Inputs/XRGamepadController.js.map +0 -0
  180. /package/dist/{commonjs → cjs}/PeerConnectionController/CandidatePairStats.js +0 -0
  181. /package/dist/{commonjs → cjs}/PeerConnectionController/CandidatePairStats.js.map +0 -0
  182. /package/dist/{commonjs → cjs}/PeerConnectionController/CandidateStat.js +0 -0
  183. /package/dist/{commonjs → cjs}/PeerConnectionController/CandidateStat.js.map +0 -0
  184. /package/dist/{commonjs → cjs}/PeerConnectionController/CodecStats.js +0 -0
  185. /package/dist/{commonjs → cjs}/PeerConnectionController/CodecStats.js.map +0 -0
  186. /package/dist/{commonjs → cjs}/PeerConnectionController/DataChannelStats.js +0 -0
  187. /package/dist/{commonjs → cjs}/PeerConnectionController/DataChannelStats.js.map +0 -0
  188. /package/dist/{commonjs → cjs}/PeerConnectionController/InboundRTPStats.js +0 -0
  189. /package/dist/{commonjs → cjs}/PeerConnectionController/InboundTrackStats.js +0 -0
  190. /package/dist/{commonjs → cjs}/PeerConnectionController/InboundTrackStats.js.map +0 -0
  191. /package/dist/{commonjs → cjs}/PeerConnectionController/SessionStats.js +0 -0
  192. /package/dist/{commonjs → cjs}/PeerConnectionController/SessionStats.js.map +0 -0
  193. /package/dist/{commonjs → cjs}/PeerConnectionController/StreamStats.js +0 -0
  194. /package/dist/{commonjs → cjs}/PeerConnectionController/StreamStats.js.map +0 -0
  195. /package/dist/{commonjs → cjs}/UI/OnScreenKeyboard.js +0 -0
  196. /package/dist/{commonjs → cjs}/UI/OnScreenKeyboard.js.map +0 -0
  197. /package/dist/{commonjs → cjs}/UeInstanceMessage/ResponseController.js +0 -0
  198. /package/dist/{commonjs → cjs}/UeInstanceMessage/ResponseController.js.map +0 -0
  199. /package/dist/{commonjs → cjs}/UeInstanceMessage/SendMessageController.js +0 -0
  200. /package/dist/{commonjs → cjs}/UeInstanceMessage/SendMessageController.js.map +0 -0
  201. /package/dist/{commonjs → cjs}/UeInstanceMessage/StreamMessageController.js +0 -0
  202. /package/dist/{commonjs → cjs}/UeInstanceMessage/StreamMessageController.js.map +0 -0
  203. /package/dist/{commonjs → cjs}/UeInstanceMessage/ToStreamerMessagesController.js +0 -0
  204. /package/dist/{commonjs → cjs}/UeInstanceMessage/ToStreamerMessagesController.js.map +0 -0
  205. /package/dist/{commonjs → cjs}/Util/FileUtil.js +0 -0
  206. /package/dist/{commonjs → cjs}/Util/FileUtil.js.map +0 -0
  207. /package/dist/{commonjs → cjs}/Util/IURLSearchParams.js +0 -0
  208. /package/dist/{commonjs → cjs}/Util/IURLSearchParams.js.map +0 -0
  209. /package/dist/{commonjs → cjs}/Util/InputCoordTranslator.js +0 -0
  210. /package/dist/{commonjs → cjs}/Util/InputCoordTranslator.js.map +0 -0
  211. /package/dist/{commonjs → cjs}/Util/RTCUtils.js +0 -0
  212. /package/dist/{commonjs → cjs}/Util/RTCUtils.js.map +0 -0
  213. /package/dist/{commonjs → cjs}/VideoPlayer/StreamController.js +0 -0
  214. /package/dist/{commonjs → cjs}/VideoPlayer/StreamController.js.map +0 -0
  215. /package/dist/{commonjs → cjs}/VideoPlayer/VideoPlayer.js +0 -0
  216. /package/dist/{commonjs → cjs}/VideoPlayer/VideoPlayer.js.map +0 -0
  217. /package/dist/{commonjs → cjs}/WebXR/WebXRController.js +0 -0
  218. /package/dist/{commonjs → cjs}/WebXR/WebXRController.js.map +0 -0
@@ -0,0 +1,392 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { AggregatedStats } from './AggregatedStats';
4
+ import { CandidatePairStats } from './CandidatePairStats';
5
+
6
+ /**
7
+ * Represents either a:
8
+ * - synchronization source: https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpReceiver/getSynchronizationSources
9
+ * - contributing source: https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpReceiver/getContributingSources
10
+ * Which also (if browser supports it) may optionall contain fields for captureTimestamp + senderCaptureTimeOffset
11
+ * if the abs-capture-time RTP header extension is enabled (currently this only works in Chromium based browsers).
12
+ */
13
+ class RTCRtpCaptureSource {
14
+ timestamp: number;
15
+ captureTimestamp: number;
16
+ senderCaptureTimeOffset: number;
17
+ }
18
+
19
+ /**
20
+ * FrameTimingInfo is a Chromium-specific set of WebRTC stats useful for latency calculation. It is stored in WebRTC stats as `googTimingFrameInfo`.
21
+ * It is defined as an RTP header extension here: https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-timing/README.md
22
+ * It is defined in source code here: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/video/video_timing.cc;l=82;drc=8d399817282e3c12ed54eb23ec42a5e418298ec6
23
+ * It is discussed by its author here: https://github.com/w3c/webrtc-provisional-stats/issues/40#issuecomment-1272916692
24
+ * In summary it a comma-delimited string that contains the following (in this order):
25
+ * 1) RTP timestamp: the RTP timestamp of the frame
26
+ * 2) Capture time: timestamp when this frame was captured
27
+ * 3) Encode start: timestamp when this frame started to be encoded
28
+ * 4) Encode finish: timestamp when this frame finished encoding
29
+ * 5) Packetization finish: timestamp when this frame was split into packets and was ready to be sent over the network
30
+ * 6) Pacer exit: timestamp when last packet of this frame was sent over the network by the sender at this timestamp
31
+ * 7) Network timestamp1: place for the SFU to mark when the frame started being forwarded. Application specific.
32
+ * 8) Network timestamp2: place for the SFU to mark when the frame finished being forwarded. Application specific.
33
+ * 9) Receive start: timestamp when the first packet of this frame was received
34
+ * 10) Receive finish: timestamp when the last packet of this frame was received
35
+ * 11) Decode start: timestamp when the frame was passed to decoder
36
+ * 12) Decode finish: timestamp when the frame was decoded
37
+ * 13) Render time: timestamp of the projected render time for this frame
38
+ * 14) "is outlier": a flag for if this frame is bigger in encoded size than the average frame by at least 5x.
39
+ * 15) "triggered by timer": a flag for if this report was triggered by the timer (The report is sent every 200ms)
40
+ */
41
+ export class FrameTimingInfo {
42
+ rtpTimestamp: number;
43
+ captureTimestamp: number;
44
+ encodeStartTimestamp: number;
45
+ encodeFinishTimestamp: number;
46
+ packetizerFinishTimestamp: number;
47
+ pacerExitTimestamp: number;
48
+ networkTimestamp1: number;
49
+ networkTimestamp2: number;
50
+ receiveStart: number;
51
+ receiveFinish: number;
52
+ decodeStart: number;
53
+ decodeFinish: number;
54
+ renderTime: number;
55
+ isOutlier: boolean;
56
+ isTriggeredByTimer: boolean;
57
+
58
+ /* Milliseconds between encoder start and finish */
59
+ encoderLatencyMs: number;
60
+
61
+ /* Milliseconds between encode end and packetizer finish time */
62
+ packetizeLatencyMs: number;
63
+
64
+ /* Milliseconds between packetize finish time and pacer sending the frame */
65
+ pacerLatencyMs: number;
66
+
67
+ /* Milliseconds between capture time and pacer exit */
68
+ captureToSendLatencyMs: number;
69
+ }
70
+
71
+ /**
72
+ * Calculates a combination of latency statistics using purely WebRTC API.
73
+ */
74
+ export class LatencyCalculator {
75
+ /* Clock offset between peer clocks cannot always be calculated as it relies of latest sender reports.
76
+ * so we store the last time we had a valid clock offset in the assumption that clocks haven't drifted too much since then.
77
+ */
78
+ private latestSenderRecvClockOffset: number | null = null;
79
+
80
+ public calculate(stats: AggregatedStats, receivers: RTCRtpReceiver[]): LatencyInfo {
81
+ const latencyInfo = new LatencyInfo();
82
+
83
+ const rttMS: number | null = this.getRTTMs(stats);
84
+
85
+ if (rttMS != null) {
86
+ latencyInfo.rttMs = rttMS;
87
+
88
+ // Calculate sender latency using the first valid video ssrc/csrc
89
+ const captureSource: RTCRtpCaptureSource | null = this.getCaptureSource(receivers);
90
+ if (captureSource != null) {
91
+ const senderLatencyMs = this.calculateSenderLatency(stats, captureSource);
92
+ if (senderLatencyMs !== null) {
93
+ latencyInfo.senderLatencyMs = senderLatencyMs;
94
+ }
95
+ }
96
+ }
97
+
98
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay
99
+ if (
100
+ stats.inboundVideoStats.totalProcessingDelay !== undefined &&
101
+ stats.inboundVideoStats.framesDecoded !== undefined
102
+ ) {
103
+ latencyInfo.averageProcessingDelayMs =
104
+ (stats.inboundVideoStats.totalProcessingDelay / stats.inboundVideoStats.framesDecoded) * 1000;
105
+ }
106
+
107
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferminimumdelay
108
+ if (
109
+ stats.inboundVideoStats.jitterBufferDelay !== undefined &&
110
+ stats.inboundVideoStats.jitterBufferEmittedCount !== undefined
111
+ ) {
112
+ latencyInfo.averageJitterBufferDelayMs =
113
+ (stats.inboundVideoStats.jitterBufferDelay /
114
+ stats.inboundVideoStats.jitterBufferEmittedCount) *
115
+ 1000;
116
+ }
117
+
118
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime
119
+ if (
120
+ stats.inboundVideoStats.framesDecoded !== undefined &&
121
+ stats.inboundVideoStats.totalDecodeTime !== undefined
122
+ ) {
123
+ latencyInfo.averageDecodeLatencyMs =
124
+ (stats.inboundVideoStats.totalDecodeTime / stats.inboundVideoStats.framesDecoded) * 1000;
125
+ }
126
+
127
+ // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-framesassembledfrommultiplepackets
128
+ if (
129
+ stats.inboundVideoStats.totalAssemblyTime !== undefined &&
130
+ stats.inboundVideoStats.framesAssembledFromMultiplePackets !== undefined
131
+ ) {
132
+ latencyInfo.averageAssemblyDelayMs =
133
+ (stats.inboundVideoStats.totalAssemblyTime /
134
+ stats.inboundVideoStats.framesAssembledFromMultiplePackets) *
135
+ 1000;
136
+ }
137
+
138
+ // Extract extra Chrome-specific stats like encoding latency
139
+ if (
140
+ stats.inboundVideoStats.googTimingFrameInfo !== undefined &&
141
+ stats.inboundVideoStats.googTimingFrameInfo.length > 0
142
+ ) {
143
+ latencyInfo.frameTiming = this.extractFrameTimingInfo(
144
+ stats.inboundVideoStats.googTimingFrameInfo
145
+ );
146
+ }
147
+
148
+ // Calculate E2E latency using video-timing capture to send time + one way network latency + receiver-side latency
149
+ if (
150
+ latencyInfo.frameTiming !== undefined &&
151
+ latencyInfo.frameTiming.captureToSendLatencyMs !== undefined &&
152
+ latencyInfo.averageProcessingDelayMs !== undefined &&
153
+ latencyInfo.rttMs !== undefined
154
+ ) {
155
+ latencyInfo.averageE2ELatency =
156
+ latencyInfo.frameTiming.captureToSendLatencyMs +
157
+ latencyInfo.rttMs * 0.5 +
158
+ latencyInfo.averageProcessingDelayMs;
159
+ }
160
+
161
+ // Calculate E2E latency as abs-capture-time capture to send latency + one way network latency + receiver-side latency
162
+ if (
163
+ latencyInfo.senderLatencyMs != undefined &&
164
+ latencyInfo.averageProcessingDelayMs !== undefined &&
165
+ latencyInfo.rttMs !== undefined
166
+ ) {
167
+ latencyInfo.averageE2ELatency =
168
+ latencyInfo.senderLatencyMs + latencyInfo.rttMs * 0.5 + latencyInfo.averageProcessingDelayMs;
169
+ }
170
+
171
+ return latencyInfo;
172
+ }
173
+
174
+ private extractFrameTimingInfo(googTimingFrameInfo: string): FrameTimingInfo {
175
+ const timingInfo: FrameTimingInfo = new FrameTimingInfo();
176
+
177
+ const timingInfoArr: string[] = googTimingFrameInfo.split(',');
178
+
179
+ // Should have exactly 15 elements according to:
180
+ // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/video/video_timing.cc;l=82;drc=8d399817282e3c12ed54eb23ec42a5e418298ec6
181
+ if (timingInfoArr.length === 15) {
182
+ timingInfo.rtpTimestamp = Number.parseInt(timingInfoArr[0]);
183
+ timingInfo.captureTimestamp = Number.parseInt(timingInfoArr[1]);
184
+ timingInfo.encodeStartTimestamp = Number.parseInt(timingInfoArr[2]);
185
+ timingInfo.encodeFinishTimestamp = Number.parseInt(timingInfoArr[3]);
186
+ timingInfo.packetizerFinishTimestamp = Number.parseInt(timingInfoArr[4]);
187
+ timingInfo.pacerExitTimestamp = Number.parseInt(timingInfoArr[5]);
188
+ timingInfo.networkTimestamp1 = Number.parseInt(timingInfoArr[6]);
189
+ timingInfo.networkTimestamp2 = Number.parseInt(timingInfoArr[7]);
190
+ timingInfo.receiveStart = Number.parseInt(timingInfoArr[8]);
191
+ timingInfo.receiveFinish = Number.parseInt(timingInfoArr[9]);
192
+ timingInfo.decodeStart = Number.parseInt(timingInfoArr[10]);
193
+ timingInfo.decodeFinish = Number.parseInt(timingInfoArr[11]);
194
+ timingInfo.renderTime = Number.parseInt(timingInfoArr[12]);
195
+ timingInfo.isOutlier = Number.parseInt(timingInfoArr[13]) > 0;
196
+ timingInfo.isTriggeredByTimer = Number.parseInt(timingInfoArr[14]) > 0;
197
+
198
+ // Calculate some latency stats
199
+ timingInfo.encoderLatencyMs = timingInfo.encodeFinishTimestamp - timingInfo.encodeStartTimestamp;
200
+ timingInfo.packetizeLatencyMs =
201
+ timingInfo.packetizerFinishTimestamp - timingInfo.encodeFinishTimestamp;
202
+ timingInfo.pacerLatencyMs = timingInfo.pacerExitTimestamp - timingInfo.packetizerFinishTimestamp;
203
+ timingInfo.captureToSendLatencyMs = timingInfo.pacerExitTimestamp - timingInfo.captureTimestamp;
204
+ }
205
+
206
+ return timingInfo;
207
+ }
208
+
209
+ private calculateSenderLatency(
210
+ stats: AggregatedStats,
211
+ captureSource: RTCRtpCaptureSource
212
+ ): number | null {
213
+ // The calculation performed in this function is as per the procedure defined here:
214
+ // https://w3c.github.io/webrtc-extensions/#dom-rtcrtpcontributingsource-sendercapturetimeoffset
215
+
216
+ // Get the sender capture in the sender's clock
217
+ const senderCaptureTimestamp = captureSource.captureTimestamp + captureSource.senderCaptureTimeOffset;
218
+
219
+ let sendRecvClockOffset: number | null = this.calculateSenderReceiverClockOffset(stats);
220
+
221
+ // Use latest clock offset if we couldn't calculate one now
222
+ if (sendRecvClockOffset == null) {
223
+ if (this.latestSenderRecvClockOffset != null) {
224
+ sendRecvClockOffset = this.latestSenderRecvClockOffset;
225
+ } else {
226
+ return null;
227
+ }
228
+ } else {
229
+ this.latestSenderRecvClockOffset = sendRecvClockOffset;
230
+ }
231
+
232
+ // This brings sender clock roughly inline with recv clock
233
+ const recvCaptureTimestampNTP = senderCaptureTimestamp + sendRecvClockOffset;
234
+
235
+ // As defined in Chrome source: https://chromium.googlesource.com/external/webrtc/+/master/system_wrappers/include/clock.h#26
236
+ const ntp1970 = 2208988800000;
237
+
238
+ const recvCaptureTimestamp = recvCaptureTimestampNTP - ntp1970;
239
+
240
+ const senderLatency = captureSource.timestamp - recvCaptureTimestamp;
241
+
242
+ return senderLatency;
243
+ }
244
+
245
+ /**
246
+ * Find the first valid ssrc or csrc that has capture time fields present from abs-capture-time header extension.
247
+ * @param receivers The RTP receviers this peer connection has.
248
+ * @returns A single valid ssrc or csrc that has capture time fields or null if there is none (e.g. in non-chromium browsers it will be null).
249
+ */
250
+ private getCaptureSource(receivers: RTCRtpReceiver[]): RTCRtpCaptureSource | null {
251
+ // We only want video receivers
252
+ receivers = receivers.filter((receiver) => receiver.track.kind === 'video');
253
+
254
+ for (const receiver of receivers) {
255
+ // Go through all ssrc and csrc to check for capture timestamp
256
+ // Note: Conversion to `any` here is because TS does not have captureTimestamp etc defined in the types
257
+ // these fields only exist in Chromium currently.
258
+ const sources: any[] = receiver
259
+ .getSynchronizationSources()
260
+ .concat(receiver.getContributingSources());
261
+
262
+ for (const src of sources) {
263
+ if (
264
+ src.captureTimestamp !== undefined &&
265
+ src.senderCaptureTimeOffset !== undefined &&
266
+ src.timestamp !== undefined
267
+ ) {
268
+ const captureSrc = new RTCRtpCaptureSource();
269
+ captureSrc.timestamp = src.timestamp;
270
+ captureSrc.captureTimestamp = src.captureTimestamp;
271
+ captureSrc.senderCaptureTimeOffset = src.senderCaptureTimeOffset;
272
+ return captureSrc;
273
+ }
274
+ }
275
+ }
276
+
277
+ return null;
278
+ }
279
+
280
+ private calculateSenderReceiverClockOffset(stats: AggregatedStats): number | null {
281
+ // The calculation performed in this function is as per the procedure defined here:
282
+ // https://w3c.github.io/webrtc-extensions/#dom-rtcrtpcontributingsource-sendercapturetimeoffset
283
+
284
+ const hasRemoteOutboundVideoStats =
285
+ stats.remoteOutboundVideoStats !== undefined &&
286
+ stats.remoteOutboundVideoStats.timestamp !== undefined &&
287
+ stats.remoteOutboundVideoStats.remoteTimestamp !== undefined;
288
+
289
+ // Note: As of Chrome 132, remote-outbound-rtp stats for video are not yet implemented (audio works).
290
+ // This codepath should activate once they do begin to work.
291
+ if (!hasRemoteOutboundVideoStats) {
292
+ return null;
293
+ }
294
+
295
+ const remoteStatsArrivedTimestamp = stats.remoteOutboundVideoStats.timestamp;
296
+ const remoteStatsSentTimestamp = stats.remoteOutboundVideoStats.remoteTimestamp;
297
+
298
+ const rttMs: number | null = this.getRTTMs(stats);
299
+
300
+ if (
301
+ remoteStatsArrivedTimestamp !== undefined &&
302
+ remoteStatsSentTimestamp !== undefined &&
303
+ rttMs !== null
304
+ ) {
305
+ const onewayDelay = rttMs * 0.5;
306
+ return remoteStatsArrivedTimestamp - (remoteStatsSentTimestamp + onewayDelay);
307
+ }
308
+ // Could not get stats to calculate sender/receiver clock offset
309
+ else {
310
+ return null;
311
+ }
312
+ }
313
+
314
+ private getRTTMs(stats: AggregatedStats): number | null {
315
+ // Try to get it from the active candidate pair
316
+ const activeCandidatePair: CandidatePairStats | null = stats.getActiveCandidatePair();
317
+ if (!!activeCandidatePair && activeCandidatePair.currentRoundTripTime !== undefined) {
318
+ const curRTTSeconds = activeCandidatePair.currentRoundTripTime;
319
+ return curRTTSeconds * 1000;
320
+ }
321
+
322
+ // Next try to get it from remote-outbound-rtp video stats
323
+ if (
324
+ !!stats.remoteOutboundVideoStats &&
325
+ stats.remoteOutboundVideoStats.totalRoundTripTime !== undefined &&
326
+ stats.remoteOutboundVideoStats.roundTripTimeMeasurements !== undefined &&
327
+ stats.remoteOutboundVideoStats.roundTripTimeMeasurements > 0
328
+ ) {
329
+ const avgRttSeconds =
330
+ stats.remoteOutboundVideoStats.totalRoundTripTime /
331
+ stats.remoteOutboundVideoStats.roundTripTimeMeasurements;
332
+ return avgRttSeconds * 1000;
333
+ }
334
+
335
+ // Next try to get it from remote-outbound-rtp audio stats
336
+ if (
337
+ !!stats.remoteOutboundAudioStats &&
338
+ stats.remoteOutboundAudioStats.totalRoundTripTime !== undefined &&
339
+ stats.remoteOutboundAudioStats.roundTripTimeMeasurements !== undefined &&
340
+ stats.remoteOutboundAudioStats.roundTripTimeMeasurements > 0
341
+ ) {
342
+ const avgRttSeconds =
343
+ stats.remoteOutboundAudioStats.totalRoundTripTime /
344
+ stats.remoteOutboundAudioStats.roundTripTimeMeasurements;
345
+ return avgRttSeconds * 1000;
346
+ }
347
+
348
+ return null;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * A collection of latency information calculated using the WebRTC API.
354
+ * Most stats are calculated following the spec:
355
+ * https://w3c.github.io/webrtc-stats/#dictionary-rtcinboundrtpstreamstats-members
356
+ */
357
+ export class LatencyInfo {
358
+ /**
359
+ * The time taken from the moment a frame is done capturing to the moment it is sent over the network.
360
+ * Note: This can only be calculated if both offer and answer contain the
361
+ * the RTP header extension for `video-timing` (Chrome only for now)
362
+ */
363
+ public senderLatencyMs: number | undefined = undefined;
364
+
365
+ /**
366
+ * The time taken from the moment a frame is done capturing to the moment it is sent over the network.
367
+ * Note: This can only be calculated if both offer and answer contain the
368
+ * the RTP header extension for `abs-capture-time` (Chrome only for now)
369
+ */
370
+ public senderLatencyAbsCaptureTimeMs: number | undefined = undefined;
371
+
372
+ /* The round trip time (milliseconds) between each sender->receiver->sender */
373
+ public rttMs: number | undefined = undefined;
374
+
375
+ /* Average time taken (milliseconds) from video packet receipt to post-decode. */
376
+ public averageProcessingDelayMs: number | undefined = undefined;
377
+
378
+ /* Average time taken (milliseconds) inside the jitter buffer (which is post-receipt but pre-decode). */
379
+ public averageJitterBufferDelayMs: number | undefined = undefined;
380
+
381
+ /* Average time taken (milliseconds) to decode a video frame. */
382
+ public averageDecodeLatencyMs: number | undefined = undefined;
383
+
384
+ /* Average time taken (milliseconds) to between receipt of the first and last video packet of a. */
385
+ public averageAssemblyDelayMs: number | undefined = undefined;
386
+
387
+ /* The sender latency + RTT/2 + processing delay */
388
+ public averageE2ELatency: number | undefined = undefined;
389
+
390
+ /* Timing information about the worst performing frame since the last getStats call (only works on Chrome) */
391
+ public frameTiming: FrameTimingInfo | undefined = undefined;
392
+ }
@@ -1,26 +1,60 @@
1
1
  // Copyright Epic Games, Inc. All Rights Reserved.
2
2
 
3
3
  /**
4
- * Outbound Video Stats collected from the RTC Stats Report
4
+ * Outbound RTP stats collected from the RTC Stats Report under `outbound-rtp`.
5
+ * Wrapper around: https://developer.mozilla.org/en-US/docs/Web/API/RTCOutboundRtpStreamStats
6
+ * These are stats for video we are sending to a remote peer.
5
7
  */
6
- export class OutBoundVideoStats {
8
+ export class OutboundRTPStats {
9
+ active: boolean | undefined;
10
+ codecId: string | undefined;
7
11
  bytesSent: number;
12
+ frameHeight: number | undefined;
13
+ frameWidth: number | undefined;
14
+ framesEncoded: number | undefined;
15
+ framesPerSecond: number | undefined;
16
+ framesSent: number | undefined;
17
+ headerBytesSent: number;
8
18
  id: string;
9
- localId: string;
19
+ keyFramesEncoded: number | undefined;
20
+ kind: string;
21
+ mediaSourceId: string | undefined;
22
+ mid: string | undefined;
23
+ nackCount: number | undefined;
10
24
  packetsSent: number;
11
- remoteTimestamp: number;
25
+ qpSum: number | undefined;
26
+ qualityLimitationDurations: number | undefined;
27
+ qualityLimitationReason: string | undefined;
28
+ remoteId: string | undefined;
29
+ retransmittedBytesSent: number;
30
+ rid: string | undefined;
31
+ scalabilityMode: string | undefined;
32
+ ssrc: string;
33
+ targetBitrate: number | undefined;
12
34
  timestamp: number;
35
+ totalEncodeTime: number | undefined;
36
+ totalEncodeBytesTarget: number | undefined;
37
+ totalPacketSendDelay: number | undefined;
38
+ transportId: string | undefined;
13
39
  }
14
40
 
15
41
  /**
16
- * Outbound Stats collected from the RTC Stats Report
42
+ * Remote outbound stats collected from the RTC Stats Report under `remote-outbound-rtp`.
43
+ * Wrapper around: https://developer.mozilla.org/en-US/docs/Web/API/RTCRemoteOutboundRtpStreamStats
44
+ * These are stats for media we are receiving from a remote peer.
17
45
  */
18
- export class OutBoundRTPStats {
46
+ export class RemoteOutboundRTPStats {
47
+ bytesSent: number | undefined;
48
+ codecId: string;
49
+ id: string | undefined;
19
50
  kind: string;
20
- bytesSent: number;
21
- id: string;
22
- localId: string;
23
- packetsSent: number;
24
- remoteTimestamp: number;
25
- timestamp: number;
51
+ localId: string | undefined;
52
+ packetsSent: number | undefined;
53
+ remoteTimestamp: number | undefined;
54
+ reportsSent: number | undefined;
55
+ roundTripTimeMeasurements: number | undefined;
56
+ ssrc: string;
57
+ timestamp: number | undefined;
58
+ totalRoundTripTime: number | undefined;
59
+ transportId: string | undefined;
26
60
  }
@@ -6,6 +6,10 @@ import { AggregatedStats } from './AggregatedStats';
6
6
  import { parseRtpParameters, splitSections } from 'sdp';
7
7
  import { RTCUtils } from '../Util/RTCUtils';
8
8
  import { CodecStats } from './CodecStats';
9
+ import { SDPUtils } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
10
+ import { LatencyCalculator, LatencyInfo } from './LatencyCalculator';
11
+
12
+ export const kAbsCaptureTime = 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time';
9
13
 
10
14
  /**
11
15
  * Handles the Peer Connection
@@ -18,6 +22,7 @@ export class PeerConnectionController {
18
22
  updateCodecSelection: boolean;
19
23
  videoTrack: MediaStreamTrack;
20
24
  audioTrack: MediaStreamTrack;
25
+ latencyCalculator: LatencyCalculator;
21
26
 
22
27
  /**
23
28
  * Create a new RTC Peer Connection client
@@ -27,6 +32,7 @@ export class PeerConnectionController {
27
32
  constructor(options: RTCConfiguration, config: Config, preferredCodec: string) {
28
33
  this.config = config;
29
34
  this.createPeerConnection(options, preferredCodec);
35
+ this.latencyCalculator = new LatencyCalculator();
30
36
  }
31
37
 
32
38
  createPeerConnection(options: RTCConfiguration, preferredCodec: string) {
@@ -88,12 +94,26 @@ export class PeerConnectionController {
88
94
  }
89
95
 
90
96
  /**
91
- *
97
+ * Receive offer from UE side and process it as the remote description of this peer connection
92
98
  */
93
99
  async receiveOffer(offer: RTCSessionDescriptionInit, config: Config) {
94
100
  Logger.Info('Receive Offer');
95
101
 
102
+ // If UE or JSStreamer did send abs-capture-time RTP header extension to a non-Chrome browser
103
+ // then remove it from the SDP because if Firefox detects it in offer or answer it will fail to connect
104
+ // due having 15 or more header extensions: https://mailarchive.ietf.org/arch/msg/rtcweb/QRnWNuWzGuLRovWdHkodNP6VOgg/
105
+ if (this.isFirefox()) {
106
+ // example: a=extmap:15 http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time
107
+ offer.sdp = offer.sdp.replace(
108
+ /^a=extmap:\d+ http:\/\/www\.webrtc\.org\/experiments\/rtp-hdrext\/abs-capture-time\r\n/gm,
109
+ ''
110
+ );
111
+ }
112
+
96
113
  this.peerConnection?.setRemoteDescription(offer).then(() => {
114
+ // Fire event for when remote offer description is set
115
+ this.onSetRemoteDescription(offer);
116
+
97
117
  const isLocalhostConnection =
98
118
  location.hostname === 'localhost' || location.hostname === '127.0.0.1';
99
119
  const isHttpsConnection = location.protocol === 'https:';
@@ -124,10 +144,10 @@ export class PeerConnectionController {
124
144
  return this.peerConnection?.setLocalDescription(Answer);
125
145
  })
126
146
  .then(() => {
127
- this.onSendWebRTCAnswer(this.peerConnection?.currentLocalDescription);
147
+ this.onSetLocalDescription(this.peerConnection?.currentLocalDescription);
128
148
  })
129
- .catch(() => {
130
- Logger.Error('createAnswer() failed');
149
+ .catch((err) => {
150
+ Logger.Error(`createAnswer() failed - ${err}`);
131
151
  });
132
152
  });
133
153
  });
@@ -151,25 +171,29 @@ export class PeerConnectionController {
151
171
  * Generate Aggregated Stats and then fire a onVideo Stats event
152
172
  */
153
173
  generateStats() {
154
- const audioPromise = this.audioTrack
155
- ? this.peerConnection?.getStats(this.audioTrack).then((statsData: RTCStatsReport) => {
156
- this.aggregatedStats.processStats(statsData);
157
- })
158
- : Promise.resolve();
159
- const videoPromise = this.videoTrack
160
- ? this.peerConnection?.getStats(this.videoTrack).then((statsData: RTCStatsReport) => {
161
- this.aggregatedStats.processStats(statsData);
162
- })
163
- : Promise.resolve();
164
-
165
- Promise.allSettled([audioPromise, videoPromise]).then(() => {
174
+ this.peerConnection.getStats().then((statsData: RTCStatsReport) => {
175
+ this.aggregatedStats.processStats(statsData);
176
+
166
177
  this.onVideoStats(this.aggregatedStats);
178
+
179
+ // Calculate latency using stats and video receivers and then call the handling function
180
+ const latencyInfo: LatencyInfo = this.latencyCalculator.calculate(
181
+ this.aggregatedStats,
182
+ this.peerConnection.getReceivers()
183
+ );
184
+ this.onLatencyCalculated(latencyInfo);
185
+
167
186
  // Update the preferred codec selection based on what was actually negotiated
168
187
  if (this.updateCodecSelection && !!this.aggregatedStats.inboundVideoStats.codecId) {
169
188
  // Construct the qualified codec name from the mimetype and fmtp
170
- const codecStats: CodecStats = this.aggregatedStats.codecs.get(
189
+ const codecStats: CodecStats | undefined = this.aggregatedStats.codecs.get(
171
190
  this.aggregatedStats.inboundVideoStats.codecId
172
191
  );
192
+
193
+ if (codecStats === undefined) {
194
+ return;
195
+ }
196
+
173
197
  const codecShortname = codecStats.mimeType.replace('video/', '');
174
198
  let fullCodecName = codecShortname;
175
199
  if (codecStats.sdpFmtpLine && codecStats.sdpFmtpLine.trim() !== '') {
@@ -237,9 +261,20 @@ export class PeerConnectionController {
237
261
  // We use the line 'useinbandfec=1' (which Opus uses) to set our Opus specific audio parameters.
238
262
  mungedSDP = mungedSDP.replace('useinbandfec=1', audioSDP);
239
263
 
264
+ // Add abs-capture-time RTP header extension if we have enabled the setting.
265
+ // Note: As at Feb 2025, Chromium based browsers are the only ones that support this and
266
+ // munging it into the answer in Firefox will cause the connection to fail.
267
+ if (this.config.isFlagEnabled(Flags.EnableCaptureTimeExt) && !this.isFirefox()) {
268
+ mungedSDP = SDPUtils.addVideoHeaderExtensionToSdp(mungedSDP, kAbsCaptureTime);
269
+ }
270
+
240
271
  return mungedSDP;
241
272
  }
242
273
 
274
+ isFirefox(): boolean {
275
+ return navigator.userAgent.indexOf('Firefox') > 0;
276
+ }
277
+
243
278
  /**
244
279
  * When a Ice Candidate is received add to the RTC Peer Connection
245
280
  * @param iceCandidate - RTC Ice Candidate from the Signaling Server
@@ -586,6 +621,15 @@ export class PeerConnectionController {
586
621
  // Default Functionality: Do Nothing
587
622
  }
588
623
 
624
+ /**
625
+ * And override event for when latency info is calculated
626
+ * @param latencyInfo - Calculated latency information.
627
+ */
628
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
629
+ onLatencyCalculated(latencyInfo: LatencyInfo) {
630
+ // Default Functionality: Do Nothing
631
+ }
632
+
589
633
  /**
590
634
  * Event to send the RTC offer to the Signaling server
591
635
  * @param offer - RTC Offer
@@ -596,11 +640,20 @@ export class PeerConnectionController {
596
640
  }
597
641
 
598
642
  /**
599
- * Event to send the RTC Answer to the Signaling server
643
+ * Event fired when remote offer description is set.
644
+ * @param offer - RTC Offer
645
+ */
646
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
647
+ onSetRemoteDescription(offer: RTCSessionDescriptionInit) {
648
+ // Default Functionality: Do Nothing
649
+ }
650
+
651
+ /**
652
+ * Event fire when local description answer is set.
600
653
  * @param answer - RTC Answer
601
654
  */
602
655
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
603
- onSendWebRTCAnswer(answer: RTCSessionDescriptionInit) {
656
+ onSetLocalDescription(answer: RTCSessionDescriptionInit) {
604
657
  // Default Functionality: Do Nothing
605
658
  }
606
659