Skip to content

Commit 14df32f

Browse files
authored
Merge pull request #2193 from twilio/prep-2.34.0
Prep 2.34.0 release
2 parents 8796166 + 1c77fe1 commit 14df32f

18 files changed

Lines changed: 972 additions & 557 deletions

File tree

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ commands:
103103
key: dependency-cache-{{ checksum "package.json" }}
104104
- run:
105105
name: Installing dependencies
106-
command: npm install --legacy-peer-deps
106+
command: npm install
107107
- save_cache:
108108
key: dependency-cache-{{ checksum "package.json" }}
109109
paths:

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@ The Twilio Programmable Video SDKs use [Semantic Versioning](http://www.semver.o
22

33
**Version 1.x reached End of Life on September 8th, 2021.** See the changelog entry [here](https://www.twilio.com/changelog/end-of-life-complete-for-unsupported-versions-of-the-programmable-video-sdk). Support for the 1.x version ended on December 4th, 2020.
44

5+
2.34.0 (January 8, 2026)
6+
====================
7+
8+
New Features
9+
------------
10+
### Enhanced Telemetry
11+
- Added new telemetry events to enhance monitoring and diagnostics for the following media-related states:
12+
- [RTCPeerConnectionState](https://w3c.github.io/webrtc-pc/#rtcpeerconnectionstate-enum)
13+
- [RTCSignalingState](https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum)
14+
- [RTCIceGatheringState](https://w3c.github.io/webrtc-pc/#rtcicegatheringstate-enum)
15+
- [RTCIceConnectionState](https://w3c.github.io/webrtc-pc/#rtciceconnectionstate-enum)
16+
17+
- Added room lifecycle telemetry events: reconnecting, reconnected, and disconnected.
18+
19+
Changes
20+
-------
21+
- Replaced SDP munging for codec preferences with native `RTCRtpTransceiver.setCodecPreferences()` API.
22+
523
2.33.0 (November 6, 2025)
624
====================
725

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Releases of twilio-video.js are hosted on a CDN, and you can include these
7474
directly in your web app using a <script> tag.
7575

7676
```html
77-
<script src="//sdk.twilio.com/js/video/releases/2.33.0/twilio-video.min.js"></script>
77+
<script src="//sdk.twilio.com/js/video/releases/2.34.0/twilio-video.min.js"></script>
7878
```
7979

8080
Using this method, twilio-video.js will set a browser global:

lib/connect.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,12 @@ function connect(token, options) {
421421
log.info('Room name:', room.name);
422422
log.debug('Room:', room);
423423
room.once('disconnected', () => {
424-
telemetry.reset();
425-
eventPublisher.disconnect();
424+
// NOTE(lrivas): Allow pending state change events to be emitted before disconnecting telemetry.
425+
setTimeout(() => eventPublisher.disconnect(), 50);
426426
});
427427
return room;
428428
}, error => {
429-
telemetry.reset();
430-
eventPublisher.disconnect();
429+
setTimeout(() => eventPublisher.disconnect(), 50);
431430
if (cancelableRoomPromise._isCanceled) {
432431
log.info('Attempt to connect to a Room was canceled');
433432
} else {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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;

lib/insights/events/room.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use strict';
2+
3+
const { MediaConnectionError } = require('../../util/twilio-video-errors');
4+
5+
/**
6+
* @typedef {import('../../util/twilio-video-errors').MediaConnectionError} MediaConnectionError
7+
* @typedef {import('../../util/twilio-video-errors').SignalingConnectionDisconnectedError} SignalingConnectionDisconnectedError
8+
*/
9+
10+
/**
11+
* Room telemetry events
12+
* @internal
13+
*/
14+
class RoomEvents {
15+
/**
16+
* @param {import('../telemetry')} telemetry - The telemetry instance
17+
*/
18+
constructor(telemetry) {
19+
this._telemetry = telemetry;
20+
}
21+
22+
/**
23+
* Emit when room enters reconnecting state
24+
* @param {MediaConnectionError|SignalingConnectionDisconnectedError} error - The error that caused reconnection
25+
* @returns {void}
26+
*/
27+
reconnecting(error) {
28+
const reason = error instanceof MediaConnectionError ? 'media' : 'signaling';
29+
this._telemetry.info({
30+
group: 'room',
31+
name: 'reconnecting',
32+
payload: {
33+
reason
34+
}
35+
});
36+
}
37+
38+
/**
39+
* Emit when room successfully reconnected
40+
* @returns {void}
41+
*/
42+
reconnected() {
43+
this._telemetry.info({
44+
group: 'room',
45+
name: 'reconnected'
46+
});
47+
}
48+
49+
/**
50+
* Emit when room disconnected
51+
* @returns {void}
52+
*/
53+
disconnected() {
54+
this._telemetry.info({
55+
group: 'room',
56+
name: 'disconnected'
57+
});
58+
}
59+
}
60+
61+
module.exports = RoomEvents;

0 commit comments

Comments
 (0)