|
| 1 | +// @ts-check |
| 2 | +'use strict'; |
| 3 | + |
| 4 | +const { filterObject } = require('../../util'); |
| 5 | + |
| 6 | +/** @typedef {Object} IceCandidatePayload |
| 7 | + * Definition for local and remote ICE candidate telemetry payloads |
| 8 | + * @property {string} [candidateType] - Alias for RTCIceCandidate.type |
| 9 | + * @property {string} [ip] - Alias for RTCIceCandidate.address |
| 10 | + * @property {number} [port] |
| 11 | + * @property {number} [priority] |
| 12 | + * @property {string} [protocol] |
| 13 | + * @property {string} [relatedAddress] |
| 14 | + * @property {number} [relatedPort] |
| 15 | + * @property {string} [tcpType] |
| 16 | + * @property {string} [transportId] - Alias for RTCIceCandidate.sdpMid |
| 17 | + * @property {number} [networkCost] |
| 18 | +*/ |
| 19 | + |
| 20 | +/** |
| 21 | + * PeerConnection telemetry events |
| 22 | + * @internal |
| 23 | + */ |
| 24 | +class PeerConnectionEvents { |
| 25 | + /** |
| 26 | + * @param {import('../telemetry')} telemetry - The telemetry instance |
| 27 | + */ |
| 28 | + constructor(telemetry) { |
| 29 | + this._telemetry = telemetry; |
| 30 | + } |
| 31 | + |
| 32 | + /** |
| 33 | + * Emit connection state change event |
| 34 | + * @param {string} peerConnectionId - Peer connection identifier |
| 35 | + * @param {('new'|'connecting'|'connected'|'disconnected'|'failed'|'closed')} state - Connection state |
| 36 | + * @returns {void} |
| 37 | + */ |
| 38 | + connectionState(peerConnectionId, state) { |
| 39 | + this._telemetry.debug({ |
| 40 | + group: 'pc-connection-state', |
| 41 | + name: state, |
| 42 | + payload: { peerConnectionId } |
| 43 | + }); |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Emit signaling state change event |
| 48 | + * @param {string} peerConnectionId - Peer connection identifier |
| 49 | + * @param {('stable'|'have-local-offer'|'have-remote-offer'|'have-local-pranswer'|'have-remote-pranswer'|'closed')} state - Signaling state |
| 50 | + * @returns {void} |
| 51 | + */ |
| 52 | + signalingState(peerConnectionId, state) { |
| 53 | + this._telemetry.debug({ |
| 54 | + group: 'pc-signaling-state', |
| 55 | + name: state, |
| 56 | + payload: { peerConnectionId } |
| 57 | + }); |
| 58 | + } |
| 59 | + |
| 60 | + /** |
| 61 | + * Emit ice gathering state change event |
| 62 | + * @param {string} peerConnectionId - Peer connection identifier |
| 63 | + * @param {('new'|'gathering'|'complete')} state - Ice gathering state |
| 64 | + * @returns {void} |
| 65 | + */ |
| 66 | + iceGatheringState(peerConnectionId, state) { |
| 67 | + this._telemetry.debug({ |
| 68 | + group: 'ice-gathering-state', |
| 69 | + name: state, |
| 70 | + payload: { peerConnectionId } |
| 71 | + }); |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Emit ice connection state change event |
| 76 | + * @param {string} peerConnectionId - Peer connection identifier |
| 77 | + * @param {('new'|'checking'|'connected'|'completed'|'disconnected'|'failed'|'closed')} state - Ice connection state |
| 78 | + * @returns {void} |
| 79 | + */ |
| 80 | + iceConnectionState(peerConnectionId, state) { |
| 81 | + const level = state === 'failed' ? 'error' : 'debug'; |
| 82 | + this._telemetry[level]({ |
| 83 | + group: 'ice-connection-state', |
| 84 | + name: state, |
| 85 | + payload: { peerConnectionId } |
| 86 | + }); |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Emit DTLS transport state change event |
| 91 | + * @param {string} peerConnectionId - Peer connection identifier |
| 92 | + * @param {('new'|'connecting'|'connected'|'closed'|'failed')} state - DTLS transport state |
| 93 | + * @returns {void} |
| 94 | + */ |
| 95 | + dtlsTransportState(peerConnectionId, state) { |
| 96 | + const level = state === 'failed' ? 'error' : 'debug'; |
| 97 | + this._telemetry[level]({ |
| 98 | + group: 'dtls-transport-state', |
| 99 | + name: state, |
| 100 | + payload: { peerConnectionId } |
| 101 | + }); |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * Emit a telemetry event when a new ICE candidate is added |
| 106 | + * @param {string} peerConnectionId - The peer connection identifier |
| 107 | + * @param {RTCIceCandidate} iceCandidate - The native RTCIceCandidate object |
| 108 | + * @param {boolean} [isRemote = false] - Whether the candidate is remote |
| 109 | + * @returns {void} |
| 110 | + */ |
| 111 | + iceCandidate(peerConnectionId, iceCandidate, isRemote = false) { |
| 112 | + this._telemetry.debug({ |
| 113 | + group: 'ice-candidate', |
| 114 | + name: 'ice-candidate', |
| 115 | + payload: { |
| 116 | + peerConnectionId, |
| 117 | + isRemote: isRemote ? 'true' : 'false', |
| 118 | + ...this._toIceCandidatePayload(iceCandidate) |
| 119 | + } |
| 120 | + }); |
| 121 | + } |
| 122 | + |
| 123 | + /** |
| 124 | + * Emit a telemetry event when the selected ICE candidate pair changes |
| 125 | + * @param {string} peerConnectionId - The peer connection identifier |
| 126 | + * @param {RTCIceCandidatePair} pair - The selected candidate pair |
| 127 | + * @returns {void} |
| 128 | + */ |
| 129 | + selectedCandidatePair(peerConnectionId, pair) { |
| 130 | + this._telemetry.debug({ |
| 131 | + group: 'ice-candidate', |
| 132 | + name: 'selected-ice-candidate-pair', |
| 133 | + payload: filterObject({ |
| 134 | + peerConnectionId, |
| 135 | + localCandidate: pair.local ? this._toIceCandidatePayload(pair.local) : null, |
| 136 | + remoteCandidate: pair.remote ? this._toIceCandidatePayload(pair.remote) : null |
| 137 | + }, null) |
| 138 | + }); |
| 139 | + } |
| 140 | + |
| 141 | + /** |
| 142 | + * Convert RTCIceCandidate to telemetry payload format |
| 143 | + * @private |
| 144 | + * @param {RTCIceCandidate} iceCandidate - The RTCIceCandidate object |
| 145 | + * @returns {IceCandidatePayload} The telemetry payload format |
| 146 | + */ |
| 147 | + _toIceCandidatePayload(iceCandidate) { |
| 148 | + const match = iceCandidate.candidate?.match(/network-cost\s+(\d+)/); |
| 149 | + const networkCost = match ? parseInt(match[1], 10) : null; |
| 150 | + |
| 151 | + return filterObject({ |
| 152 | + 'candidateType': iceCandidate.type, |
| 153 | + 'ip': iceCandidate.address, |
| 154 | + 'port': iceCandidate.port, |
| 155 | + 'priority': iceCandidate.priority, |
| 156 | + 'protocol': iceCandidate.protocol, |
| 157 | + 'relatedAddress': iceCandidate.relatedAddress, |
| 158 | + 'relatedPort': iceCandidate.relatedPort, |
| 159 | + 'tcpType': iceCandidate.tcpType, |
| 160 | + 'transportId': iceCandidate.sdpMid, |
| 161 | + 'networkCost': networkCost |
| 162 | + }, null); |
| 163 | + } |
| 164 | +} |
| 165 | + |
| 166 | +module.exports = PeerConnectionEvents; |
0 commit comments