Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Added collecting of peer metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkonst committed Apr 24, 2020
1 parent d391cd4 commit 74fb72f
Show file tree
Hide file tree
Showing 7 changed files with 1,860 additions and 50 deletions.
49 changes: 35 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
# node-wrtc-agent
WebRTC agent for load testing

# Installation

```bash
git clone [email protected]:netology-group/node-wrtc-agent.git
cd node-wrtc-agent
npm i

# macOS
npm run copy-module-darwin
# or linux
npm run copy-module-linux

sudo npm link
```

# Options

```bash
$ wrtc-agent --help
Options:
--version Show version number [boolean]
-c, --client-id Client id for mqtt-client [string] [required]
-n, --name Conference app name [string] [required]
-P, --password Password for mqtt-client [string] [required]
-r, --room-id Conference room id [string] [required]
--relay-only Use only "relay" ICE candidates [boolean]
--stun STUN server URL [string] [required]
--turn TURN server URL [string] [required]
--turn-password TURN password [string] [required]
--turn-username TURN username [string] [required]
-u, --uri MQTT broker URI [string] [required]
--help Show help [boolean]
--version Show version number [boolean]
-c, --client-id Client id for mqtt-client [string] [required]
-n, --name Conference app name [string] [required]
-P, --password Password for mqtt-client [string] [required]
-r, --room-id Conference room id [string] [required]
--relay-only Use only "relay" ICE candidates [boolean]
--stun STUN server URL [string] [required]
--telemetry Telemetry app name [string]
--telemetry-interval Telemetry interval (ms) [number] [default: 5000]
--turn TURN server URL [string] [required]
--turn-password TURN password [string] [required]
--turn-username TURN username [string] [required]
-u, --uri MQTT broker URI [string] [required]
--help Show help [boolean]
```

# Usage
Expand All @@ -27,8 +44,10 @@ Options:

ACCESS_TOKEN=foobar
BROKER_URI=ws://example.org/
CONFERENCE_APP_NAME=conference.svc.netology-group.services
CONFERENCE_APP_NAME=conference.example.org
CONFERENCE_ROOM_ID=ea3f9fd1-3356-43b4-b709-b7cfc563ea59
STUN_URL=stun:stun.example.org:3478
TELEMETRY_APP_NAME=telemetry.example.org
TURN_URL=turn:example.org:3478
TURN_USERNAME=username
TURN_PASSWORD=password
Expand All @@ -37,9 +56,11 @@ wrtc-agent \
-c web.john-doe.example.org \
-n ${CONFERENCE_APP_NAME} \
-P ${ACCESS_TOKEN} \
-r ea3f9fd1-3356-43b4-b709-b7cfc563ea59 \
-r ${CONFERENCE_ROOM_ID} \
--relay-only \
--stun ${STUN_URL} \
--telemetry ${TELEMETRY_APP_NAME} \
--telemetry-interval 10000 \
--turn ${TURN_URL} \
--turn-username ${TURN_USERNAME} \
--turn-password ${TURN_PASSWORD} \
Expand Down
50 changes: 35 additions & 15 deletions cli.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node

/* eslint-disable quote-props */
const { argv } = require('yargs')
.scriptName('wrtc-agent')
.options({
Expand Down Expand Up @@ -36,6 +37,15 @@ const { argv } = require('yargs')
description: 'STUN server URL',
type: 'string'
},
'telemetry': {
description: 'Telemetry app name',
type: 'string'
},
'telemetry-interval': {
default: 5000,
description: 'Telemetry interval (ms)',
type: 'number'
},
'turn': {
demandOption: true,
description: 'TURN server URL',
Expand All @@ -56,13 +66,13 @@ const { argv } = require('yargs')
demandOption: true,
description: 'MQTT broker URI',
type: 'string'
},
}
})
.help()
/* eslint-enable quote-props */

// console.log('[argv]', argv)

const { createClient, enterRoom } = require('./lib/mqtt')
const { createPeerStatsMonitor, stats2metrics } = require('./lib/metrics')
const { createClient, enterRoom, publishTelemetry } = require('./lib/mqtt')
const { Peer, transformOffer } = require('./lib/peer')

// args
Expand All @@ -73,6 +83,8 @@ const {
roomId,
relayOnly,
stun,
telemetry: telemetryAppName,
telemetryInterval,
turn,
turnPassword,
turnUsername,
Expand All @@ -84,13 +96,14 @@ const iceServers = [
{
urls: turn,
username: turnUsername,
credential: turnPassword,
},
credential: turnPassword
}
]
const iceTransportPolicy = relayOnly ? 'relay' : 'all'

let activeRtcStream = null
let peer = null
let peerStats = null

function listRtcStreamAll (client, roomId) {
const LIST_LIMIT = 25
Expand Down Expand Up @@ -135,7 +148,7 @@ function listRtcStreamAll (client, roomId) {
})
}

function startListening (client, activeRtcStream) {
function startListening (client, mqttClient, activeRtcStream) {
const activeRtcId = activeRtcStream.rtc_id
const listenerOptions = { offerToReceiveVideo: true, offerToReceiveAudio: true }

Expand All @@ -151,13 +164,16 @@ function startListening (client, activeRtcStream) {
},
(track, streams) => {
console.debug('[track]', track.kind)
},
}
)

// setInterval(() => {
// peer.__peer.getStats()
// .then(report => console.log('[getStats]', report))
// }, 5000)
if (telemetryAppName) {
peerStats = createPeerStatsMonitor(peer._peer, telemetryInterval, (stats) => {
const payload = stats2metrics(stats)

publishTelemetry(mqttClient, clientId, telemetryAppName, payload)
})
}

client.connectRtc(activeRtcId)
.then((response) => {
Expand All @@ -184,10 +200,14 @@ function stopListening () {

peer = null
}

if (peerStats) {
peerStats = null
}
}

createClient({ appName, clientId, password, uri })
.then(({ conferenceClient }) => {
.then(({ conferenceClient, mqttClient }) => {
function isStreamActive (stream) {
const { time } = stream

Expand All @@ -204,7 +224,7 @@ createClient({ appName, clientId, password, uri })
if (!activeRtcStream && isStreamActive(stream)) {
activeRtcStream = stream

startListening(conferenceClient, activeRtcStream)
startListening(conferenceClient, mqttClient, activeRtcStream)
} else if (activeRtcStream && stream && activeRtcStream.id === stream.id && isStreamEnded(stream)) {
activeRtcStream = null

Expand All @@ -215,7 +235,7 @@ createClient({ appName, clientId, password, uri })
}

conferenceClient.on('rtc_stream.update', (event) => {
const { id, rtc_id, sent_by, time } = event.data
const { id, rtc_id, sent_by, time } = event.data // eslint-disable-line camelcase

console.group(`[event:${event.type}]`)
console.log('[id]', id)
Expand Down
141 changes: 141 additions & 0 deletions lib/metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const { WebRTCStats } = require('@peermetrics/webrtc-stats')
const { snakeCase } = require('change-case')
const { v4 } = require('uuid')
const wrtc = require('wrtc')

function stats2metrics (stats) {
// const localAudioMetricList = [
// 'packetsSent',
// 'retransmittedPacketsSent',
// 'bytesSent',
// 'headerBytesSent',
// 'retransmittedBytesSent',
// 'bitrate',
// 'packetRate'
// ]
const remoteAudioMetricList = [
'packetsReceived',
'bytesReceived',
'headerBytesReceived',
'packetsLost',
'jitter',
'jitterBufferDelay',
'jitterBufferEmittedCount',
'audioLevel',
'totalAudioEnergy',
'totalSamplesReceived',
'totalSamplesDuration',
'bitrate',
'packetRate'
]
// const localVideoMetricList = [
// 'firCount',
// 'pliCount',
// 'nackCount',
// 'qpSum',
// 'packetsSent',
// 'retransmittedPacketsSent',
// 'bytesSent',
// 'headerBytesSent',
// 'retransmittedBytesSent',
// 'framesEncoded',
// 'keyFramesEncoded',
// 'totalEncodeTime',
// 'totalEncodedBytesTarget',
// 'totalPacketSendDelay',
// 'qualityLimitationReason',
// 'qualityLimitationResolutionChanges',
// 'frameWidth',
// 'frameHeight',
// 'framesSent',
// 'hugeFramesSent',
// 'bitrate',
// 'packetRate'
// ]
const remoteVideoMetricList = [
'firCount',
'pliCount',
'nackCount',
'qpSum',
'packetsReceived',
'bytesReceived',
'headerBytesReceived',
'packetsLost',
'framesDecoded',
'keyFramesDecoded',
'totalDecodeTime',
'totalInterFrameDelay',
'totalSquaredInterFrameDelay',
'jitterBufferDelay',
'jitterBufferEmittedCount',
'frameWidth',
'frameHeight',
'framesReceived',
'framesDropped',
'bitrate',
'packetRate'
]
const connectionMetricList = [
'bytesSent',
'bytesReceived',
'totalRoundTripTime',
'currentRoundTripTime',
'availableOutgoingBitrate'
]

const metricBaseName = 'apps.wrtc-agent.pc'
// const localAudioMetricName = `${metricBaseName}.audio.local`
// const localAudioMetrics = localAudioMetricList.map(_ => ({
// metric: `${localAudioMetricName}.${snakeCase(_)}`,
// value: stats.data.audio.local[_] || 0
// }))
const remoteAudioMetricName = `${metricBaseName}.audio.remote`
const remoteAudioMetrics = remoteAudioMetricList.map(_ => ({
metric: `${remoteAudioMetricName}.${snakeCase(_)}`,
value: stats.data.audio.remote[_] || 0
}))
// const localVideoMetricName = `${metricBaseName}.video.local`
// const localVideoMetrics = localVideoMetricList.map(_ => ({
// metric: `${localVideoMetricName}.${snakeCase(_)}`,
// value: stats.data.video.local[_] || 0
// }))
const remoteVideoMetricName = `${metricBaseName}.video.remote`
const remoteVideoMetrics = remoteVideoMetricList.map(_ => ({
metric: `${remoteVideoMetricName}.${snakeCase(_)}`,
value: stats.data.video.remote[_] || 0
}))
const connectionMetricName = `${metricBaseName}.connection`
const connectionMetrics = connectionMetricList.map(_ => ({
metric: `${connectionMetricName}.${snakeCase(_)}`,
value: stats.data.connection[_] || 0
}))

return [
// ...localAudioMetrics,
...remoteAudioMetrics,
// ...localVideoMetrics,
...remoteVideoMetrics,
...connectionMetrics
]
}

function createPeerStatsMonitor (peer, interval, statsHandler) {
const p = new WebRTCStats({
getStatsInterval: interval,
wrtc
})

p.on('stats', statsHandler)

p.addPeer({
pc: peer,
peerId: v4()
})

return p
}

module.exports = {
createPeerStatsMonitor,
stats2metrics
}
Loading

0 comments on commit 74fb72f

Please sign in to comment.