├── .prettierignore
├── CODEOWNERS
├── .prettierrc
├── test
├── index.html
├── mock-sdp.json
├── mock-spec-stats-recvonly.json
├── mock-spec-stats-3.json
├── mock-spec-stats-initial.json
├── mock-spec-stats-1.json
├── mock-spec-stats-2.json
└── test.ts
├── eslint.config.mjs
├── .gitignore
├── .editorconfig
├── README.md
├── jest.config.js
├── LICENSE.md
├── .vscode
└── launch.json
├── package.json
├── CHANGELOG.md
├── src
├── interfaces.ts
└── index.ts
└── tsconfig.json
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.json
3 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | @zservies @maxwellmooney13 @hjon @patrickfeeney03
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 120
4 | }
5 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Hello
7 |
8 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from '@eslint/js';
4 | import tseslint from 'typescript-eslint';
5 |
6 | export default tseslint.config(
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended,
9 | {
10 | rules: {
11 | 'no-prototype-builtins': 'off',
12 | '@typescript-eslint/no-var-requires': 'warn'
13 | }
14 | }
15 | );
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | bower.json.ember-try
12 |
13 | # misc
14 | /.sass-cache
15 | /connect.lock
16 | /coverage/*
17 | /libpeerconnection.log
18 | npm-debug.log
19 | testem.log
20 | .idea
21 |
22 | /test-results
23 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | insert_final_newline = false
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.css]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.html]
30 | indent_style = space
31 | indent_size = 2
32 |
33 | [*.{diff,md}]
34 | trim_trailing_whitespace = false
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebRTC Stats Gatherer
2 |
3 | This module is designed to collect [RTCPeerConnection](https://github.com/otalk/rtcpeerconnection) stats on a regular interval
4 | and emit stats and trace data as appropriate.
5 |
6 | Note that this project makes use of event emitting capabilities of [RTCPeerConnection](https://github.com/otalk/rtcpeerconnection) as opposed to a raw browser RTCPeerConnection.
7 |
8 | ## API
9 |
10 | `constructor(peerConnection: RTCPeerConnection, opts: StatsGathererOpts)`
11 |
12 | ```
13 | interface StatsGathererOpts {
14 | session?: string; // sessionId
15 | initiator?: string;
16 | conference?: string; // conversationId
17 | interval?: number; // interval, in seconds, at which stats are polled (default to 5)
18 | logger?: any; // defaults to console
19 | }
20 | ```
21 |
22 | ## Usage
23 | ```
24 | import StatsGatherer from 'webrtc-stats-gatherer';
25 |
26 | const gatherer = new StatsGatherer(myPeerConnection);
27 | gatherer.on('stats', (statsEvent) => doSomethingWithStats(statsEvent));
28 | ```
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'jsdom',
3 | roots: [
4 | '/src',
5 | '/test'
6 | ],
7 | testMatch: [
8 | '/test/**/*.(ts)'
9 | ],
10 | transform: {
11 | '^.+\\.tsx?$': [
12 | 'ts-jest',
13 | {
14 | tsconfig: {
15 | target: 'es2020',
16 | moduleResolution: 'node',
17 | resolveJsonModule: true,
18 | },
19 | },
20 | ],
21 | },
22 | collectCoverage: true,
23 | collectCoverageFrom: [
24 | '/src/**/*.{ts,tsx}',
25 | '!**/node_modules/**',
26 | '!**/types/**'
27 | ],
28 | coverageReporters: [
29 | 'lcov', 'text', 'text-summary'
30 | ],
31 | coverageDirectory: './coverage',
32 | coverageThreshold: {
33 | global: {
34 | functions: 100,
35 | lines: 100,
36 | statements: 100
37 | }
38 | },
39 | preset: 'ts-jest',
40 | reporters: [
41 | 'default',
42 | ['jest-junit', {
43 | outputDirectory: 'test-results/unit',
44 | outputName: 'test-results.xml'
45 | }]
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Genesys Cloud Services, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "name": "file tests",
10 | "request": "launch",
11 | "args": [
12 | "--runInBand",
13 | "${fileBasenameNoExtension}",
14 | "--coverage=false"
15 | ],
16 | "cwd": "${workspaceFolder}",
17 | "console": "integratedTerminal",
18 | "internalConsoleOptions": "neverOpen",
19 | "disableOptimisticBPs": true,
20 | "program": "${workspaceFolder}/node_modules/jest/bin/jest"
21 | },
22 | {
23 | "type": "node",
24 | "name": "all tests",
25 | "request": "launch",
26 | "args": [
27 | "--runInBand",
28 | "--coverage=false"
29 | ],
30 | "cwd": "${workspaceFolder}",
31 | "console": "integratedTerminal",
32 | "internalConsoleOptions": "neverOpen",
33 | "disableOptimisticBPs": true,
34 | "program": "${workspaceFolder}/node_modules/jest/bin/jest"
35 | }
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webrtc-stats-gatherer",
3 | "version": "9.0.11",
4 | "description": "Gathers stats on interval for webrtc peer connection",
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "files": [
8 | "src",
9 | "dist"
10 | ],
11 | "scripts": {
12 | "clean": "rimraf dist lib",
13 | "build": "npm run clean && tsc",
14 | "_test": "jest",
15 | "test": "npm run lint && jest",
16 | "lint": "eslint src",
17 | "lint:fix": "npm run lint -- --fix",
18 | "format": "prettier src --write && prettier test --write",
19 | "greenkeep": "npx npm-check --update"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/mypurecloud/webrtc-stats-gatherer.git"
24 | },
25 | "keywords": [
26 | "webrtc",
27 | "stats"
28 | ],
29 | "author": "Xander Dumaine , Garrett Jensen , Fippo & Lance <3",
30 | "license": "MIT",
31 | "publishConfig": {
32 | "registry": "https://registry.npmjs.org/"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/mypurecloud/webrtc-stats-gatherer/issues"
36 | },
37 | "homepage": "https://github.com/mypurecloud/webrtc-stats-gatherer#readme",
38 | "devDependencies": {
39 | "@eslint/js": "^9.5.0",
40 | "@types/jest": "^29.5.12",
41 | "eslint": "^8.57.0",
42 | "jest": "^29.7.0",
43 | "jest-environment-jsdom": "^29.7.0",
44 | "jest-junit": "^16.0.0",
45 | "pre-commit": "^1.2.2",
46 | "pre-push": "^0.1.4",
47 | "prettier": "^3.3.2",
48 | "rimraf": "^5.0.7",
49 | "ts-jest": "^29.1.5",
50 | "typescript": "^5.5.2",
51 | "typescript-eslint": "^7.14.1"
52 | },
53 | "pre-push": [
54 | "test"
55 | ],
56 | "pre-commit": [
57 | "lint",
58 | "format"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5 |
6 | # [Unreleased](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.11...HEAD)
7 | ### Added
8 | * [STREAM-884](https://inindca.atlassian.net/browse/STREAM-884) - Generate a test report in JUnit.xml format.
9 |
10 | # [v9.0.11](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.10...v9.0.11)
11 | ### Changed
12 | * [STREAM-621](https://inindca.atlassian.net/browse/STREAM-621) - Remove pipeline infra, update CODEOWNERS.
13 |
14 | # [v9.0.10](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.9...v9.0.10)
15 | ### Fixed
16 | * [NO-JIRA] Use target of `es5` to avoid breaking consumers
17 |
18 | # [v9.0.9](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.8...v9.0.9)
19 | ### Fixed
20 | * [STREAM-69](https://inindca.atlassian.net/browse/STREAM-69) Return an empty array when gathering stats for other states like `disconnected` to prevent errors in callers when network connectivity drops
21 |
22 | ### Changed
23 | * [STREAM-32](https://inindca.atlassian.net/browse/STREAM-32) Update dev dependencies, switch to ESLint, add Prettier
24 |
25 | # [v9.0.8](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.6...v9.0.8)
26 | ### Fixed
27 | * [PCM-2326](https://inindca.atlassian.net/browse/PCM-2326) - Stop stats gathering if the session ends in "fail" state
28 |
29 | # [v9.0.6](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.5...v9.0.6)
30 | ### Fixed
31 | * [PCM-2058](https://inindca.atlassian.net/browse/PCM-2058) - Fix issue with initial stats check generating an error
32 |
33 | # [v9.0.5](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.4...v9.0.5)
34 |
35 | ### Fixed
36 | * [PCM-1946](https://inindca.atlassian.net/browse/PCM-1946) - Fix issue with initial stats check generating an error
37 |
38 | # [v9.0.4](https://github.com/mypurecloud/webrtc-stats-gatherer/compare/v9.0.3...v9.0.4)
39 |
40 | ### Fixed
41 | * [PCM-1946](https://inindca.atlassian.net/browse/PCM-1946) - Fix the typo for calculating incoming tracks' packetloss
42 |
--------------------------------------------------------------------------------
/src/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface StatsEvent {
2 | name: string;
3 | session?: string;
4 | conference?: string;
5 | initiator?: string;
6 | }
7 |
8 | export interface StatsConnectEvent extends StatsEvent {
9 | name: 'connect';
10 | userAgent: string;
11 | platform: string;
12 | cores: number;
13 | connectTime: number;
14 | localCandidateType?: string;
15 | remoteCandidateType?: string;
16 | candidatePair?: string;
17 | candidatePairDetails?: {
18 | local?: {
19 | id?: string;
20 | timestamp?: number;
21 | type?: string;
22 | transportId?: string;
23 | isRemote?: boolean;
24 | networkType?: string;
25 | ip?: string;
26 | port?: number;
27 | protocol?: string;
28 | candidateType?: string;
29 | priority?: number;
30 | deleted?: boolean;
31 | };
32 | remote?: {
33 | id?: string;
34 | timestamp?: number;
35 | type?: string;
36 | transportId?: string;
37 | isRemote?: boolean;
38 | ip?: string;
39 | port?: number;
40 | protocol?: string;
41 | candidateType?: string;
42 | priority?: number;
43 | deleted?: boolean;
44 | };
45 | pair?: {
46 | id?: string;
47 | timestamp?: number;
48 | type?: string;
49 | transportId?: string;
50 | localCandidateId?: string;
51 | remoteCandidateId?: string;
52 | state?: string;
53 | priority?: number;
54 | nominated?: boolean;
55 | writable?: boolean;
56 | bytesSent?: number;
57 | bytesReceived?: number;
58 | totalRoundTripTime?: number;
59 | currentRoundTripTime?: number;
60 | availableOutgoingBitrate?: number;
61 | requestsReceived?: number;
62 | requestsSent?: number;
63 | responsesReceived?: number;
64 | responsesSent?: number;
65 | consentRequestsSent?: number;
66 | };
67 | };
68 | transport?: string;
69 | networkType?: string;
70 | }
71 |
72 | export interface FailureEvent extends StatsEvent {
73 | name: 'failure';
74 | failTime: number;
75 | iceRW?: number;
76 | numLocalHostCandidates?: number;
77 | numLocalSrflxCandidates?: number;
78 | numLocalRelayCandidates?: number;
79 | numRemoteHostCandidates?: number;
80 | numRemoteSrflxCandidates?: number;
81 | numRemoteRelayCandidates?: number;
82 | }
83 |
84 | export interface GetStatsEvent extends StatsEvent {
85 | name: 'getStats';
86 | tracks: TrackStats[];
87 | remoteTracks: TrackStats[];
88 | type?: string;
89 | candidatePairHadActiveSource?: boolean;
90 | localCandidateChanged?: boolean;
91 | remoteCandidateChanged?: boolean;
92 | networkType?: string;
93 | candidatePair?: string;
94 | bytesSent?: number;
95 | bytesReceived?: number;
96 | requestsReceived?: number;
97 | requestsSent?: number;
98 | responsesReceived?: number;
99 | responsesSent?: number;
100 | consentRequestsSent?: number;
101 | totalRoundTripTime?: number;
102 | }
103 |
104 | export interface TrackStats {
105 | track: string;
106 | kind: 'audio' | 'video';
107 | bytes: number;
108 | codec?: string;
109 | bitrate?: number;
110 | jitter?: number;
111 | roundTripTime: number;
112 |
113 | packetsSent?: number;
114 | packetsLost: number;
115 | packetLoss: number;
116 | intervalPacketsSent?: number;
117 | intervalPacketsLost?: number;
118 | intervalPacketLoss?: number;
119 |
120 | retransmittedBytesSent?: number;
121 | retransmittedPacketsSent?: number;
122 |
123 | packetsReceived?: number;
124 | intervalPacketsReceived?: number;
125 |
126 | audioLevel?: number;
127 | totalAudioEnergy?: number;
128 | echoReturnLoss?: number;
129 | echoReturnLossEnhancement?: number;
130 | }
131 |
--------------------------------------------------------------------------------
/test/mock-sdp.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdp": "v=0\r\no=- 8723607365185958946 2 IN IP4 xx.xxx.xxx.xx\r\ns=-\r\nt=0 0\r\na=group:BUNDLE video audio data\r\na=msid-semantic: WMS 8vyiPwd1YGs8Kmz9Xxf3atma7zRmI3M1BVF3\r\nm=video 52643 UDP/TLS/RTP/SAVPF 100 116 117\r\nc=IN IP4 xx.xxx.xxx.xx\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=candidate:2099809157 1 udp 2122194687 xx.xxx.xxx.xx 56433 typ host generation 0 network-id 3\r\na=candidate:3471623853 1 udp 2122129151 xx.xxx.xxx.xx 64406 typ host generation 0 network-id 1\r\na=candidate:2281949109 1 udp 2122063615 xx.xxx.xxx.xx 51949 typ host generation 0 network-id 2 network-cost 10\r\na=candidate:155959553 1 udp 1685855999 xx.xxx.xxx.xx 51949 typ srflx raddr xx.xxx.xxx.xx rport 51949 generation 0 network-id 2 network-cost 10\r\na=candidate:2980426774 1 udp 1685987071 xx.xxx.xxx.xx 56433 typ srflx raddr xx.xxx.xxx.xx rport 56433 generation 0 network-id 3\r\na=candidate:3030388924 1 udp 41820159 xx.xxx.xxx.xx 52643 typ relay raddr xx.xxx.xxx.xx rport 56433 generation 0 network-id 3\r\na=candidate:2493585755 1 udp 41819903 xx.xxx.xxx.xx 62520 typ relay raddr xx.xxx.xxx.xx rport 56433 generation 0 network-id 3\r\na=candidate:3030388924 1 udp 41689087 xx.xxx.xxx.xx 50596 typ relay raddr xx.xxx.xxx.xx rport 51949 generation 0 network-id 2 network-cost 10\r\na=candidate:2493585755 1 udp 41688831 xx.xxx.xxx.xx 61197 typ relay raddr xx.xxx.xxx.xx rport 51949 generation 0 network-id 2 network-cost 10\r\na=candidate:23753127 1 tcp 1518280447 xx.xxx.xxx.xx 9 typ host tcptype active generation 0 network-id 45 network-cost 50\r\na=candidate:866875253 1 tcp 1518214911 xx.xxx.xxx.xx 9 typ host tcptype active generation 0 network-id 3\r\na=candidate:2154773085 1 tcp 1518149375 xx.xxx.xxx.xx 9 typ host tcptype active generation 0 network-id 1\r\na=candidate:3330292549 1 tcp 1518083839 xx.xxx.xxx.xx 9 typ host tcptype active generation 0 network-id 2 network-cost 10\r\na=ice-ufrag:UvdW\r\na=ice-pwd:ChoDd/FJYg8fwZXvkkUAGmtx\r\na=fingerprint:sha-256 97:24:9A:81:A6:31:0D:F2:40:DC:9B:77:DD:43:61:88:85:0E:97:C4:16:13:D8:BD:E2:D7:69:85:3C:96:56:79\r\na=setup:active\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 nack pli\r\na=rtcp-fb:100 goog-remb\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\na=ssrc:1736533542 cname:9QXTjVRMGHpi+iFY\r\na=ssrc:1736533542 msid:8vyiPwd1YGs8Kmz9Xxf3atma7zRmI3M1BVF3 8deea3c8-f641-4617-968e-b8c459ce89e5\r\na=ssrc:1736533542 mslabel:8vyiPwd1YGs8Kmz9Xxf3atma7zRmI3M1BVF3\r\na=ssrc:1736533542 label:8deea3c8-f641-4617-968e-b8c459ce89e5\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:UvdW\r\na=ice-pwd:ChoDd/FJYg8fwZXvkkUAGmtx\r\na=fingerprint:sha-256 97:24:9A:81:A6:31:0D:F2:40:DC:9B:77:DD:43:61:88:85:0E:97:C4:16:13:D8:BD:E2:D7:69:85:3C:96:56:79\r\na=setup:active\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=ssrc:66009837 cname:9QXTjVRMGHpi+iFY\r\na=ssrc:66009837 msid:8vyiPwd1YGs8Kmz9Xxf3atma7zRmI3M1BVF3 51e382ec-ce22-4850-8c6e-7b3da44def98\r\na=ssrc:66009837 mslabel:8vyiPwd1YGs8Kmz9Xxf3atma7zRmI3M1BVF3\r\na=ssrc:66009837 label:51e382ec-ce22-4850-8c6e-7b3da44def98\r\nm=application 9 DTLS/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\nb=AS:30\r\na=ice-ufrag:UvdW\r\na=ice-pwd:ChoDd/FJYg8fwZXvkkUAGmtx\r\na=fingerprint:sha-256 97:24:9A:81:A6:31:0D:F2:40:DC:9B:77:DD:43:61:88:85:0E:97:C4:16:13:D8:BD:E2:D7:69:85:3C:96:56:79\r\na=setup:active\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024"
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "./src/**/*"
4 | ],
5 | "exclude": [
6 | "./test/**/*"
7 | ],
8 | "compilerOptions": {
9 | /* Basic Options */
10 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
11 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
12 | "lib": [
13 | "es6",
14 | "dom",
15 | "ES2017"
16 | ], /* Specify library files to be included in the compilation. */
17 | // "allowJs": true, /* Allow javascript files to be compiled. */
18 | // "checkJs": true, /* Report errors in .js files. */
19 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
20 | "declaration": true, /* Generates corresponding '.d.ts' file. */
21 | // "declarationDir": "dist/typings",
22 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
23 | "sourceMap": true, /* Generates corresponding '.map' file. */
24 | // "outFile": "./", /* Concatenate and emit output to single file. */
25 | "outDir": "./dist", /* Redirect output structure to the directory. */
26 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
27 | // "composite": true, /* Enable project compilation */
28 | // "incremental": true, /* Enable incremental compilation */
29 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
30 | // "removeComments": true, /* Do not emit comments to output. */
31 | // "noEmit": true, /* Do not emit outputs. */
32 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
33 | "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
34 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
35 | /* Strict Type-Checking Options */
36 | "strict": true, /* Enable all strict type-checking options. */
37 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
38 | "strictNullChecks": false, /* Enable strict null checks. */
39 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
40 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
41 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
42 | "noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */
43 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
44 | /* Additional Checks */
45 | // "noUnusedLocals": true, /* Report errors on unused locals. */
46 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
47 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
48 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
49 | /* Module Resolution Options */
50 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
51 | "baseUrl": "./src/types", /* Base directory to resolve non-absolute module names. */
52 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
53 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
54 | // "typeRoots": [], /* List of folders to include type definitions from. */
55 | "types": [
56 | "node",
57 | "jest"
58 | ], /* Type declaration files to be included in compilation. */
59 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
60 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
61 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
62 | /* Source Map Options */
63 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
64 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
65 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
66 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
67 | /* Experimental Options */
68 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
69 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/mock-spec-stats-recvonly.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "key": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
4 | "value": {
5 | "id": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
6 | "timestamp": 1571687966413.522,
7 | "type": "certificate",
8 | "fingerprint": "4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
9 | "fingerprintAlgorithm": "sha-256",
10 | "base64Certificate": "MIIBFjCBvaADAgECAgkA+u7UIbM5Bf8wCgYIKoZIzj0EAwIwETEPMA0GA1UEAwwGV2ViUlRDMB4XDTE5MTAyMDE5NTgzNloXDTE5MTEyMDE5NTgzNlowETEPMA0GA1UEAwwGV2ViUlRDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL+6mSh54eIwC43u2GTcTbDPEy6qs2Ju+q/VihDS51nXSmOlPIZTWPTsKZyztHaO0H4XXwScdrbTfwo1Xy4XfoDAKBggqhkjOPQQDAgNIADBFAiEAh6bFRu+g7t7xZutMvp98wtEPoxRDHtiNAeD8wte3q+UCIHgL8SqpANHKjiYxB6iW3zq6CbMvd9KvwAijYWBt6a/H"
11 | }
12 | },
13 | {
14 | "key": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
15 | "value": {
16 | "id": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
17 | "timestamp": 1571687966413.522,
18 | "type": "certificate",
19 | "fingerprint": "DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
20 | "fingerprintAlgorithm": "sha-1",
21 | "base64Certificate": "MIIBsTCCARqgAwIBAgIGAW3vQVhGMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNVBAMMEUpWQiAwLjEuYnVpbGQuU1ZOMB4XDTE5MTAyMDE2NTgyMFoXDTE5MTAyODE2NTgyMFowHDEaMBgGA1UEAwwRSlZCIDAuMS5idWlsZC5TVk4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOD5A2Y2+Du6AldslGzwXHQjrddaiQyHK3REbvew1qHTivVclTq450nVMV6TeLqkVJujX1KHp5X4umkyYYBzHFZUFzFo76JpxxxutuAkhuoAoajEMgWybUcR/S2BOWYDah7tgfv23QDhzXUbPc0MwLmDWsf2l6nmlNb92SCKxWmZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAvS3p12Lf7pko6B8mlh8uj5V/1Anqly8g0CRLFE/DsaUFGU6AIDLNyS3T5pzbAFZmhJyyTfWo4RHlivZo+16VIrzMGacGt6gD8VaFifKXxRaSz/zaLgTasx1KiKJfUWjVn5cpND0HgYweVZygh/paFCGSw0viKXjDB1ciu1M40bA="
22 | }
23 | },
24 | {
25 | "key": "RTCCodec_video_Inbound_100",
26 | "value": {
27 | "id": "RTCCodec_video_Inbound_100",
28 | "timestamp": 1571687966413.522,
29 | "type": "codec",
30 | "payloadType": 100,
31 | "mimeType": "video/VP8",
32 | "clockRate": 90000
33 | }
34 | },
35 | {
36 | "key": "RTCCodec_video_Inbound_116",
37 | "value": {
38 | "id": "RTCCodec_video_Inbound_116",
39 | "timestamp": 1571687966413.522,
40 | "type": "codec",
41 | "payloadType": 116,
42 | "mimeType": "video/red",
43 | "clockRate": 90000
44 | }
45 | },
46 | {
47 | "key": "RTCCodec_video_Inbound_117",
48 | "value": {
49 | "id": "RTCCodec_video_Inbound_117",
50 | "timestamp": 1571687966413.522,
51 | "type": "codec",
52 | "payloadType": 117,
53 | "mimeType": "video/ulpfec",
54 | "clockRate": 90000
55 | }
56 | },
57 | {
58 | "key": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
59 | "value": {
60 | "id": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
61 | "timestamp": 1571687966413.522,
62 | "type": "candidate-pair",
63 | "transportId": "RTCTransport_audio_1",
64 | "localCandidateId": "RTCIceCandidate_WzsdBtXT",
65 | "remoteCandidateId": "RTCIceCandidate_nq8LUB9k",
66 | "state": "succeeded",
67 | "priority": 7962116751041233000,
68 | "nominated": true,
69 | "writable": true,
70 | "bytesSent": 0,
71 | "bytesReceived": 7881226,
72 | "totalRoundTripTime": 0.654,
73 | "currentRoundTripTime": 0.026,
74 | "availableOutgoingBitrate": 1777375,
75 | "availableIncomingBitrate": 3802216,
76 | "requestsReceived": 20,
77 | "requestsSent": 1,
78 | "responsesReceived": 24,
79 | "responsesSent": 20,
80 | "consentRequestsSent": 23
81 | }
82 | },
83 | {
84 | "key": "RTCIceCandidate_WzsdBtXT",
85 | "value": {
86 | "id": "RTCIceCandidate_WzsdBtXT",
87 | "timestamp": 1571687966413.522,
88 | "type": "local-candidate",
89 | "transportId": "RTCTransport_audio_1",
90 | "isRemote": false,
91 | "networkType": "ethernet",
92 | "ip": "10.27.48.148",
93 | "port": 23033,
94 | "protocol": "udp",
95 | "candidateType": "prflx",
96 | "priority": 1853824767,
97 | "deleted": false
98 | }
99 | },
100 | {
101 | "key": "RTCIceCandidate_nq8LUB9k",
102 | "value": {
103 | "id": "RTCIceCandidate_nq8LUB9k",
104 | "timestamp": 1571687966413.522,
105 | "type": "remote-candidate",
106 | "transportId": "RTCTransport_audio_1",
107 | "isRemote": true,
108 | "ip": "10.27.31.155",
109 | "port": 10000,
110 | "protocol": "udp",
111 | "candidateType": "host",
112 | "priority": 2130706431,
113 | "deleted": false
114 | }
115 | },
116 | {
117 | "key": "RTCInboundRTPVideoStream_1043752814",
118 | "value": {
119 | "id": "RTCInboundRTPVideoStream_1043752814",
120 | "timestamp": 1571687966413.522,
121 | "type": "inbound-rtp",
122 | "ssrc": 1043752814,
123 | "isRemote": false,
124 | "mediaType": "video",
125 | "kind": "video",
126 | "trackId": "RTCMediaStreamTrack_receiver_2",
127 | "transportId": "RTCTransport_audio_1",
128 | "codecId": "RTCCodec_video_Inbound_100",
129 | "firCount": 0,
130 | "pliCount": 1,
131 | "nackCount": 1,
132 | "qpSum": 20207,
133 | "packetsReceived": 6905,
134 | "bytesReceived": 7637399,
135 | "packetsLost": 18,
136 | "lastPacketReceivedTimestamp": 260348.613,
137 | "framesDecoded": 833,
138 | "keyFramesDecoded": 3,
139 | "totalDecodeTime": 3.16
140 | }
141 | },
142 | {
143 | "key": "RTCMediaStreamTrack_receiver_2",
144 | "value": {
145 | "id": "RTCMediaStreamTrack_receiver_2",
146 | "timestamp": 1571687966413.522,
147 | "type": "track",
148 | "trackIdentifier": "83d46c2c-5348-4902-a26a-d405c8e968de",
149 | "remoteSource": true,
150 | "ended": false,
151 | "detached": false,
152 | "kind": "video",
153 | "jitterBufferDelay": 30.04,
154 | "jitterBufferEmittedCount": 832,
155 | "frameWidth": 1280,
156 | "frameHeight": 720,
157 | "framesReceived": 836,
158 | "framesDecoded": 833,
159 | "framesDropped": 3
160 | }
161 | },
162 | {
163 | "key": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
164 | "value": {
165 | "id": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
166 | "timestamp": 1571687966413.522,
167 | "type": "stream",
168 | "streamIdentifier": "1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
169 | "trackIds": [
170 | "RTCMediaStreamTrack_receiver_1",
171 | "RTCMediaStreamTrack_receiver_2"
172 | ]
173 | }
174 | },
175 | {
176 | "key": "RTCPeerConnection",
177 | "value": {
178 | "id": "RTCPeerConnection",
179 | "timestamp": 1571687966413.522,
180 | "type": "peer-connection",
181 | "dataChannelsOpened": 1,
182 | "dataChannelsClosed": 0
183 | }
184 | },
185 | {
186 | "key": "RTCTransport_audio_1",
187 | "value": {
188 | "id": "RTCTransport_audio_1",
189 | "timestamp": 1571687966413.522,
190 | "type": "transport",
191 | "bytesSent": 0,
192 | "bytesReceived": 7881226,
193 | "dtlsState": "connected",
194 | "selectedCandidatePairId": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
195 | "localCertificateId": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
196 | "remoteCertificateId": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23"
197 | }
198 | }
199 | ]
--------------------------------------------------------------------------------
/test/mock-spec-stats-3.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "key": "RTCAudioSource_1",
4 | "value": {
5 | "id": "RTCAudioSource_1",
6 | "timestamp": 1571687996412.929,
7 | "type": "media-source",
8 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
9 | "kind": "audio",
10 | "audioLevel": 0.0007629627368999298,
11 | "totalAudioEnergy": 1.2405401795363926,
12 | "totalSamplesDuration": 80.18000000000411
13 | }
14 | },
15 | {
16 | "key": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
17 | "value": {
18 | "id": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
19 | "timestamp": 1571687996412.929,
20 | "type": "certificate",
21 | "fingerprint": "4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
22 | "fingerprintAlgorithm": "sha-256",
23 | "base64Certificate": "MIIBFjCBvaADAgECAgkA+u7UIbM5Bf8wCgYIKoZIzj0EAwIwETEPMA0GA1UEAwwGV2ViUlRDMB4XDTE5MTAyMDE5NTgzNloXDTE5MTEyMDE5NTgzNlowETEPMA0GA1UEAwwGV2ViUlRDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL+6mSh54eIwC43u2GTcTbDPEy6qs2Ju+q/VihDS51nXSmOlPIZTWPTsKZyztHaO0H4XXwScdrbTfwo1Xy4XfoDAKBggqhkjOPQQDAgNIADBFAiEAh6bFRu+g7t7xZutMvp98wtEPoxRDHtiNAeD8wte3q+UCIHgL8SqpANHKjiYxB6iW3zq6CbMvd9KvwAijYWBt6a/H"
24 | }
25 | },
26 | {
27 | "key": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
28 | "value": {
29 | "id": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
30 | "timestamp": 1571687996412.929,
31 | "type": "certificate",
32 | "fingerprint": "DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
33 | "fingerprintAlgorithm": "sha-1",
34 | "base64Certificate": "MIIBsTCCARqgAwIBAgIGAW3vQVhGMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNVBAMMEUpWQiAwLjEuYnVpbGQuU1ZOMB4XDTE5MTAyMDE2NTgyMFoXDTE5MTAyODE2NTgyMFowHDEaMBgGA1UEAwwRSlZCIDAuMS5idWlsZC5TVk4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOD5A2Y2+Du6AldslGzwXHQjrddaiQyHK3REbvew1qHTivVclTq450nVMV6TeLqkVJujX1KHp5X4umkyYYBzHFZUFzFo76JpxxxutuAkhuoAoajEMgWybUcR/S2BOWYDah7tgfv23QDhzXUbPc0MwLmDWsf2l6nmlNb92SCKxWmZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAvS3p12Lf7pko6B8mlh8uj5V/1Anqly8g0CRLFE/DsaUFGU6AIDLNyS3T5pzbAFZmhJyyTfWo4RHlivZo+16VIrzMGacGt6gD8VaFifKXxRaSz/zaLgTasx1KiKJfUWjVn5cpND0HgYweVZygh/paFCGSw0viKXjDB1ciu1M40bA="
35 | }
36 | },
37 | {
38 | "key": "RTCCodec_audio_Inbound_0",
39 | "value": {
40 | "id": "RTCCodec_audio_Inbound_0",
41 | "timestamp": 1571687996412.929,
42 | "type": "codec",
43 | "payloadType": 0,
44 | "mimeType": "audio/PCMU",
45 | "clockRate": 8000
46 | }
47 | },
48 | {
49 | "key": "RTCCodec_audio_Inbound_103",
50 | "value": {
51 | "id": "RTCCodec_audio_Inbound_103",
52 | "timestamp": 1571687996412.929,
53 | "type": "codec",
54 | "payloadType": 103,
55 | "mimeType": "audio/ISAC",
56 | "clockRate": 16000
57 | }
58 | },
59 | {
60 | "key": "RTCCodec_audio_Inbound_104",
61 | "value": {
62 | "id": "RTCCodec_audio_Inbound_104",
63 | "timestamp": 1571687996412.929,
64 | "type": "codec",
65 | "payloadType": 104,
66 | "mimeType": "audio/ISAC",
67 | "clockRate": 32000
68 | }
69 | },
70 | {
71 | "key": "RTCCodec_audio_Inbound_111",
72 | "value": {
73 | "id": "RTCCodec_audio_Inbound_111",
74 | "timestamp": 1571687996412.929,
75 | "type": "codec",
76 | "payloadType": 111,
77 | "mimeType": "audio/opus",
78 | "clockRate": 48000
79 | }
80 | },
81 | {
82 | "key": "RTCCodec_audio_Inbound_8",
83 | "value": {
84 | "id": "RTCCodec_audio_Inbound_8",
85 | "timestamp": 1571687996412.929,
86 | "type": "codec",
87 | "payloadType": 8,
88 | "mimeType": "audio/PCMA",
89 | "clockRate": 8000
90 | }
91 | },
92 | {
93 | "key": "RTCCodec_audio_Inbound_9",
94 | "value": {
95 | "id": "RTCCodec_audio_Inbound_9",
96 | "timestamp": 1571687996412.929,
97 | "type": "codec",
98 | "payloadType": 9,
99 | "mimeType": "audio/G722",
100 | "clockRate": 8000
101 | }
102 | },
103 | {
104 | "key": "RTCCodec_audio_Outbound_0",
105 | "value": {
106 | "id": "RTCCodec_audio_Outbound_0",
107 | "timestamp": 1571687996412.929,
108 | "type": "codec",
109 | "payloadType": 0,
110 | "mimeType": "audio/PCMU",
111 | "clockRate": 8000
112 | }
113 | },
114 | {
115 | "key": "RTCCodec_audio_Outbound_103",
116 | "value": {
117 | "id": "RTCCodec_audio_Outbound_103",
118 | "timestamp": 1571687996412.929,
119 | "type": "codec",
120 | "payloadType": 103,
121 | "mimeType": "audio/ISAC",
122 | "clockRate": 16000
123 | }
124 | },
125 | {
126 | "key": "RTCCodec_audio_Outbound_104",
127 | "value": {
128 | "id": "RTCCodec_audio_Outbound_104",
129 | "timestamp": 1571687996412.929,
130 | "type": "codec",
131 | "payloadType": 104,
132 | "mimeType": "audio/ISAC",
133 | "clockRate": 32000
134 | }
135 | },
136 | {
137 | "key": "RTCCodec_audio_Outbound_111",
138 | "value": {
139 | "id": "RTCCodec_audio_Outbound_111",
140 | "timestamp": 1571687996412.929,
141 | "type": "codec",
142 | "payloadType": 111,
143 | "mimeType": "audio/opus",
144 | "clockRate": 48000
145 | }
146 | },
147 | {
148 | "key": "RTCCodec_audio_Outbound_8",
149 | "value": {
150 | "id": "RTCCodec_audio_Outbound_8",
151 | "timestamp": 1571687996412.929,
152 | "type": "codec",
153 | "payloadType": 8,
154 | "mimeType": "audio/PCMA",
155 | "clockRate": 8000
156 | }
157 | },
158 | {
159 | "key": "RTCCodec_audio_Outbound_9",
160 | "value": {
161 | "id": "RTCCodec_audio_Outbound_9",
162 | "timestamp": 1571687996412.929,
163 | "type": "codec",
164 | "payloadType": 9,
165 | "mimeType": "audio/G722",
166 | "clockRate": 8000
167 | }
168 | },
169 | {
170 | "key": "RTCCodec_video_Inbound_100",
171 | "value": {
172 | "id": "RTCCodec_video_Inbound_100",
173 | "timestamp": 1571687996412.929,
174 | "type": "codec",
175 | "payloadType": 100,
176 | "mimeType": "video/VP8",
177 | "clockRate": 90000
178 | }
179 | },
180 | {
181 | "key": "RTCCodec_video_Inbound_116",
182 | "value": {
183 | "id": "RTCCodec_video_Inbound_116",
184 | "timestamp": 1571687996412.929,
185 | "type": "codec",
186 | "payloadType": 116,
187 | "mimeType": "video/red",
188 | "clockRate": 90000
189 | }
190 | },
191 | {
192 | "key": "RTCCodec_video_Inbound_117",
193 | "value": {
194 | "id": "RTCCodec_video_Inbound_117",
195 | "timestamp": 1571687996412.929,
196 | "type": "codec",
197 | "payloadType": 117,
198 | "mimeType": "video/ulpfec",
199 | "clockRate": 90000
200 | }
201 | },
202 | {
203 | "key": "RTCCodec_video_Outbound_100",
204 | "value": {
205 | "id": "RTCCodec_video_Outbound_100",
206 | "timestamp": 1571687996412.929,
207 | "type": "codec",
208 | "payloadType": 100,
209 | "mimeType": "video/VP8",
210 | "clockRate": 90000
211 | }
212 | },
213 | {
214 | "key": "RTCCodec_video_Outbound_116",
215 | "value": {
216 | "id": "RTCCodec_video_Outbound_116",
217 | "timestamp": 1571687996412.929,
218 | "type": "codec",
219 | "payloadType": 116,
220 | "mimeType": "video/red",
221 | "clockRate": 90000
222 | }
223 | },
224 | {
225 | "key": "RTCCodec_video_Outbound_117",
226 | "value": {
227 | "id": "RTCCodec_video_Outbound_117",
228 | "timestamp": 1571687996412.929,
229 | "type": "codec",
230 | "payloadType": 117,
231 | "mimeType": "video/ulpfec",
232 | "clockRate": 90000
233 | }
234 | },
235 | {
236 | "key": "RTCDataChannel_0",
237 | "value": {
238 | "id": "RTCDataChannel_0",
239 | "timestamp": 1571687996412.929,
240 | "type": "data-channel",
241 | "label": "default",
242 | "protocol": "http://jitsi.org/protocols/colibri",
243 | "datachannelid": 0,
244 | "state": "open",
245 | "messagesSent": 7,
246 | "bytesSent": 1460,
247 | "messagesReceived": 8,
248 | "bytesReceived": 1505
249 | }
250 | },
251 | {
252 | "key": "RTCIceCandidatePair_qwerty_asdfasdf",
253 | "value": {
254 | "id": "RTCIceCandidatePair_qwerty_asdfasdf",
255 | "timestamp": 1571687996412.929,
256 | "type": "candidate-pair",
257 | "transportId": "RTCTransport_audio_1",
258 | "localCandidateId": "RTCIceCandidate_qwerty",
259 | "remoteCandidateId": "RTCIceCandidate_asdfasdf",
260 | "state": "succeeded",
261 | "priority": 7962116751041233000,
262 | "nominated": true,
263 | "writable": true,
264 | "bytesSent": 12301506,
265 | "bytesReceived": 17210339,
266 | "totalRoundTripTime": 0.981,
267 | "currentRoundTripTime": 0.027,
268 | "availableOutgoingBitrate": 3824272,
269 | "availableIncomingBitrate": 3856816,
270 | "requestsReceived": 30,
271 | "requestsSent": 1,
272 | "responsesReceived": 36,
273 | "responsesSent": 30,
274 | "consentRequestsSent": 35
275 | }
276 | },
277 | {
278 | "key": "RTCIceCandidate_qwerty",
279 | "value": {
280 | "id": "RTCIceCandidate_qwerty",
281 | "timestamp": 1571687996412.929,
282 | "type": "local-candidate",
283 | "transportId": "RTCTransport_audio_1",
284 | "isRemote": false,
285 | "networkType": "ethernet",
286 | "ip": "10.27.48.148",
287 | "port": 23033,
288 | "protocol": "udp",
289 | "candidateType": "prflx",
290 | "priority": 1853824767,
291 | "deleted": false
292 | }
293 | },
294 | {
295 | "key": "RTCIceCandidate_asdfasdf",
296 | "value": {
297 | "id": "RTCIceCandidate_asdfasdf",
298 | "timestamp": 1571687996412.929,
299 | "type": "remote-candidate",
300 | "transportId": "RTCTransport_audio_1",
301 | "isRemote": true,
302 | "ip": "10.27.31.155",
303 | "port": 10000,
304 | "protocol": "udp",
305 | "candidateType": "host",
306 | "priority": 2130706431,
307 | "deleted": false
308 | }
309 | },
310 | {
311 | "key": "RTCMediaStreamTrack_receiver_1",
312 | "value": {
313 | "id": "RTCMediaStreamTrack_receiver_1",
314 | "timestamp": 1571687996412.929,
315 | "type": "track",
316 | "trackIdentifier": "c6e25c3b-6ab3-4a84-8469-2fec574ffc94",
317 | "remoteSource": true,
318 | "ended": true,
319 | "detached": false,
320 | "kind": "audio",
321 | "jitterBufferDelay": 108576,
322 | "jitterBufferEmittedCount": 2782080,
323 | "audioLevel": 0.000640888698995941,
324 | "totalAudioEnergy": 2.0850051616069054,
325 | "totalSamplesReceived": 2783040,
326 | "totalSamplesDuration": 57.979999999997034,
327 | "concealedSamples": 1483,
328 | "silentConcealedSamples": 0,
329 | "concealmentEvents": 2,
330 | "insertedSamplesForDeceleration": 1171,
331 | "removedSamplesForAcceleration": 949
332 | }
333 | },
334 | {
335 | "key": "RTCMediaStreamTrack_receiver_2",
336 | "value": {
337 | "id": "RTCMediaStreamTrack_receiver_2",
338 | "timestamp": 1571687996412.929,
339 | "type": "track",
340 | "trackIdentifier": "83d46c2c-5348-4902-a26a-d405c8e968de",
341 | "remoteSource": true,
342 | "ended": false,
343 | "detached": false,
344 | "kind": "video",
345 | "jitterBufferDelay": 58.849,
346 | "jitterBufferEmittedCount": 1732,
347 | "frameWidth": 1280,
348 | "frameHeight": 720,
349 | "framesReceived": 1736,
350 | "framesDecoded": 1733,
351 | "framesDropped": 3
352 | }
353 | },
354 | {
355 | "key": "RTCMediaStreamTrack_sender_1",
356 | "value": {
357 | "id": "RTCMediaStreamTrack_sender_1",
358 | "timestamp": 1571687996412.929,
359 | "type": "track",
360 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
361 | "mediaSourceId": "RTCAudioSource_1",
362 | "remoteSource": false,
363 | "ended": false,
364 | "detached": false,
365 | "kind": "audio",
366 | "echoReturnLoss": -30,
367 | "echoReturnLossEnhancement": 0.17551203072071075
368 | }
369 | },
370 | {
371 | "key": "RTCMediaStreamTrack_sender_2",
372 | "value": {
373 | "id": "RTCMediaStreamTrack_sender_2",
374 | "timestamp": 1571687996412.929,
375 | "type": "track",
376 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
377 | "mediaSourceId": "RTCVideoSource_2",
378 | "remoteSource": false,
379 | "ended": false,
380 | "detached": false,
381 | "kind": "video",
382 | "frameWidth": 1280,
383 | "frameHeight": 720,
384 | "framesSent": 2389,
385 | "hugeFramesSent": 3
386 | }
387 | },
388 | {
389 | "key": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
390 | "value": {
391 | "id": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
392 | "timestamp": 1571687996412.929,
393 | "type": "stream",
394 | "streamIdentifier": "1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
395 | "trackIds": [
396 | "RTCMediaStreamTrack_receiver_1",
397 | "RTCMediaStreamTrack_receiver_2"
398 | ]
399 | }
400 | },
401 | {
402 | "key": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
403 | "value": {
404 | "id": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
405 | "timestamp": 1571687996412.929,
406 | "type": "stream",
407 | "streamIdentifier": "tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
408 | "trackIds": [
409 | "RTCMediaStreamTrack_sender_1",
410 | "RTCMediaStreamTrack_sender_2"
411 | ]
412 | }
413 | },
414 | {
415 | "key": "RTCPeerConnection",
416 | "value": {
417 | "id": "RTCPeerConnection",
418 | "timestamp": 1571687996412.929,
419 | "type": "peer-connection",
420 | "dataChannelsOpened": 1,
421 | "dataChannelsClosed": 0
422 | }
423 | },
424 | {
425 | "key": "RTCTransport_audio_1",
426 | "value": {
427 | "id": "RTCTransport_audio_1",
428 | "timestamp": 1571687996412.929,
429 | "type": "transport",
430 | "bytesSent": 12301506,
431 | "bytesReceived": 17210339,
432 | "dtlsState": "connected",
433 | "selectedCandidatePairId": "RTCIceCandidatePair_qwerty_asdfasdf",
434 | "localCertificateId": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
435 | "remoteCertificateId": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23"
436 | }
437 | },
438 | {
439 | "key": "RTCVideoSource_2",
440 | "value": {
441 | "id": "RTCVideoSource_2",
442 | "timestamp": 1571687996412.929,
443 | "type": "media-source",
444 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
445 | "kind": "video",
446 | "width": 1280,
447 | "height": 720,
448 | "framesPerSecond": 30
449 | }
450 | }
451 | ]
--------------------------------------------------------------------------------
/test/mock-spec-stats-initial.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "key": "RTCAudioSource_1",
4 | "value": {
5 | "id": "RTCAudioSource_1",
6 | "timestamp": 1571687916415.012,
7 | "type": "media-source",
8 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
9 | "kind": "audio",
10 | "audioLevel": 0.0011902218695638905,
11 | "totalAudioEnergy": 1.1333024790305303e-7,
12 | "totalSamplesDuration": 0.18000000000000002
13 | }
14 | },
15 | {
16 | "key": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
17 | "value": {
18 | "id": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
19 | "timestamp": 1571687916415.012,
20 | "type": "certificate",
21 | "fingerprint": "4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
22 | "fingerprintAlgorithm": "sha-256",
23 | "base64Certificate": "MIIBFjCBvaADAgECAgkA+u7UIbM5Bf8wCgYIKoZIzj0EAwIwETEPMA0GA1UEAwwGV2ViUlRDMB4XDTE5MTAyMDE5NTgzNloXDTE5MTEyMDE5NTgzNlowETEPMA0GA1UEAwwGV2ViUlRDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL+6mSh54eIwC43u2GTcTbDPEy6qs2Ju+q/VihDS51nXSmOlPIZTWPTsKZyztHaO0H4XXwScdrbTfwo1Xy4XfoDAKBggqhkjOPQQDAgNIADBFAiEAh6bFRu+g7t7xZutMvp98wtEPoxRDHtiNAeD8wte3q+UCIHgL8SqpANHKjiYxB6iW3zq6CbMvd9KvwAijYWBt6a/H"
24 | }
25 | },
26 | {
27 | "key": "RTCCodec_audio_Inbound_0",
28 | "value": {
29 | "id": "RTCCodec_audio_Inbound_0",
30 | "timestamp": 1571687916415.012,
31 | "type": "codec",
32 | "payloadType": 0,
33 | "mimeType": "audio/PCMU",
34 | "clockRate": 8000
35 | }
36 | },
37 | {
38 | "key": "RTCCodec_audio_Inbound_103",
39 | "value": {
40 | "id": "RTCCodec_audio_Inbound_103",
41 | "timestamp": 1571687916415.012,
42 | "type": "codec",
43 | "payloadType": 103,
44 | "mimeType": "audio/ISAC",
45 | "clockRate": 16000
46 | }
47 | },
48 | {
49 | "key": "RTCCodec_audio_Inbound_104",
50 | "value": {
51 | "id": "RTCCodec_audio_Inbound_104",
52 | "timestamp": 1571687916415.012,
53 | "type": "codec",
54 | "payloadType": 104,
55 | "mimeType": "audio/ISAC",
56 | "clockRate": 32000
57 | }
58 | },
59 | {
60 | "key": "RTCCodec_audio_Inbound_111",
61 | "value": {
62 | "id": "RTCCodec_audio_Inbound_111",
63 | "timestamp": 1571687916415.012,
64 | "type": "codec",
65 | "payloadType": 111,
66 | "mimeType": "audio/opus",
67 | "clockRate": 48000
68 | }
69 | },
70 | {
71 | "key": "RTCCodec_audio_Inbound_8",
72 | "value": {
73 | "id": "RTCCodec_audio_Inbound_8",
74 | "timestamp": 1571687916415.012,
75 | "type": "codec",
76 | "payloadType": 8,
77 | "mimeType": "audio/PCMA",
78 | "clockRate": 8000
79 | }
80 | },
81 | {
82 | "key": "RTCCodec_audio_Inbound_9",
83 | "value": {
84 | "id": "RTCCodec_audio_Inbound_9",
85 | "timestamp": 1571687916415.012,
86 | "type": "codec",
87 | "payloadType": 9,
88 | "mimeType": "audio/G722",
89 | "clockRate": 8000
90 | }
91 | },
92 | {
93 | "key": "RTCCodec_audio_Outbound_0",
94 | "value": {
95 | "id": "RTCCodec_audio_Outbound_0",
96 | "timestamp": 1571687916415.012,
97 | "type": "codec",
98 | "payloadType": 0,
99 | "mimeType": "audio/PCMU",
100 | "clockRate": 8000
101 | }
102 | },
103 | {
104 | "key": "RTCCodec_audio_Outbound_103",
105 | "value": {
106 | "id": "RTCCodec_audio_Outbound_103",
107 | "timestamp": 1571687916415.012,
108 | "type": "codec",
109 | "payloadType": 103,
110 | "mimeType": "audio/ISAC",
111 | "clockRate": 16000
112 | }
113 | },
114 | {
115 | "key": "RTCCodec_audio_Outbound_104",
116 | "value": {
117 | "id": "RTCCodec_audio_Outbound_104",
118 | "timestamp": 1571687916415.012,
119 | "type": "codec",
120 | "payloadType": 104,
121 | "mimeType": "audio/ISAC",
122 | "clockRate": 32000
123 | }
124 | },
125 | {
126 | "key": "RTCCodec_audio_Outbound_111",
127 | "value": {
128 | "id": "RTCCodec_audio_Outbound_111",
129 | "timestamp": 1571687916415.012,
130 | "type": "codec",
131 | "payloadType": 111,
132 | "mimeType": "audio/opus",
133 | "clockRate": 48000
134 | }
135 | },
136 | {
137 | "key": "RTCCodec_audio_Outbound_8",
138 | "value": {
139 | "id": "RTCCodec_audio_Outbound_8",
140 | "timestamp": 1571687916415.012,
141 | "type": "codec",
142 | "payloadType": 8,
143 | "mimeType": "audio/PCMA",
144 | "clockRate": 8000
145 | }
146 | },
147 | {
148 | "key": "RTCCodec_audio_Outbound_9",
149 | "value": {
150 | "id": "RTCCodec_audio_Outbound_9",
151 | "timestamp": 1571687916415.012,
152 | "type": "codec",
153 | "payloadType": 9,
154 | "mimeType": "audio/G722",
155 | "clockRate": 8000
156 | }
157 | },
158 | {
159 | "key": "RTCCodec_video_Inbound_100",
160 | "value": {
161 | "id": "RTCCodec_video_Inbound_100",
162 | "timestamp": 1571687916415.012,
163 | "type": "codec",
164 | "payloadType": 100,
165 | "mimeType": "video/VP8",
166 | "clockRate": 90000
167 | }
168 | },
169 | {
170 | "key": "RTCCodec_video_Inbound_116",
171 | "value": {
172 | "id": "RTCCodec_video_Inbound_116",
173 | "timestamp": 1571687916415.012,
174 | "type": "codec",
175 | "payloadType": 116,
176 | "mimeType": "video/red",
177 | "clockRate": 90000
178 | }
179 | },
180 | {
181 | "key": "RTCCodec_video_Inbound_117",
182 | "value": {
183 | "id": "RTCCodec_video_Inbound_117",
184 | "timestamp": 1571687916415.012,
185 | "type": "codec",
186 | "payloadType": 117,
187 | "mimeType": "video/ulpfec",
188 | "clockRate": 90000
189 | }
190 | },
191 | {
192 | "key": "RTCCodec_video_Outbound_100",
193 | "value": {
194 | "id": "RTCCodec_video_Outbound_100",
195 | "timestamp": 1571687916415.012,
196 | "type": "codec",
197 | "payloadType": 100,
198 | "mimeType": "video/VP8",
199 | "clockRate": 90000
200 | }
201 | },
202 | {
203 | "key": "RTCCodec_video_Outbound_116",
204 | "value": {
205 | "id": "RTCCodec_video_Outbound_116",
206 | "timestamp": 1571687916415.012,
207 | "type": "codec",
208 | "payloadType": 116,
209 | "mimeType": "video/red",
210 | "clockRate": 90000
211 | }
212 | },
213 | {
214 | "key": "RTCCodec_video_Outbound_117",
215 | "value": {
216 | "id": "RTCCodec_video_Outbound_117",
217 | "timestamp": 1571687916415.012,
218 | "type": "codec",
219 | "payloadType": 117,
220 | "mimeType": "video/ulpfec",
221 | "clockRate": 90000
222 | }
223 | },
224 | {
225 | "key": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
226 | "value": {
227 | "id": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
228 | "timestamp": 1571687916415.012,
229 | "type": "candidate-pair",
230 | "transportId": "RTCTransport_audio_1",
231 | "localCandidateId": "RTCIceCandidate_WzsdBtXT",
232 | "remoteCandidateId": "RTCIceCandidate_nq8LUB9k",
233 | "state": "succeeded",
234 | "priority": 7962116751041233000,
235 | "nominated": true,
236 | "writable": true,
237 | "bytesSent": 155,
238 | "bytesReceived": 0,
239 | "totalRoundTripTime": 0.047,
240 | "currentRoundTripTime": 0.047,
241 | "requestsReceived": 0,
242 | "requestsSent": 1,
243 | "responsesReceived": 1,
244 | "responsesSent": 0,
245 | "consentRequestsSent": 1
246 | }
247 | },
248 | {
249 | "key": "RTCIceCandidatePair_yI+kvNvF_kJy6c1E9",
250 | "value": {
251 | "id": "RTCIceCandidatePair_yI+kvNvF_kJy6c1E9",
252 | "timestamp": 1571687916415.012,
253 | "type": "candidate-pair",
254 | "transportId": "RTCTransport_audio_1",
255 | "localCandidateId": "RTCIceCandidate_yI+kvNvF",
256 | "remoteCandidateId": "RTCIceCandidate_kJy6c1E9",
257 | "state": "waiting",
258 | "priority": 179896594039051780,
259 | "nominated": false,
260 | "writable": false,
261 | "bytesSent": 0,
262 | "bytesReceived": 0,
263 | "totalRoundTripTime": 0,
264 | "requestsReceived": 0,
265 | "requestsSent": 0,
266 | "responsesReceived": 0,
267 | "responsesSent": 0,
268 | "consentRequestsSent": 0
269 | }
270 | },
271 | {
272 | "key": "RTCIceCandidate_WzsdBtXT",
273 | "value": {
274 | "id": "RTCIceCandidate_WzsdBtXT",
275 | "timestamp": 1571687916415.012,
276 | "type": "local-candidate",
277 | "transportId": "RTCTransport_audio_1",
278 | "isRemote": false,
279 | "networkType": "ethernet",
280 | "ip": "10.27.48.148",
281 | "port": 23033,
282 | "protocol": "udp",
283 | "candidateType": "prflx",
284 | "priority": 1853824767,
285 | "deleted": false
286 | }
287 | },
288 | {
289 | "key": "RTCIceCandidate_kJy6c1E9",
290 | "value": {
291 | "id": "RTCIceCandidate_kJy6c1E9",
292 | "timestamp": 1571687916415.012,
293 | "type": "remote-candidate",
294 | "transportId": "RTCTransport_audio_1",
295 | "isRemote": true,
296 | "ip": "3.92.206.70",
297 | "port": 10000,
298 | "protocol": "udp",
299 | "candidateType": "srflx",
300 | "priority": 1677724415,
301 | "deleted": false
302 | }
303 | },
304 | {
305 | "key": "RTCIceCandidate_nq8LUB9k",
306 | "value": {
307 | "id": "RTCIceCandidate_nq8LUB9k",
308 | "timestamp": 1571687916415.012,
309 | "type": "remote-candidate",
310 | "transportId": "RTCTransport_audio_1",
311 | "isRemote": true,
312 | "ip": "10.27.31.155",
313 | "port": 10000,
314 | "protocol": "udp",
315 | "candidateType": "host",
316 | "priority": 2130706431,
317 | "deleted": false
318 | }
319 | },
320 | {
321 | "key": "RTCIceCandidate_yI+kvNvF",
322 | "value": {
323 | "id": "RTCIceCandidate_yI+kvNvF",
324 | "timestamp": 1571687916415.012,
325 | "type": "local-candidate",
326 | "transportId": "RTCTransport_audio_1",
327 | "isRemote": false,
328 | "networkType": "ethernet",
329 | "ip": "54.242.15.31",
330 | "port": 23033,
331 | "protocol": "udp",
332 | "relayProtocol": "udp",
333 | "candidateType": "relay",
334 | "priority": 41885439,
335 | "deleted": false
336 | }
337 | },
338 | {
339 | "key": "RTCMediaStreamTrack_sender_1",
340 | "value": {
341 | "id": "RTCMediaStreamTrack_sender_1",
342 | "timestamp": 1571687916415.012,
343 | "type": "track",
344 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
345 | "mediaSourceId": "RTCAudioSource_1",
346 | "remoteSource": false,
347 | "ended": false,
348 | "detached": false,
349 | "kind": "audio"
350 | }
351 | },
352 | {
353 | "key": "RTCMediaStreamTrack_sender_2",
354 | "value": {
355 | "id": "RTCMediaStreamTrack_sender_2",
356 | "timestamp": 1571687916415.012,
357 | "type": "track",
358 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
359 | "mediaSourceId": "RTCVideoSource_2",
360 | "remoteSource": false,
361 | "ended": false,
362 | "detached": false,
363 | "kind": "video",
364 | "frameWidth": 0,
365 | "frameHeight": 0,
366 | "framesSent": 0,
367 | "hugeFramesSent": 0
368 | }
369 | },
370 | {
371 | "key": "RTCMediaStream_466fc587-c93d-407b-abbf-d4efa4ac0be6",
372 | "value": {
373 | "id": "RTCMediaStream_466fc587-c93d-407b-abbf-d4efa4ac0be6",
374 | "timestamp": 1571687916415.012,
375 | "type": "stream",
376 | "streamIdentifier": "466fc587-c93d-407b-abbf-d4efa4ac0be6",
377 | "trackIds": [
378 | "RTCMediaStreamTrack_receiver_1",
379 | "RTCMediaStreamTrack_receiver_2"
380 | ]
381 | }
382 | },
383 | {
384 | "key": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
385 | "value": {
386 | "id": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
387 | "timestamp": 1571687916415.012,
388 | "type": "stream",
389 | "streamIdentifier": "tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
390 | "trackIds": [
391 | "RTCMediaStreamTrack_sender_1",
392 | "RTCMediaStreamTrack_sender_2"
393 | ]
394 | }
395 | },
396 | {
397 | "key": "RTCOutboundRTPAudioStream_545464236",
398 | "value": {
399 | "id": "RTCOutboundRTPAudioStream_545464236",
400 | "timestamp": 1571687916415.012,
401 | "type": "outbound-rtp",
402 | "ssrc": 545464236,
403 | "isRemote": false,
404 | "mediaType": "audio",
405 | "kind": "audio",
406 | "trackId": "RTCMediaStreamTrack_sender_1",
407 | "transportId": "RTCTransport_audio_1",
408 | "codecId": "RTCCodec_audio_Outbound_111",
409 | "mediaSourceId": "RTCAudioSource_1",
410 | "packetsSent": 0,
411 | "retransmittedPacketsSent": 0,
412 | "bytesSent": 0,
413 | "retransmittedBytesSent": 0
414 | }
415 | },
416 | {
417 | "key": "RTCOutboundRTPVideoStream_780297609",
418 | "value": {
419 | "id": "RTCOutboundRTPVideoStream_780297609",
420 | "timestamp": 1571687916415.012,
421 | "type": "outbound-rtp",
422 | "ssrc": 780297609,
423 | "isRemote": false,
424 | "mediaType": "video",
425 | "kind": "video",
426 | "trackId": "RTCMediaStreamTrack_sender_2",
427 | "transportId": "RTCTransport_audio_1",
428 | "codecId": "RTCCodec_video_Outbound_100",
429 | "firCount": 0,
430 | "pliCount": 0,
431 | "nackCount": 0,
432 | "mediaSourceId": "RTCVideoSource_2",
433 | "packetsSent": 0,
434 | "retransmittedPacketsSent": 0,
435 | "bytesSent": 0,
436 | "retransmittedBytesSent": 0,
437 | "framesEncoded": 0,
438 | "keyFramesEncoded": 0,
439 | "totalEncodeTime": 0,
440 | "totalEncodedBytesTarget": 0,
441 | "totalPacketSendDelay": 0,
442 | "qualityLimitationReason": "bandwidth"
443 | }
444 | },
445 | {
446 | "key": "RTCPeerConnection",
447 | "value": {
448 | "id": "RTCPeerConnection",
449 | "timestamp": 1571687916415.012,
450 | "type": "peer-connection",
451 | "dataChannelsOpened": 0,
452 | "dataChannelsClosed": 0
453 | }
454 | },
455 | {
456 | "key": "RTCTransport_audio_1",
457 | "value": {
458 | "id": "RTCTransport_audio_1",
459 | "timestamp": 1571687916415.012,
460 | "type": "transport",
461 | "bytesSent": 155,
462 | "bytesReceived": 0,
463 | "dtlsState": "connecting",
464 | "selectedCandidatePairId": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
465 | "localCertificateId": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8"
466 | }
467 | },
468 | {
469 | "key": "RTCVideoSource_2",
470 | "value": {
471 | "id": "RTCVideoSource_2",
472 | "timestamp": 1571687916415.012,
473 | "type": "media-source",
474 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
475 | "kind": "video",
476 | "width": 1280,
477 | "height": 720,
478 | "framesPerSecond": 35
479 | }
480 | }
481 | ]
--------------------------------------------------------------------------------
/test/mock-spec-stats-1.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "key": "RTCAudioSource_1",
4 | "value": {
5 | "id": "RTCAudioSource_1",
6 | "timestamp": 1571687926414.613,
7 | "type": "media-source",
8 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
9 | "kind": "audio",
10 | "audioLevel": 0.0009765923032319102,
11 | "totalAudioEnergy": 0.0229691871587937,
12 | "totalSamplesDuration": 10.179999999999827
13 | }
14 | },
15 | {
16 | "key": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
17 | "value": {
18 | "id": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
19 | "timestamp": 1571687926414.613,
20 | "type": "certificate",
21 | "fingerprint": "4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
22 | "fingerprintAlgorithm": "sha-256",
23 | "base64Certificate": "MIIBFjCBvaADAgECAgkA+u7UIbM5Bf8wCgYIKoZIzj0EAwIwETEPMA0GA1UEAwwGV2ViUlRDMB4XDTE5MTAyMDE5NTgzNloXDTE5MTEyMDE5NTgzNlowETEPMA0GA1UEAwwGV2ViUlRDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL+6mSh54eIwC43u2GTcTbDPEy6qs2Ju+q/VihDS51nXSmOlPIZTWPTsKZyztHaO0H4XXwScdrbTfwo1Xy4XfoDAKBggqhkjOPQQDAgNIADBFAiEAh6bFRu+g7t7xZutMvp98wtEPoxRDHtiNAeD8wte3q+UCIHgL8SqpANHKjiYxB6iW3zq6CbMvd9KvwAijYWBt6a/H"
24 | }
25 | },
26 | {
27 | "key": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
28 | "value": {
29 | "id": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
30 | "timestamp": 1571687926414.613,
31 | "type": "certificate",
32 | "fingerprint": "DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
33 | "fingerprintAlgorithm": "sha-1",
34 | "base64Certificate": "MIIBsTCCARqgAwIBAgIGAW3vQVhGMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNVBAMMEUpWQiAwLjEuYnVpbGQuU1ZOMB4XDTE5MTAyMDE2NTgyMFoXDTE5MTAyODE2NTgyMFowHDEaMBgGA1UEAwwRSlZCIDAuMS5idWlsZC5TVk4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOD5A2Y2+Du6AldslGzwXHQjrddaiQyHK3REbvew1qHTivVclTq450nVMV6TeLqkVJujX1KHp5X4umkyYYBzHFZUFzFo76JpxxxutuAkhuoAoajEMgWybUcR/S2BOWYDah7tgfv23QDhzXUbPc0MwLmDWsf2l6nmlNb92SCKxWmZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAvS3p12Lf7pko6B8mlh8uj5V/1Anqly8g0CRLFE/DsaUFGU6AIDLNyS3T5pzbAFZmhJyyTfWo4RHlivZo+16VIrzMGacGt6gD8VaFifKXxRaSz/zaLgTasx1KiKJfUWjVn5cpND0HgYweVZygh/paFCGSw0viKXjDB1ciu1M40bA="
35 | }
36 | },
37 | {
38 | "key": "RTCCodec_audio_Inbound_0",
39 | "value": {
40 | "id": "RTCCodec_audio_Inbound_0",
41 | "timestamp": 1571687926414.613,
42 | "type": "codec",
43 | "payloadType": 0,
44 | "mimeType": "audio/PCMU",
45 | "clockRate": 8000
46 | }
47 | },
48 | {
49 | "key": "RTCCodec_audio_Inbound_103",
50 | "value": {
51 | "id": "RTCCodec_audio_Inbound_103",
52 | "timestamp": 1571687926414.613,
53 | "type": "codec",
54 | "payloadType": 103,
55 | "mimeType": "audio/ISAC",
56 | "clockRate": 16000
57 | }
58 | },
59 | {
60 | "key": "RTCCodec_audio_Inbound_104",
61 | "value": {
62 | "id": "RTCCodec_audio_Inbound_104",
63 | "timestamp": 1571687926414.613,
64 | "type": "codec",
65 | "payloadType": 104,
66 | "mimeType": "audio/ISAC",
67 | "clockRate": 32000
68 | }
69 | },
70 | {
71 | "key": "RTCCodec_audio_Inbound_111",
72 | "value": {
73 | "id": "RTCCodec_audio_Inbound_111",
74 | "timestamp": 1571687926414.613,
75 | "type": "codec",
76 | "payloadType": 111,
77 | "mimeType": "audio/opus",
78 | "clockRate": 48000
79 | }
80 | },
81 | {
82 | "key": "RTCCodec_audio_Inbound_8",
83 | "value": {
84 | "id": "RTCCodec_audio_Inbound_8",
85 | "timestamp": 1571687926414.613,
86 | "type": "codec",
87 | "payloadType": 8,
88 | "mimeType": "audio/PCMA",
89 | "clockRate": 8000
90 | }
91 | },
92 | {
93 | "key": "RTCCodec_audio_Inbound_9",
94 | "value": {
95 | "id": "RTCCodec_audio_Inbound_9",
96 | "timestamp": 1571687926414.613,
97 | "type": "codec",
98 | "payloadType": 9,
99 | "mimeType": "audio/G722",
100 | "clockRate": 8000
101 | }
102 | },
103 | {
104 | "key": "RTCCodec_audio_Outbound_0",
105 | "value": {
106 | "id": "RTCCodec_audio_Outbound_0",
107 | "timestamp": 1571687926414.613,
108 | "type": "codec",
109 | "payloadType": 0,
110 | "mimeType": "audio/PCMU",
111 | "clockRate": 8000
112 | }
113 | },
114 | {
115 | "key": "RTCCodec_audio_Outbound_103",
116 | "value": {
117 | "id": "RTCCodec_audio_Outbound_103",
118 | "timestamp": 1571687926414.613,
119 | "type": "codec",
120 | "payloadType": 103,
121 | "mimeType": "audio/ISAC",
122 | "clockRate": 16000
123 | }
124 | },
125 | {
126 | "key": "RTCCodec_audio_Outbound_104",
127 | "value": {
128 | "id": "RTCCodec_audio_Outbound_104",
129 | "timestamp": 1571687926414.613,
130 | "type": "codec",
131 | "payloadType": 104,
132 | "mimeType": "audio/ISAC",
133 | "clockRate": 32000
134 | }
135 | },
136 | {
137 | "key": "RTCCodec_audio_Outbound_111",
138 | "value": {
139 | "id": "RTCCodec_audio_Outbound_111",
140 | "timestamp": 1571687926414.613,
141 | "type": "codec",
142 | "payloadType": 111,
143 | "mimeType": "audio/opus",
144 | "clockRate": 48000
145 | }
146 | },
147 | {
148 | "key": "RTCCodec_audio_Outbound_8",
149 | "value": {
150 | "id": "RTCCodec_audio_Outbound_8",
151 | "timestamp": 1571687926414.613,
152 | "type": "codec",
153 | "payloadType": 8,
154 | "mimeType": "audio/PCMA",
155 | "clockRate": 8000
156 | }
157 | },
158 | {
159 | "key": "RTCCodec_audio_Outbound_9",
160 | "value": {
161 | "id": "RTCCodec_audio_Outbound_9",
162 | "timestamp": 1571687926414.613,
163 | "type": "codec",
164 | "payloadType": 9,
165 | "mimeType": "audio/G722",
166 | "clockRate": 8000
167 | }
168 | },
169 | {
170 | "key": "RTCCodec_video_Inbound_100",
171 | "value": {
172 | "id": "RTCCodec_video_Inbound_100",
173 | "timestamp": 1571687926414.613,
174 | "type": "codec",
175 | "payloadType": 100,
176 | "mimeType": "video/VP8",
177 | "clockRate": 90000
178 | }
179 | },
180 | {
181 | "key": "RTCCodec_video_Inbound_116",
182 | "value": {
183 | "id": "RTCCodec_video_Inbound_116",
184 | "timestamp": 1571687926414.613,
185 | "type": "codec",
186 | "payloadType": 116,
187 | "mimeType": "video/red",
188 | "clockRate": 90000
189 | }
190 | },
191 | {
192 | "key": "RTCCodec_video_Inbound_117",
193 | "value": {
194 | "id": "RTCCodec_video_Inbound_117",
195 | "timestamp": 1571687926414.613,
196 | "type": "codec",
197 | "payloadType": 117,
198 | "mimeType": "video/ulpfec",
199 | "clockRate": 90000
200 | }
201 | },
202 | {
203 | "key": "RTCCodec_video_Outbound_100",
204 | "value": {
205 | "id": "RTCCodec_video_Outbound_100",
206 | "timestamp": 1571687926414.613,
207 | "type": "codec",
208 | "payloadType": 100,
209 | "mimeType": "video/VP8",
210 | "clockRate": 90000
211 | }
212 | },
213 | {
214 | "key": "RTCCodec_video_Outbound_116",
215 | "value": {
216 | "id": "RTCCodec_video_Outbound_116",
217 | "timestamp": 1571687926414.613,
218 | "type": "codec",
219 | "payloadType": 116,
220 | "mimeType": "video/red",
221 | "clockRate": 90000
222 | }
223 | },
224 | {
225 | "key": "RTCCodec_video_Outbound_117",
226 | "value": {
227 | "id": "RTCCodec_video_Outbound_117",
228 | "timestamp": 1571687926414.613,
229 | "type": "codec",
230 | "payloadType": 117,
231 | "mimeType": "video/ulpfec",
232 | "clockRate": 90000
233 | }
234 | },
235 | {
236 | "key": "RTCDataChannel_0",
237 | "value": {
238 | "id": "RTCDataChannel_0",
239 | "timestamp": 1571687926414.613,
240 | "type": "data-channel",
241 | "label": "default",
242 | "protocol": "http://jitsi.org/protocols/colibri",
243 | "datachannelid": 0,
244 | "state": "open",
245 | "messagesSent": 2,
246 | "bytesSent": 492,
247 | "messagesReceived": 2,
248 | "bytesReceived": 223
249 | }
250 | },
251 | {
252 | "key": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
253 | "value": {
254 | "id": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
255 | "timestamp": 1571687926414.613,
256 | "type": "candidate-pair",
257 | "transportId": "RTCTransport_audio_1",
258 | "localCandidateId": "RTCIceCandidate_WzsdBtXT",
259 | "remoteCandidateId": "RTCIceCandidate_nq8LUB9k",
260 | "state": "succeeded",
261 | "priority": 7962116751041233000,
262 | "nominated": true,
263 | "writable": true,
264 | "bytesSent": 349198,
265 | "bytesReceived": 3907,
266 | "totalRoundTripTime": 0.231,
267 | "currentRoundTripTime": 0.026,
268 | "availableOutgoingBitrate": 300000,
269 | "requestsReceived": 7,
270 | "requestsSent": 1,
271 | "responsesReceived": 8,
272 | "responsesSent": 7,
273 | "consentRequestsSent": 7
274 | }
275 | },
276 | {
277 | "key": "RTCIceCandidatePair_yI+kvNvF_kJy6c1E9",
278 | "value": {
279 | "id": "RTCIceCandidatePair_yI+kvNvF_kJy6c1E9",
280 | "timestamp": 1571687926414.613,
281 | "type": "candidate-pair",
282 | "transportId": "RTCTransport_audio_1",
283 | "localCandidateId": "RTCIceCandidate_yI+kvNvF",
284 | "remoteCandidateId": "RTCIceCandidate_kJy6c1E9",
285 | "state": "in-progress",
286 | "priority": 179896594039051780,
287 | "nominated": false,
288 | "writable": false,
289 | "bytesSent": 0,
290 | "bytesReceived": 0,
291 | "totalRoundTripTime": 0,
292 | "requestsReceived": 0,
293 | "requestsSent": 17,
294 | "responsesReceived": 0,
295 | "responsesSent": 0,
296 | "consentRequestsSent": 0
297 | }
298 | },
299 | {
300 | "key": "RTCIceCandidate_WzsdBtXT",
301 | "value": {
302 | "id": "RTCIceCandidate_WzsdBtXT",
303 | "timestamp": 1571687926414.613,
304 | "type": "local-candidate",
305 | "transportId": "RTCTransport_audio_1",
306 | "isRemote": false,
307 | "networkType": "ethernet",
308 | "ip": "10.27.48.148",
309 | "port": 23033,
310 | "protocol": "udp",
311 | "candidateType": "prflx",
312 | "priority": 1853824767,
313 | "deleted": false
314 | }
315 | },
316 | {
317 | "key": "RTCIceCandidate_kJy6c1E9",
318 | "value": {
319 | "id": "RTCIceCandidate_kJy6c1E9",
320 | "timestamp": 1571687926414.613,
321 | "type": "remote-candidate",
322 | "transportId": "RTCTransport_audio_1",
323 | "isRemote": true,
324 | "ip": "3.92.206.70",
325 | "port": 10000,
326 | "protocol": "udp",
327 | "candidateType": "srflx",
328 | "priority": 1677724415,
329 | "deleted": false
330 | }
331 | },
332 | {
333 | "key": "RTCIceCandidate_nq8LUB9k",
334 | "value": {
335 | "id": "RTCIceCandidate_nq8LUB9k",
336 | "timestamp": 1571687926414.613,
337 | "type": "remote-candidate",
338 | "transportId": "RTCTransport_audio_1",
339 | "isRemote": true,
340 | "ip": "10.27.31.155",
341 | "port": 10000,
342 | "protocol": "udp",
343 | "candidateType": "host",
344 | "priority": 2130706431,
345 | "deleted": false
346 | }
347 | },
348 | {
349 | "key": "RTCIceCandidate_yI+kvNvF",
350 | "value": {
351 | "id": "RTCIceCandidate_yI+kvNvF",
352 | "timestamp": 1571687926414.613,
353 | "type": "local-candidate",
354 | "transportId": "RTCTransport_audio_1",
355 | "isRemote": false,
356 | "networkType": "ethernet",
357 | "ip": "54.242.15.31",
358 | "port": 23033,
359 | "protocol": "udp",
360 | "relayProtocol": "udp",
361 | "candidateType": "relay",
362 | "priority": 41885439,
363 | "deleted": false
364 | }
365 | },
366 | {
367 | "key": "RTCMediaStreamTrack_sender_1",
368 | "value": {
369 | "id": "RTCMediaStreamTrack_sender_1",
370 | "timestamp": 1571687926414.613,
371 | "type": "track",
372 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
373 | "mediaSourceId": "RTCAudioSource_1",
374 | "remoteSource": false,
375 | "ended": false,
376 | "detached": false,
377 | "kind": "audio"
378 | }
379 | },
380 | {
381 | "key": "RTCMediaStreamTrack_sender_2",
382 | "value": {
383 | "id": "RTCMediaStreamTrack_sender_2",
384 | "timestamp": 1571687926414.613,
385 | "type": "track",
386 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
387 | "mediaSourceId": "RTCVideoSource_2",
388 | "remoteSource": false,
389 | "ended": false,
390 | "detached": false,
391 | "kind": "video",
392 | "frameWidth": 640,
393 | "frameHeight": 360,
394 | "framesSent": 288,
395 | "hugeFramesSent": 1
396 | }
397 | },
398 | {
399 | "key": "RTCMediaStream_466fc587-c93d-407b-abbf-d4efa4ac0be6",
400 | "value": {
401 | "id": "RTCMediaStream_466fc587-c93d-407b-abbf-d4efa4ac0be6",
402 | "timestamp": 1571687926414.613,
403 | "type": "stream",
404 | "streamIdentifier": "466fc587-c93d-407b-abbf-d4efa4ac0be6",
405 | "trackIds": [
406 | "RTCMediaStreamTrack_receiver_1",
407 | "RTCMediaStreamTrack_receiver_2"
408 | ]
409 | }
410 | },
411 | {
412 | "key": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
413 | "value": {
414 | "id": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
415 | "timestamp": 1571687926414.613,
416 | "type": "stream",
417 | "streamIdentifier": "tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
418 | "trackIds": [
419 | "RTCMediaStreamTrack_sender_1",
420 | "RTCMediaStreamTrack_sender_2"
421 | ]
422 | }
423 | },
424 | {
425 | "key": "RTCOutboundRTPAudioStream_545464236",
426 | "value": {
427 | "id": "RTCOutboundRTPAudioStream_545464236",
428 | "timestamp": 1571687926414.613,
429 | "type": "outbound-rtp",
430 | "ssrc": 545464236,
431 | "isRemote": false,
432 | "mediaType": "audio",
433 | "kind": "audio",
434 | "trackId": "RTCMediaStreamTrack_sender_1",
435 | "transportId": "RTCTransport_audio_1",
436 | "codecId": "RTCCodec_audio_Outbound_111",
437 | "mediaSourceId": "RTCAudioSource_1",
438 | "packetsSent": 481,
439 | "retransmittedPacketsSent": 0,
440 | "bytesSent": 41467,
441 | "retransmittedBytesSent": 0
442 | }
443 | },
444 | {
445 | "key": "RTCOutboundRTPVideoStream_780297609",
446 | "value": {
447 | "id": "RTCOutboundRTPVideoStream_780297609",
448 | "timestamp": 1571687926414.613,
449 | "type": "outbound-rtp",
450 | "ssrc": 780297609,
451 | "isRemote": false,
452 | "mediaType": "video",
453 | "kind": "video",
454 | "trackId": "RTCMediaStreamTrack_sender_2",
455 | "transportId": "RTCTransport_audio_1",
456 | "codecId": "RTCCodec_video_Outbound_100",
457 | "firCount": 0,
458 | "pliCount": 0,
459 | "nackCount": 1,
460 | "qpSum": 17388,
461 | "mediaSourceId": "RTCVideoSource_2",
462 | "packetsSent": 366,
463 | "retransmittedPacketsSent": 1,
464 | "bytesSent": 295849,
465 | "retransmittedBytesSent": 760,
466 | "framesEncoded": 288,
467 | "keyFramesEncoded": 1,
468 | "totalEncodeTime": 0.537,
469 | "totalEncodedBytesTarget": 427706,
470 | "totalPacketSendDelay": 7.917,
471 | "qualityLimitationReason": "bandwidth"
472 | }
473 | },
474 | {
475 | "key": "RTCPeerConnection",
476 | "value": {
477 | "id": "RTCPeerConnection",
478 | "timestamp": 1571687926414.613,
479 | "type": "peer-connection",
480 | "dataChannelsOpened": 1,
481 | "dataChannelsClosed": 0
482 | }
483 | },
484 | {
485 | "key": "RTCTransport_audio_1",
486 | "value": {
487 | "id": "RTCTransport_audio_1",
488 | "timestamp": 1571687926414.613,
489 | "type": "transport",
490 | "bytesSent": 349198,
491 | "bytesReceived": 3907,
492 | "dtlsState": "connected",
493 | "selectedCandidatePairId": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
494 | "localCertificateId": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
495 | "remoteCertificateId": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23"
496 | }
497 | },
498 | {
499 | "key": "RTCVideoSource_2",
500 | "value": {
501 | "id": "RTCVideoSource_2",
502 | "timestamp": 1571687926414.613,
503 | "type": "media-source",
504 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
505 | "kind": "video",
506 | "width": 1280,
507 | "height": 720,
508 | "framesPerSecond": 31
509 | }
510 | }
511 | ]
--------------------------------------------------------------------------------
/test/mock-spec-stats-2.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "key": "RTCAudioSource_1",
4 | "value": {
5 | "id": "RTCAudioSource_1",
6 | "timestamp": 1571687966413.522,
7 | "type": "media-source",
8 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
9 | "kind": "audio",
10 | "audioLevel": 0.0008239997558519242,
11 | "totalAudioEnergy": 1.227674190176716,
12 | "totalSamplesDuration": 50.179999999998586
13 | }
14 | },
15 | {
16 | "key": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
17 | "value": {
18 | "id": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
19 | "timestamp": 1571687966413.522,
20 | "type": "certificate",
21 | "fingerprint": "4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
22 | "fingerprintAlgorithm": "sha-256",
23 | "base64Certificate": "MIIBFjCBvaADAgECAgkA+u7UIbM5Bf8wCgYIKoZIzj0EAwIwETEPMA0GA1UEAwwGV2ViUlRDMB4XDTE5MTAyMDE5NTgzNloXDTE5MTEyMDE5NTgzNlowETEPMA0GA1UEAwwGV2ViUlRDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL+6mSh54eIwC43u2GTcTbDPEy6qs2Ju+q/VihDS51nXSmOlPIZTWPTsKZyztHaO0H4XXwScdrbTfwo1Xy4XfoDAKBggqhkjOPQQDAgNIADBFAiEAh6bFRu+g7t7xZutMvp98wtEPoxRDHtiNAeD8wte3q+UCIHgL8SqpANHKjiYxB6iW3zq6CbMvd9KvwAijYWBt6a/H"
24 | }
25 | },
26 | {
27 | "key": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
28 | "value": {
29 | "id": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
30 | "timestamp": 1571687966413.522,
31 | "type": "certificate",
32 | "fingerprint": "DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23",
33 | "fingerprintAlgorithm": "sha-1",
34 | "base64Certificate": "MIIBsTCCARqgAwIBAgIGAW3vQVhGMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNVBAMMEUpWQiAwLjEuYnVpbGQuU1ZOMB4XDTE5MTAyMDE2NTgyMFoXDTE5MTAyODE2NTgyMFowHDEaMBgGA1UEAwwRSlZCIDAuMS5idWlsZC5TVk4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOD5A2Y2+Du6AldslGzwXHQjrddaiQyHK3REbvew1qHTivVclTq450nVMV6TeLqkVJujX1KHp5X4umkyYYBzHFZUFzFo76JpxxxutuAkhuoAoajEMgWybUcR/S2BOWYDah7tgfv23QDhzXUbPc0MwLmDWsf2l6nmlNb92SCKxWmZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAvS3p12Lf7pko6B8mlh8uj5V/1Anqly8g0CRLFE/DsaUFGU6AIDLNyS3T5pzbAFZmhJyyTfWo4RHlivZo+16VIrzMGacGt6gD8VaFifKXxRaSz/zaLgTasx1KiKJfUWjVn5cpND0HgYweVZygh/paFCGSw0viKXjDB1ciu1M40bA="
35 | }
36 | },
37 | {
38 | "key": "RTCCodec_audio_Inbound_0",
39 | "value": {
40 | "id": "RTCCodec_audio_Inbound_0",
41 | "timestamp": 1571687966413.522,
42 | "type": "codec",
43 | "payloadType": 0,
44 | "mimeType": "audio/PCMU",
45 | "clockRate": 8000
46 | }
47 | },
48 | {
49 | "key": "RTCCodec_audio_Inbound_103",
50 | "value": {
51 | "id": "RTCCodec_audio_Inbound_103",
52 | "timestamp": 1571687966413.522,
53 | "type": "codec",
54 | "payloadType": 103,
55 | "mimeType": "audio/ISAC",
56 | "clockRate": 16000
57 | }
58 | },
59 | {
60 | "key": "RTCCodec_audio_Inbound_104",
61 | "value": {
62 | "id": "RTCCodec_audio_Inbound_104",
63 | "timestamp": 1571687966413.522,
64 | "type": "codec",
65 | "payloadType": 104,
66 | "mimeType": "audio/ISAC",
67 | "clockRate": 32000
68 | }
69 | },
70 | {
71 | "key": "RTCCodec_audio_Inbound_111",
72 | "value": {
73 | "id": "RTCCodec_audio_Inbound_111",
74 | "timestamp": 1571687966413.522,
75 | "type": "codec",
76 | "payloadType": 111,
77 | "mimeType": "audio/opus",
78 | "clockRate": 48000
79 | }
80 | },
81 | {
82 | "key": "RTCCodec_audio_Inbound_8",
83 | "value": {
84 | "id": "RTCCodec_audio_Inbound_8",
85 | "timestamp": 1571687966413.522,
86 | "type": "codec",
87 | "payloadType": 8,
88 | "mimeType": "audio/PCMA",
89 | "clockRate": 8000
90 | }
91 | },
92 | {
93 | "key": "RTCCodec_audio_Inbound_9",
94 | "value": {
95 | "id": "RTCCodec_audio_Inbound_9",
96 | "timestamp": 1571687966413.522,
97 | "type": "codec",
98 | "payloadType": 9,
99 | "mimeType": "audio/G722",
100 | "clockRate": 8000
101 | }
102 | },
103 | {
104 | "key": "RTCCodec_audio_Outbound_0",
105 | "value": {
106 | "id": "RTCCodec_audio_Outbound_0",
107 | "timestamp": 1571687966413.522,
108 | "type": "codec",
109 | "payloadType": 0,
110 | "mimeType": "audio/PCMU",
111 | "clockRate": 8000
112 | }
113 | },
114 | {
115 | "key": "RTCCodec_audio_Outbound_103",
116 | "value": {
117 | "id": "RTCCodec_audio_Outbound_103",
118 | "timestamp": 1571687966413.522,
119 | "type": "codec",
120 | "payloadType": 103,
121 | "mimeType": "audio/ISAC",
122 | "clockRate": 16000
123 | }
124 | },
125 | {
126 | "key": "RTCCodec_audio_Outbound_104",
127 | "value": {
128 | "id": "RTCCodec_audio_Outbound_104",
129 | "timestamp": 1571687966413.522,
130 | "type": "codec",
131 | "payloadType": 104,
132 | "mimeType": "audio/ISAC",
133 | "clockRate": 32000
134 | }
135 | },
136 | {
137 | "key": "RTCCodec_audio_Outbound_111",
138 | "value": {
139 | "id": "RTCCodec_audio_Outbound_111",
140 | "timestamp": 1571687966413.522,
141 | "type": "codec",
142 | "payloadType": 111,
143 | "mimeType": "audio/opus",
144 | "clockRate": 48000
145 | }
146 | },
147 | {
148 | "key": "RTCCodec_audio_Outbound_8",
149 | "value": {
150 | "id": "RTCCodec_audio_Outbound_8",
151 | "timestamp": 1571687966413.522,
152 | "type": "codec",
153 | "payloadType": 8,
154 | "mimeType": "audio/PCMA",
155 | "clockRate": 8000
156 | }
157 | },
158 | {
159 | "key": "RTCCodec_audio_Outbound_9",
160 | "value": {
161 | "id": "RTCCodec_audio_Outbound_9",
162 | "timestamp": 1571687966413.522,
163 | "type": "codec",
164 | "payloadType": 9,
165 | "mimeType": "audio/G722",
166 | "clockRate": 8000
167 | }
168 | },
169 | {
170 | "key": "RTCCodec_video_Inbound_100",
171 | "value": {
172 | "id": "RTCCodec_video_Inbound_100",
173 | "timestamp": 1571687966413.522,
174 | "type": "codec",
175 | "payloadType": 100,
176 | "mimeType": "video/VP8",
177 | "clockRate": 90000
178 | }
179 | },
180 | {
181 | "key": "RTCCodec_video_Inbound_116",
182 | "value": {
183 | "id": "RTCCodec_video_Inbound_116",
184 | "timestamp": 1571687966413.522,
185 | "type": "codec",
186 | "payloadType": 116,
187 | "mimeType": "video/red",
188 | "clockRate": 90000
189 | }
190 | },
191 | {
192 | "key": "RTCCodec_video_Inbound_117",
193 | "value": {
194 | "id": "RTCCodec_video_Inbound_117",
195 | "timestamp": 1571687966413.522,
196 | "type": "codec",
197 | "payloadType": 117,
198 | "mimeType": "video/ulpfec",
199 | "clockRate": 90000
200 | }
201 | },
202 | {
203 | "key": "RTCCodec_video_Outbound_100",
204 | "value": {
205 | "id": "RTCCodec_video_Outbound_100",
206 | "timestamp": 1571687966413.522,
207 | "type": "codec",
208 | "payloadType": 100,
209 | "mimeType": "video/VP8",
210 | "clockRate": 90000
211 | }
212 | },
213 | {
214 | "key": "RTCCodec_video_Outbound_116",
215 | "value": {
216 | "id": "RTCCodec_video_Outbound_116",
217 | "timestamp": 1571687966413.522,
218 | "type": "codec",
219 | "payloadType": 116,
220 | "mimeType": "video/red",
221 | "clockRate": 90000
222 | }
223 | },
224 | {
225 | "key": "RTCCodec_video_Outbound_117",
226 | "value": {
227 | "id": "RTCCodec_video_Outbound_117",
228 | "timestamp": 1571687966413.522,
229 | "type": "codec",
230 | "payloadType": 117,
231 | "mimeType": "video/ulpfec",
232 | "clockRate": 90000
233 | }
234 | },
235 | {
236 | "key": "RTCDataChannel_0",
237 | "value": {
238 | "id": "RTCDataChannel_0",
239 | "timestamp": 1571687966413.522,
240 | "type": "data-channel",
241 | "label": "default",
242 | "protocol": "http://jitsi.org/protocols/colibri",
243 | "datachannelid": 0,
244 | "state": "open",
245 | "messagesSent": 7,
246 | "bytesSent": 1460,
247 | "messagesReceived": 8,
248 | "bytesReceived": 1505
249 | }
250 | },
251 | {
252 | "key": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
253 | "value": {
254 | "id": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
255 | "timestamp": 1571687966413.522,
256 | "type": "candidate-pair",
257 | "transportId": "RTCTransport_audio_1",
258 | "localCandidateId": "RTCIceCandidate_WzsdBtXT",
259 | "remoteCandidateId": "RTCIceCandidate_nq8LUB9k",
260 | "state": "succeeded",
261 | "priority": 7962116751041233000,
262 | "nominated": true,
263 | "writable": true,
264 | "bytesSent": 3224052,
265 | "bytesReceived": 7881226,
266 | "totalRoundTripTime": 0.654,
267 | "currentRoundTripTime": 0.026,
268 | "availableOutgoingBitrate": 1777375,
269 | "availableIncomingBitrate": 3802216,
270 | "requestsReceived": 20,
271 | "requestsSent": 1,
272 | "responsesReceived": 24,
273 | "responsesSent": 20,
274 | "consentRequestsSent": 23
275 | }
276 | },
277 | {
278 | "key": "RTCIceCandidate_WzsdBtXT",
279 | "value": {
280 | "id": "RTCIceCandidate_WzsdBtXT",
281 | "timestamp": 1571687966413.522,
282 | "type": "local-candidate",
283 | "transportId": "RTCTransport_audio_1",
284 | "isRemote": false,
285 | "networkType": "ethernet",
286 | "ip": "10.27.48.148",
287 | "port": 23033,
288 | "protocol": "udp",
289 | "candidateType": "prflx",
290 | "priority": 1853824767,
291 | "deleted": false
292 | }
293 | },
294 | {
295 | "key": "RTCIceCandidate_nq8LUB9k",
296 | "value": {
297 | "id": "RTCIceCandidate_nq8LUB9k",
298 | "timestamp": 1571687966413.522,
299 | "type": "remote-candidate",
300 | "transportId": "RTCTransport_audio_1",
301 | "isRemote": true,
302 | "ip": "10.27.31.155",
303 | "port": 10000,
304 | "protocol": "udp",
305 | "candidateType": "host",
306 | "priority": 2130706431,
307 | "deleted": false
308 | }
309 | },
310 | {
311 | "key": "RTCInboundRTPAudioStream_3047098519",
312 | "value": {
313 | "id": "RTCInboundRTPAudioStream_3047098519",
314 | "timestamp": 1571687966413.522,
315 | "type": "inbound-rtp",
316 | "ssrc": 3047098519,
317 | "isRemote": false,
318 | "mediaType": "audio",
319 | "kind": "audio",
320 | "trackId": "RTCMediaStreamTrack_receiver_1",
321 | "transportId": "RTCTransport_audio_1",
322 | "codecId": "RTCCodec_audio_Inbound_111",
323 | "packetsReceived": 1400,
324 | "bytesReceived": 118898,
325 | "packetsLost": 1,
326 | "lastPacketReceivedTimestamp": 260348.596,
327 | "jitter": 0.002
328 | }
329 | },
330 | {
331 | "key": "RTCInboundRTPVideoStream_1043752814",
332 | "value": {
333 | "id": "RTCInboundRTPVideoStream_1043752814",
334 | "timestamp": 1571687966413.522,
335 | "type": "inbound-rtp",
336 | "ssrc": 1043752814,
337 | "isRemote": false,
338 | "mediaType": "video",
339 | "kind": "video",
340 | "trackId": "RTCMediaStreamTrack_receiver_2",
341 | "transportId": "RTCTransport_audio_1",
342 | "codecId": "RTCCodec_video_Inbound_100",
343 | "firCount": 0,
344 | "pliCount": 1,
345 | "nackCount": 1,
346 | "qpSum": 20207,
347 | "packetsReceived": 6905,
348 | "bytesReceived": 7637399,
349 | "packetsLost": 18,
350 | "lastPacketReceivedTimestamp": 260348.613,
351 | "framesDecoded": 833,
352 | "keyFramesDecoded": 3,
353 | "totalDecodeTime": 3.16
354 | }
355 | },
356 | {
357 | "key": "RTCMediaStreamTrack_receiver_1",
358 | "value": {
359 | "id": "RTCMediaStreamTrack_receiver_1",
360 | "timestamp": 1571687966413.522,
361 | "type": "track",
362 | "trackIdentifier": "c6e25c3b-6ab3-4a84-8469-2fec574ffc94",
363 | "remoteSource": true,
364 | "ended": true,
365 | "detached": false,
366 | "kind": "audio",
367 | "jitterBufferDelay": 56294.4,
368 | "jitterBufferEmittedCount": 1343040,
369 | "audioLevel": 0.0008239997558519242,
370 | "totalAudioEnergy": 2.077036001267227,
371 | "totalSamplesReceived": 1343040,
372 | "totalSamplesDuration": 27.980000000001574,
373 | "concealedSamples": 446,
374 | "silentConcealedSamples": 0,
375 | "concealmentEvents": 1,
376 | "insertedSamplesForDeceleration": 1171,
377 | "removedSamplesForAcceleration": 949
378 | }
379 | },
380 | {
381 | "key": "RTCMediaStreamTrack_receiver_2",
382 | "value": {
383 | "id": "RTCMediaStreamTrack_receiver_2",
384 | "timestamp": 1571687966413.522,
385 | "type": "track",
386 | "trackIdentifier": "83d46c2c-5348-4902-a26a-d405c8e968de",
387 | "remoteSource": true,
388 | "ended": false,
389 | "detached": false,
390 | "kind": "video",
391 | "jitterBufferDelay": 30.04,
392 | "jitterBufferEmittedCount": 832,
393 | "frameWidth": 1280,
394 | "frameHeight": 720,
395 | "framesReceived": 836,
396 | "framesDecoded": 833,
397 | "framesDropped": 3
398 | }
399 | },
400 | {
401 | "key": "RTCMediaStreamTrack_sender_1",
402 | "value": {
403 | "id": "RTCMediaStreamTrack_sender_1",
404 | "timestamp": 1571687966413.522,
405 | "type": "track",
406 | "trackIdentifier": "b5565a72-53af-43c5-9108-2de5c8a3f9ab",
407 | "mediaSourceId": "RTCAudioSource_1",
408 | "remoteSource": false,
409 | "ended": false,
410 | "detached": false,
411 | "kind": "audio",
412 | "echoReturnLoss": -100,
413 | "echoReturnLossEnhancement": 0.18
414 | }
415 | },
416 | {
417 | "key": "RTCMediaStreamTrack_sender_2",
418 | "value": {
419 | "id": "RTCMediaStreamTrack_sender_2",
420 | "timestamp": 1571687966413.522,
421 | "type": "track",
422 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
423 | "mediaSourceId": "RTCVideoSource_2",
424 | "remoteSource": false,
425 | "ended": false,
426 | "detached": false,
427 | "kind": "video",
428 | "frameWidth": 1280,
429 | "frameHeight": 720,
430 | "framesSent": 1489,
431 | "hugeFramesSent": 3
432 | }
433 | },
434 | {
435 | "key": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
436 | "value": {
437 | "id": "RTCMediaStream_1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
438 | "timestamp": 1571687966413.522,
439 | "type": "stream",
440 | "streamIdentifier": "1c7msKqE3Egj7PoeyC20HkSzkHg1Y9cXUAMk",
441 | "trackIds": [
442 | "RTCMediaStreamTrack_receiver_1",
443 | "RTCMediaStreamTrack_receiver_2"
444 | ]
445 | }
446 | },
447 | {
448 | "key": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
449 | "value": {
450 | "id": "RTCMediaStream_tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
451 | "timestamp": 1571687966413.522,
452 | "type": "stream",
453 | "streamIdentifier": "tmNCW2EqWMSTEAYh3z2qql85GHYP2uXYxt8A",
454 | "trackIds": [
455 | "RTCMediaStreamTrack_sender_1",
456 | "RTCMediaStreamTrack_sender_2"
457 | ]
458 | }
459 | },
460 | {
461 | "key": "RTCOutboundRTPAudioStream_545464236",
462 | "value": {
463 | "id": "RTCOutboundRTPAudioStream_545464236",
464 | "timestamp": 1571687966413.522,
465 | "type": "outbound-rtp",
466 | "ssrc": 545464236,
467 | "isRemote": false,
468 | "mediaType": "audio",
469 | "kind": "audio",
470 | "trackId": "RTCMediaStreamTrack_sender_1",
471 | "transportId": "RTCTransport_audio_1",
472 | "codecId": "RTCCodec_audio_Outbound_111",
473 | "mediaSourceId": "RTCAudioSource_1",
474 | "packetsSent": 2481,
475 | "retransmittedPacketsSent": 18,
476 | "bytesSent": 210799,
477 | "retransmittedBytesSent": 0
478 | }
479 | },
480 | {
481 | "key": "RTCOutboundRTPVideoStream_780297609",
482 | "value": {
483 | "id": "RTCOutboundRTPVideoStream_780297609",
484 | "timestamp": 1571687966413.522,
485 | "type": "outbound-rtp",
486 | "ssrc": 780297609,
487 | "isRemote": false,
488 | "mediaType": "video",
489 | "kind": "video",
490 | "trackId": "RTCMediaStreamTrack_sender_2",
491 | "transportId": "RTCTransport_audio_1",
492 | "codecId": "RTCCodec_video_Outbound_100",
493 | "firCount": 0,
494 | "pliCount": 1,
495 | "nackCount": 2,
496 | "qpSum": 70243,
497 | "mediaSourceId": "RTCVideoSource_2",
498 | "packetsSent": 3181,
499 | "retransmittedPacketsSent": 3,
500 | "bytesSent": 2934947,
501 | "retransmittedBytesSent": 2163,
502 | "framesEncoded": 1489,
503 | "keyFramesEncoded": 4,
504 | "totalEncodeTime": 3.425,
505 | "totalEncodedBytesTarget": 3379790,
506 | "totalPacketSendDelay": 84.162,
507 | "qualityLimitationReason": "none"
508 | }
509 | },
510 | {
511 | "key": "RTCPeerConnection",
512 | "value": {
513 | "id": "RTCPeerConnection",
514 | "timestamp": 1571687966413.522,
515 | "type": "peer-connection",
516 | "dataChannelsOpened": 1,
517 | "dataChannelsClosed": 0
518 | }
519 | },
520 | {
521 | "key": "RTCRemoteInboundRtpAudioStream_545464236",
522 | "value": {
523 | "id": "RTCRemoteInboundRtpAudioStream_545464236",
524 | "timestamp": 1571687960465.791,
525 | "type": "remote-inbound-rtp",
526 | "ssrc": 545464236,
527 | "kind": "audio",
528 | "transportId": "RTCTransport_audio_1",
529 | "codecId": "RTCCodec_audio_Outbound_111",
530 | "packetsLost": 1,
531 | "jitter": 0.0017708333333333332,
532 | "localId": "RTCOutboundRTPAudioStream_545464236",
533 | "roundTripTime": 0.052
534 | }
535 | },
536 | {
537 | "key": "RTCRemoteInboundRtpVideoStream_780297609",
538 | "value": {
539 | "id": "RTCRemoteInboundRtpVideoStream_780297609",
540 | "timestamp": 1571687966353.445,
541 | "type": "remote-inbound-rtp",
542 | "ssrc": 780297609,
543 | "kind": "video",
544 | "transportId": "RTCTransport_audio_1",
545 | "codecId": "RTCCodec_video_Outbound_100",
546 | "packetsLost": 18,
547 | "jitter": 0.008344444444444444,
548 | "localId": "RTCOutboundRTPVideoStream_780297609",
549 | "roundTripTime": 0.053
550 | }
551 | },
552 | {
553 | "key": "RTCTransport_audio_1",
554 | "value": {
555 | "id": "RTCTransport_audio_1",
556 | "timestamp": 1571687966413.522,
557 | "type": "transport",
558 | "bytesSent": 3224052,
559 | "bytesReceived": 7881226,
560 | "dtlsState": "connected",
561 | "selectedCandidatePairId": "RTCIceCandidatePair_WzsdBtXT_nq8LUB9k",
562 | "localCertificateId": "RTCCertificate_4A:9B:4E:5E:A6:34:A6:CF:EE:1E:FE:1D:16:9E:2C:AD:55:36:E4:F0:D4:89:8C:DA:F0:AC:DB:24:29:1A:63:D8",
563 | "remoteCertificateId": "RTCCertificate_DC:B5:07:DE:B1:97:30:F7:B8:A8:A7:7F:64:2B:CE:32:9C:1A:11:23"
564 | }
565 | },
566 | {
567 | "key": "RTCVideoSource_2",
568 | "value": {
569 | "id": "RTCVideoSource_2",
570 | "timestamp": 1571687966413.522,
571 | "type": "media-source",
572 | "trackIdentifier": "44d76ce5-e573-4e5b-8bfe-79e5bb71ff14",
573 | "kind": "video",
574 | "width": 1280,
575 | "height": 720,
576 | "framesPerSecond": 31
577 | }
578 | }
579 | ]
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 | import { FailureEvent, GetStatsEvent, StatsConnectEvent, TrackStats } from './interfaces';
3 |
4 | export * from './interfaces';
5 |
6 | let IS_BROWSER;
7 |
8 | const MAX_CANDIDATE_WAIT_ATTEMPTS = 3;
9 |
10 | export interface StatsGathererOpts {
11 | session?: string; // sessionId
12 | initiator?: string;
13 | conference?: string; // conversationId
14 | interval?: number;
15 | logger?: { error(...any); warn(...any) };
16 | }
17 |
18 | export default class StatsGatherer extends EventEmitter {
19 | private session: string;
20 | private initiator: string;
21 | private conference: string;
22 |
23 | private statsInterval: number;
24 | private pollingInterval: number;
25 |
26 | /* eslint-disable @typescript-eslint/no-explicit-any */
27 | private lastResult: Array<{ key: RTCStatsType; value: any }>;
28 | private lastActiveLocalCandidate: any;
29 | private lastActiveRemoteCandidate: any;
30 | /* eslint-enable @typescript-eslint/no-explicit-any */
31 |
32 | private haveConnectionMetrics = false;
33 | private iceStartTime: number;
34 | private iceFailedTime: number;
35 | private iceConnectionTime: number;
36 |
37 | private logger: { error(...any); warn(...any) };
38 |
39 | private statsArr: Array