-
Notifications
You must be signed in to change notification settings - Fork 44
/
utils.js
138 lines (127 loc) · 4.66 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* feature extraction utils */
function capitalize(str) {
return str[0].toUpperCase() + str.substr(1);
}
// determine mode (most common) element in a series.
function mode(series) {
const modes = {};
series.forEach(item => {
if (!modes[item]) modes[item] = 0;
modes[item]++;
});
let value = -1;
let max = -1;
Object.keys(modes).forEach(key => {
if (modes[key] > max) {
max = modes[key];
value = key;
}
});
return value;
}
// Calculate standardized moment.
// order=1: 0
// order=2: variance
// order=3: skewness
// order=4: kurtosis
function standardizedMoment(series, order) {
const len = series.length || 1;
const mean = series.reduce((a, b) => a + b, 0) / len;
return series.reduce((a, b) => a + Math.pow(b - mean, order), 0) / len;
}
// extracts stream id, track id and kind from the format used in addTrack/ontrack
function extractFromTrackFormat(value) {
const [kind, trackId] = value.split(' ')[0].split(':');
const streamId = value.split(' ')[1].split(':')[1];
return {kind, trackId, streamId};
}
// extracts stream id, track id and kind from the format used in legacy addStream/onaddstream
function extractFromStreamFormat(value) {
const [streamId, trackList] = value.split(' ');
const tracks = [];
trackList.split(',').forEach(id => {
const [kind, trackId] = id.split(':');
tracks.push({kind, trackId});
});
return {streamId, tracks};
}
// extracts a Map with all local and remote audio/video tracks.
function extractTracks(peerConnectionLog) {
const tracks = new Map();
for (let i = 0; i < peerConnectionLog.length; i++) {
const {type, value} = peerConnectionLog[i];
if (type === 'addStream' || type === 'onaddstream') {
const {streamId, tracks: listOfTracks} = extractFromStreamFormat(value);
const direction = type === 'addStream' ? 'send' : 'recv';
listOfTracks.forEach(({kind, trackId}) => {
tracks.set(direction + ':' + trackId, {kind, streamId, trackId, direction, stats: []});
});
} else if (type === 'addTrack' || type === 'ontrack') {
const direction = type === 'addTrack' ? 'send' : 'recv';
const {kind, trackId, streamId} = extractFromTrackFormat(value);
tracks.set(direction + ':' + trackId, {kind, streamId, trackId, direction, stats: []});
} else if (type === 'getStats') {
Object.keys(value).forEach(id => {
const report = value[id];
if (report.type === 'ssrc') {
const {trackIdentifier} = report;
const direction = id.endsWith('_recv') ? 'recv' : 'send';
const key = direction + ':' + trackIdentifier;
if (tracks.has(key)) {
if (!report.timestamp) {
report.timestamp = peerConnectionLog[i].time;
} else {
report.timestamp = new Date(report.timestamp);
}
const currentStats = tracks.get(key).stats;
const lastStat = currentStats[currentStats.length - 1];
if (!lastStat || (report.timestamp.getTime() - lastStat.timestamp.getTime() > 0)) {
tracks.get(key).stats.push(report);
}
} else if (trackIdentifier !== undefined) {
console.log('NO ONTRACK FOR', trackIdentifier, report.ssrc);
}
}
});
}
}
return tracks;
}
function timeBetween(logs, startEvents, endEvents) {
let first;
for (let i = 0; i < logs.length; i++) {
const log = logs[i];
if (startEvents.includes(log.type)) {
first = log;
} else if (endEvents.includes(log.type)) {
if (first) {
return log.timestamp - first.timestamp;
} else {
return -1;
}
}
}
}
function extractStreams(tracks) {
const streams = new Map();
for (const [trackId, {streamId}] of tracks.entries()) {
if (streams.has(streamId)) {
streams.get(streamId).push(tracks.get(trackId));
} else {
streams.set(streamId, [tracks.get(trackId)]);
}
}
return streams;
}
function isIceConnected({type, value}) {
return type === 'oniceconnectionstatechange' && ['connected', 'completed'].includes(value);
}
module.exports = {
capitalize,
extractTracks,
extractStreams,
isIceConnected,
mode,
standardizedMoment,
timeBetween,
}