@rtcstats/rtcstats-shared 2.0.1 → 2.1.0
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/address-obfuscator.js +15 -1
- package/compression.js +7 -0
- package/dump.js +70 -1
- package/index.d.ts +27 -5
- package/package.json +1 -4
- package/timeseries.js +1 -1
- package/utils.js +14 -0
package/address-obfuscator.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Obfuscate ip addresses which should not be stored long-term.
|
|
2
|
+
import {compressStatsProperty} from './compression.js';
|
|
3
|
+
|
|
2
4
|
import SDPUtils from 'sdp';
|
|
3
5
|
|
|
4
6
|
// Obfuscate an IP address, keeping address family intact.
|
|
5
7
|
// Based on libWebRTCs IPAddress::ToSensitiveString
|
|
6
8
|
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/rtc_base/ip_address.cc;l=149;drc=0dc30698370bcde67dda4f48b7ca19bf4c1dbc17
|
|
7
|
-
function obfuscateIpOrAddress(ipOrAddress) {
|
|
9
|
+
export function obfuscateIpOrAddress(ipOrAddress) {
|
|
8
10
|
if (['::1', '127.0.0.1'].includes(ipOrAddress)) {
|
|
9
11
|
return ipOrAddress;
|
|
10
12
|
}
|
|
@@ -50,6 +52,10 @@ function obfuscateSDP(sdp) {
|
|
|
50
52
|
}).join('\r\n').trim() + '\r\n';
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
const compressedAddressProperty = compressStatsProperty('address');
|
|
56
|
+
const compressedRelatedAddressProperty = compressStatsProperty('relatedAddress');
|
|
57
|
+
const compressedCandidateTypeProperty = compressStatsProperty('candidateType');
|
|
58
|
+
const compressedIpProperty = compressStatsProperty('ip');
|
|
53
59
|
function obfuscateStats(stats) {
|
|
54
60
|
Object.keys(stats).forEach((id) => {
|
|
55
61
|
const report = stats[id];
|
|
@@ -60,9 +66,17 @@ function obfuscateStats(stats) {
|
|
|
60
66
|
report.address = obfuscateIpOrAddress(report.address);
|
|
61
67
|
delete report.ip;
|
|
62
68
|
}
|
|
69
|
+
if (report[compressedAddressProperty] && report[compressedCandidateTypeProperty] !== 'relay') {
|
|
70
|
+
report[compressedAddressProperty] = obfuscateIpOrAddress(report[compressedAddressProperty]);
|
|
71
|
+
delete report.ip;
|
|
72
|
+
delete report[compressedIpProperty];
|
|
73
|
+
}
|
|
63
74
|
if (report.relatedAddress) {
|
|
64
75
|
report.relatedAddress = obfuscateIpOrAddress(report.relatedAddress);
|
|
65
76
|
}
|
|
77
|
+
if (report[compressedRelatedAddressProperty]) {
|
|
78
|
+
report[compressedRelatedAddressProperty] = obfuscateIpOrAddress(report[compressedRelatedAddressProperty]);
|
|
79
|
+
}
|
|
66
80
|
});
|
|
67
81
|
}
|
|
68
82
|
|
package/compression.js
CHANGED
|
@@ -736,3 +736,10 @@ export function decompressStatsProperty(property) {
|
|
|
736
736
|
return reverseStatsPropertyTable[property] || property;
|
|
737
737
|
}
|
|
738
738
|
|
|
739
|
+
// Mapping from compute pressure names to numerical values.
|
|
740
|
+
export const computePressureTable = {
|
|
741
|
+
nominal: 4,
|
|
742
|
+
fair: 3,
|
|
743
|
+
serious: 2,
|
|
744
|
+
critical: 1,
|
|
745
|
+
};
|
package/dump.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
descriptionDecompression,
|
|
4
4
|
statsDecompression,
|
|
5
5
|
} from './compression.js';
|
|
6
|
+
import {parseTrackWithStreams} from './utils.js';
|
|
6
7
|
|
|
7
8
|
export async function detectRTCStatsDump(blob) {
|
|
8
9
|
const magic = await blob.slice(0, 13).text();
|
|
@@ -62,7 +63,9 @@ export async function readRTCStatsDump(blob) {
|
|
|
62
63
|
if (method === 'getStats') { // delta-compressed stats
|
|
63
64
|
value = statsDecompression(baseStats[connection_id], value);
|
|
64
65
|
baseStats[connection_id] = JSON.parse(JSON.stringify(value));
|
|
65
|
-
} else if (method === 'setLocalDescription' && value
|
|
66
|
+
} else if (method === 'setLocalDescription' && value &&
|
|
67
|
+
// Workaround for Chrome <145 bug: https://source.chromium.org/chromium/chromium/src/+/4a2a384e43ea163127219548087e949fb00fa562
|
|
68
|
+
connection_id !== 'undefined-undefined') {
|
|
66
69
|
// Implicit SLD has no value to decompress.
|
|
67
70
|
let createCall;
|
|
68
71
|
for (let previousIndex = data.peerConnections[connection_id].length - 1; previousIndex >= 0; previousIndex--) {
|
|
@@ -118,3 +121,69 @@ export async function readWebRTCInternalsDump(blob) {
|
|
|
118
121
|
const textBlob = await blob.text();
|
|
119
122
|
return JSON.parse(textBlob);
|
|
120
123
|
}
|
|
124
|
+
|
|
125
|
+
export async function extractTracks(peerConnectionTrace) {
|
|
126
|
+
const tracks = [];
|
|
127
|
+
for (const traceEvent of peerConnectionTrace) {
|
|
128
|
+
if (traceEvent.type === 'addTrack') {
|
|
129
|
+
const trackInformation = parseTrackWithStreams(traceEvent.value);
|
|
130
|
+
trackInformation.startTime = traceEvent.timestamp;
|
|
131
|
+
trackInformation.direction = 'outbound';
|
|
132
|
+
tracks.push(trackInformation);
|
|
133
|
+
} else if (traceEvent.type === 'ontrack') {
|
|
134
|
+
const trackInformation = parseTrackWithStreams(traceEvent.value);
|
|
135
|
+
trackInformation.startTime = traceEvent.timestamp;
|
|
136
|
+
trackInformation.direction = 'inbound';
|
|
137
|
+
tracks.push(trackInformation);
|
|
138
|
+
} else if (traceEvent.type === 'addTransceiver') {
|
|
139
|
+
if (typeof traceEvent.value[0] === 'string') {
|
|
140
|
+
// TODO: if we pass a kind here, how do we determine the track id?
|
|
141
|
+
// libWebRTC seems to generate a random one for the SDP.
|
|
142
|
+
} else {
|
|
143
|
+
const trackInformation = parseTrackWithStreams(traceEvent.value[0]);
|
|
144
|
+
if (traceEvent.value[1]?.streams) {
|
|
145
|
+
trackInformation.streams = traceEvent.value[1].streams;
|
|
146
|
+
}
|
|
147
|
+
trackInformation.startTime = traceEvent.timestamp;
|
|
148
|
+
trackInformation.direction = 'outbound';
|
|
149
|
+
tracks.push(trackInformation);
|
|
150
|
+
}
|
|
151
|
+
} else if (traceEvent.type === 'replaceTrack') {
|
|
152
|
+
// This handles tracks that are added with addTransceiver(kind) and replaceTrack.
|
|
153
|
+
const [_, newTrack] = traceEvent.value;
|
|
154
|
+
if (newTrack) {
|
|
155
|
+
const trackInformation = parseTrackWithStreams(newTrack);
|
|
156
|
+
trackInformation.startTime = traceEvent.timestamp;
|
|
157
|
+
trackInformation.direction = 'outbound';
|
|
158
|
+
if (tracks.find(info => info.id === trackInformation.id) === undefined) {
|
|
159
|
+
tracks.push(trackInformation);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} else if (traceEvent.type === 'getStats') {
|
|
163
|
+
const report = traceEvent.value;
|
|
164
|
+
Object.keys(report).forEach(id => {
|
|
165
|
+
const stats = report[id];
|
|
166
|
+
if (!['inbound-rtp', 'outbound-rtp' /* TODO: media-source */].includes(stats.type)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const associatedTrack = tracks.find(trackInformation => {
|
|
170
|
+
// Tracks remain associated when replaceTrack is used.
|
|
171
|
+
if (trackInformation.statsId !== undefined) {
|
|
172
|
+
return trackInformation.statsId === id;
|
|
173
|
+
}
|
|
174
|
+
if (stats.type === 'inbound-rtp') {
|
|
175
|
+
return trackInformation.id === stats.trackIdentifier;
|
|
176
|
+
}
|
|
177
|
+
return trackInformation.id === (stats.mediaSourceId &&
|
|
178
|
+
report[stats.mediaSourceId] &&
|
|
179
|
+
report[stats.mediaSourceId].trackIdentifier);
|
|
180
|
+
});
|
|
181
|
+
if (!associatedTrack) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
associatedTrack.statsId = id;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return tracks;
|
|
189
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
export async function
|
|
4
|
-
export async function
|
|
5
|
-
export async function
|
|
1
|
+
// Type definitions for @rtcstats/rtcstats-shared
|
|
2
|
+
|
|
3
|
+
export async function detectWebRTCInternalsDump(blob: Blob): Promise<boolean>;
|
|
4
|
+
export async function readWebRTCInternalsDump(blob: Blob): Promise<object>;
|
|
5
|
+
export async function detectRTCStatsDump(blob: Blob): Promise<boolean>;
|
|
6
|
+
export async function readRTCStatsDump(blob: Blob): Promise<object>;
|
|
7
|
+
export async function extractTracks(peerConnectionTrace: any[]): Promise<any[]>;
|
|
8
|
+
|
|
9
|
+
export function statsCompression(baseStatsInput: object | RTCStatsReport, newStatsInput: object | RTCStatsReport, statsIdMap: object): object;
|
|
10
|
+
export function statsDecompression(baseStatsInput: object, delta: object): object;
|
|
11
|
+
export function descriptionCompression(baseDescription: RTCSessionDescription, newDescription: RTCSessionDescription): RTCSessionDescriptionInit;
|
|
12
|
+
export function descriptionDecompression(baseDescription: RTCSessionDescription, newDescription: RTCSessionDescription): RTCSessionDescriptionInit;
|
|
13
|
+
export function compressMethod(method: string): string | number;
|
|
14
|
+
export function decompressMethod(methodKey: string | number): string;
|
|
15
|
+
export function compressStatsType(type: string): string | number;
|
|
16
|
+
export function decompressStatsType(typeKey: string | number): string;
|
|
17
|
+
export function compressStatsProperty(property: string): string | number;
|
|
18
|
+
export function decompressStatsProperty(property: string | number): string;
|
|
19
|
+
|
|
20
|
+
export function createInternalsTimeSeries(connection: any): object;
|
|
21
|
+
export function createRtcStatsTimeSeries(trace: any[]): object;
|
|
22
|
+
export function insertNullForGapsIntoTimeSeries(timeSeries: any[], gapSizeMs?: number): any[];
|
|
23
|
+
|
|
24
|
+
export function map2obj(m: Map<any, any> | object): object;
|
|
25
|
+
export function dumpTrackWithStreams(track: MediaStreamTrack, ...streams: MediaStream[]): string[];
|
|
26
|
+
export function parseTrackWithStreams(serialized: any[]): object;
|
|
27
|
+
export function copyAndSanitizeConfig(config: RTCConfiguration): object;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtcstats/rtcstats-shared",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"description": "gather WebRTC API traces and statistics",
|
|
6
6
|
"type": "module",
|
|
@@ -17,8 +17,5 @@
|
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"sdp": "^3.2.1"
|
|
23
20
|
}
|
|
24
21
|
}
|
package/timeseries.js
CHANGED
|
@@ -42,7 +42,7 @@ export function createInternalsTimeSeries(connection) {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Creates a timeseries from rtcstats.
|
|
45
|
-
* Returns an object with stats by id and an object of property=> [[ts, value], ...].
|
|
45
|
+
* Returns an object with stats by id and an object of property => [[ts, value], ...].
|
|
46
46
|
*/
|
|
47
47
|
export function createRtcStatsTimeSeries(trace) {
|
|
48
48
|
const series = {};
|
package/utils.js
CHANGED
|
@@ -28,6 +28,20 @@ export function dumpTrackWithStreams(track, ...streams) {
|
|
|
28
28
|
return [track.kind, track.id, track.label, ...streams.map(s => s.id)];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Parses the serialized track and returns an object (which is not a track).
|
|
33
|
+
*
|
|
34
|
+
* @returns {Object} - representation of the track with streams.
|
|
35
|
+
*/
|
|
36
|
+
export function parseTrackWithStreams(serialized) {
|
|
37
|
+
return {
|
|
38
|
+
kind: serialized[0],
|
|
39
|
+
id: serialized[1],
|
|
40
|
+
label: serialized[2],
|
|
41
|
+
streams: serialized.slice(3),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
/**
|
|
32
46
|
* Sanitizes a RTCConfiguration by hiding the TURN server credentials and
|
|
33
47
|
* making the certificates serializable.
|