Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
31e1d08
feat: Add cryptex to SDP/Jingle translation. (#2978)
bgrozev Feb 17, 2026
6912eed
feat(connection): Adds function to refresh token. (#2977)
damencho Feb 20, 2026
92c8c18
fix(authentication): Clears connection on auth error.
damencho Feb 27, 2026
1a3c35c
feat(quality) Fires analytics events when video decode fails. (#2983)
jallamsetty1 Mar 3, 2026
c279d01
fix(JitsiMeetJS) Adds release pinning support when using joinConferen…
jallamsetty1 Mar 3, 2026
17e2281
chore(deps) Bumps rtcstats to 9.7.1 (#2988)
jallamsetty1 Mar 4, 2026
0c6afe8
fix(chat): Check match results to avoid errors.
damencho Mar 6, 2026
084a5a9
feat(connectivity) Adds stats based video freeze detection.
jallamsetty1 Mar 12, 2026
2fd6423
fix(JitsiLocalTrack) Fixes _effectEnabled to detect hardware failures
4rayaditya Mar 20, 2026
cd9f116
feat(xmpp): pass all user identity fields from JWT context (#2992)
mihhu Mar 23, 2026
fe26afb
fix(JingleSessionPC) Falls back to JVB conn from P2P when SSRCs are r…
jallamsetty1 Mar 23, 2026
6283ab1
fix: correct comment for AV_MODERATION_PARTICIPANT_REJECTED event
Alok-work23 Mar 26, 2026
19ad82c
fix(xmpp): add XEP-0461 namespace check in _parseReplyMessage
Alok-work23 Mar 26, 2026
733e66c
fix(recording): On missing session id continue to send stop.
damencho Mar 26, 2026
94d0e53
feat(audio): Add opus bitrate when toggling stereo/mono
hlvhe Apr 9, 2026
94bdbc7
fix: Fixes type error when build in jitsi-meet.
damencho Apr 16, 2026
5076104
Pin GitHub Actions to SHA hashes (#3020)
bgrozev Apr 16, 2026
df9bfa9
fix(RTC): Strip sdes:mid header extension from remote SDP on Firefox
jallamsetty1 Apr 16, 2026
98f8d2e
feat(metadata): Moves reading turn information from metadata. (#3014)
damencho Apr 20, 2026
363f750
fix: Drop scheduling ws keep-alive on shard set.
damencho Apr 21, 2026
ac96290
chore(deps): bump uuid from 8.3.2 to 14.0.0
dependabot[bot] Apr 23, 2026
691b167
chore(deps): bump lodash from 4.17.21 to 4.18.1
dependabot[bot] Apr 23, 2026
8edb566
chore(deps): bump lodash-es from 4.17.21 to 4.18.1
dependabot[bot] Apr 23, 2026
82d339e
feat(RTCStats): Report getUserMedia errors via RTCStats
jallamsetty1 Apr 27, 2026
e92bbe7
Merge remote-tracking branch 'upstream/master' into bring_updates
TamaraFinogina Apr 28, 2026
f7923eb
add missing lock.json change
TamaraFinogina Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
Expand All @@ -31,11 +31,11 @@ jobs:
npm install
npm run typedoc
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: 'docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
2 changes: 1 addition & 1 deletion .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8
with:
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
Expand Down
36 changes: 34 additions & 2 deletions JitsiConference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ export default class JitsiConference extends Listenable {
this._onIceConnectionRestored = this._onIceConnectionRestored.bind(this);
this.room.addListener(XMPPEvents.CONNECTION_RESTORED, this._onIceConnectionRestored);

this._onP2PTerminationRequired = this._onP2PTerminationRequired.bind(this);
this.room.addListener(XMPPEvents.P2P_TERMINATION_REQUIRED, this._onP2PTerminationRequired);

this._updateProperties = this._updateProperties.bind(this);
this.room.addListener(XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, this._updateProperties);

Expand Down Expand Up @@ -828,7 +831,8 @@ export default class JitsiConference extends Listenable {
Statistics.sendAnalytics(createConferenceEvent('joined', {
...connectionTimes,
meetingId,
participantId: `${meetingId}.${this._statsCurrentId}`
participantId: `${meetingId}.${this._statsCurrentId}`,
supportsTurnInRoomMetadata: true
}));
this._conferenceJoinAnalyticsEventSent = Date.now();
}
Expand Down Expand Up @@ -1810,6 +1814,26 @@ export default class JitsiConference extends Listenable {
}
}

/**
* Handles P2P_TERMINATION_REQUIRED event. Fired when a source-remove is detected on a P2P connection, which
* indicates that the browser has regenerated SSRCs for an existing source. The P2P session is stopped so the
* call falls back to JVB.
*
* @param {JingleSessionPC} session - The P2P Jingle session.
* @private
*/
private _onP2PTerminationRequired(session: JingleSessionPC): void {
if (session !== this.p2pJingleSession) {
return;
}

logger.warn('Stopping P2P session due to SSRC regeneration on P2P connection');
this._stopP2PSession({
reason: 'connectivity-error',
reasonDescription: 'P2P SSRC regeneration'
});
}

/**
* Handles CONNECTION_RESTORED event.
* @param {JingleSessionPC} session - The Jingle session.
Expand Down Expand Up @@ -2565,6 +2589,7 @@ export default class JitsiConference extends Listenable {
);

room.removeListener(XMPPEvents.MEETING_ID_SET, this._sendConferenceJoinAnalyticsEvent);
room.removeListener(XMPPEvents.P2P_TERMINATION_REQUIRED, this._onP2PTerminationRequired);
room.removeListener(XMPPEvents.SESSION_ACCEPT, this._updateRoomPresence);
room.removeListener(XMPPEvents.SOURCE_ADD, this._updateRoomPresence);
room.removeListener(XMPPEvents.SOURCE_ADD_ERROR, this._removeLocalSourceOnReject);
Expand Down Expand Up @@ -4433,7 +4458,14 @@ export default class JitsiConference extends Listenable {
*/
public joinLobby(displayName: string, email: string): Promise<void> {
if (this.room) {
return this.room.getLobby().join(displayName, email);
if (!this.room.getLobby()?.lobbyRoom?.joined) {
return this.room.getLobby().join(displayName, email);
} else {
logger.warn('Already joined the lobby');

return Promise.resolve();
}

}

return Promise.reject(new Error('The conference not started'));
Expand Down
11 changes: 11 additions & 0 deletions JitsiConferenceEventManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import JitsiRemoteTrack from './modules/RTC/JitsiRemoteTrack';
import TraceablePeerConnection from './modules/RTC/TraceablePeerConnection';
import RTCStats from './modules/RTCStats/RTCStats';
import { RTCStatsEvents } from './modules/RTCStats/RTCStatsEvents';
import { IInboundVideoStats } from './modules/qualitycontrol/QualityController';
import JibriSession from './modules/recording/JibriSession';
import { SPEAKERS_AUDIO_LEVELS } from './modules/statistics/constants';
import Statistics from './modules/statistics/statistics';
Expand Down Expand Up @@ -476,6 +477,11 @@ export default class JitsiConferenceEventManager {
if (typeof metadata.recording?.isTranscribingEnabled !== 'undefined') {
conference._setTranscribingEnabled(Boolean(metadata.recording.isTranscribingEnabled));
}

if (metadata.services) {
chatRoom.xmpp.connection.jingle.onReceiveStunAndTurnCredentials(metadata);
}

conference.eventEmitter.emit(JitsiConferenceEvents.METADATA_UPDATED, metadata);
});
}
Expand Down Expand Up @@ -621,6 +627,11 @@ export default class JitsiConferenceEventManager {
JitsiConferenceEvents.ENCODE_TIME_STATS_RECEIVED, tpc, stats);
});

conference.statistics.addInboundVideoStatsListener((tpc: TraceablePeerConnection, stats: Map<number, IInboundVideoStats>) => {
conference.eventEmitter.emit(
JitsiConferenceEvents.INBOUND_VIDEO_STATS_RECEIVED, tpc, stats);
});

// if we are in startSilent mode we will not be sending/receiving so nothing to detect
if (!conference.options.config.startSilent) {
conference.statistics.addByteSentStatsListener((tpc: TraceablePeerConnection, stats: RTCEncodedAudioFrameMetadata) => {
Expand Down
10 changes: 10 additions & 0 deletions JitsiConferenceEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ export enum JitsiConferenceEvents {
*/
FORWARDED_SOURCES_CHANGED = 'conference.forwardedSourcesChanged',

/**
* Indicates that inbound video stats have been received for remote video streams that are receiving
* media bytes but decoding no frames. Fired when at least one such stream exists, and once more with an
* empty map when all previously-failing streams have recovered (all-clear signal).
*
* @param {TraceablePeerConnection} tpc - The peer connection.
* @param {Map<number, { bitrateDownload: number, fps: number, participantId: string }>} stats - Stats per SSRC.
*/
INBOUND_VIDEO_STATS_RECEIVED = 'conference.inbound_video_stats_received',

/**
* You are kicked from the conference.
* @param {JitsiParticipant} the participant that initiated the kick.
Expand Down
4 changes: 3 additions & 1 deletion JitsiConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ export default class JitsiConnection {
* This method allows renewal of the tokens if they are expiring.
* @param token - The new token.
*/
setToken(token: string): void {
refreshToken(token: string): Promise<void> {
this.token = token;

return this._xmpp.refreshToken(this.token);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions JitsiConnectionEvents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe( "/JitsiConnectionEvents members", () => {
CONNECTION_ESTABLISHED,
CONNECTION_FAILED,
CONNECTION_REDIRECTED,
CONNECTION_TOKEN_EXPIRED,
DISPLAY_NAME_REQUIRED,
PROPERTIES_UPDATED,
JitsiConnectionEvents,
Expand All @@ -19,6 +20,7 @@ describe( "/JitsiConnectionEvents members", () => {
expect( CONNECTION_ESTABLISHED ).toBe( 'connection.connectionEstablished' );
expect( CONNECTION_FAILED ).toBe( 'connection.connectionFailed' );
expect( CONNECTION_REDIRECTED ).toBe( 'connection.redirected' );
expect( CONNECTION_TOKEN_EXPIRED ).toBe( 'connection.token_expired' );
expect( DISPLAY_NAME_REQUIRED ).toBe( 'connection.display_name_required' );
expect( PROPERTIES_UPDATED ).toBe( 'connection.propertiesUpdated' );

Expand All @@ -28,6 +30,7 @@ describe( "/JitsiConnectionEvents members", () => {
expect( JitsiConnectionEvents.CONNECTION_ESTABLISHED ).toBe( 'connection.connectionEstablished' );
expect( JitsiConnectionEvents.CONNECTION_FAILED ).toBe( 'connection.connectionFailed' );
expect( JitsiConnectionEvents.CONNECTION_REDIRECTED ).toBe( 'connection.redirected' );
expect( JitsiConnectionEvents.CONNECTION_TOKEN_EXPIRED ).toBe( 'connection.token_expired' );
expect( JitsiConnectionEvents.DISPLAY_NAME_REQUIRED ).toBe( 'connection.display_name_required' );
expect( JitsiConnectionEvents.PROPERTIES_UPDATED ).toBe( 'connection.propertiesUpdated' );
} );
Expand Down
6 changes: 6 additions & 0 deletions JitsiConnectionEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export enum JitsiConnectionEvents {
*/
CONNECTION_REDIRECTED = 'connection.redirected',

/**
* The connection is trying to resume, but token is expired.
*/
CONNECTION_TOKEN_EXPIRED = 'connection.token_expired',

/**
* Indicates that the display name is required over this connection and need to be supplied when
* joining the room.
Expand All @@ -59,5 +64,6 @@ export const CONNECTION_DISCONNECTED = JitsiConnectionEvents.CONNECTION_DISCONNE
export const CONNECTION_ESTABLISHED = JitsiConnectionEvents.CONNECTION_ESTABLISHED;
export const CONNECTION_FAILED = JitsiConnectionEvents.CONNECTION_FAILED;
export const CONNECTION_REDIRECTED = JitsiConnectionEvents.CONNECTION_REDIRECTED;
export const CONNECTION_TOKEN_EXPIRED = JitsiConnectionEvents.CONNECTION_TOKEN_EXPIRED;
export const DISPLAY_NAME_REQUIRED = JitsiConnectionEvents.DISPLAY_NAME_REQUIRED;
export const PROPERTIES_UPDATED = JitsiConnectionEvents.PROPERTIES_UPDATED;
10 changes: 8 additions & 2 deletions JitsiMeetJS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export interface IJoinConferenceOptions {
conferenceOptions?: IConferenceOptions;
connectionOptions?: IConnectionOptions;
jaas?: {
release?: boolean;
release?: string; // backend release "1234"
useStaging?: boolean;
};
tracks?: JitsiLocalTrack[];
Expand Down Expand Up @@ -460,6 +460,12 @@ const JitsiMeetJS = {

const useStage = options.jaas?.useStaging ?? false;
const jaasDomain = useStage ? 'staging.8x8.vc' : '8x8.vc';
let serviceUrl = `wss://${jaasDomain}/${appId}/xmpp-websocket?room=${roomName}`;

if (options.jaas?.release) {
serviceUrl = `${serviceUrl}&release=release-${options.jaas.release}`;
}

const opts = {
analytics: {
rtcstatsEnabled: true,
Expand All @@ -471,7 +477,7 @@ const JitsiMeetJS = {
domain: jaasDomain,
muc: `conference.${appId}.${jaasDomain}`
},
serviceUrl: `wss://${jaasDomain}/${appId}/xmpp-websocket?room=${roomName}`,
serviceUrl,
websocketKeepAliveUrl: `https://${jaasDomain}/${appId}/_unlock?room=${roomName}`
};

Expand Down
16 changes: 10 additions & 6 deletions modules/RTC/JitsiLocalTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
this._originalStream = this.stream;
this._setStream(this._streamEffect.startEffect(this._originalStream));
this.track = this.stream.getTracks()[0];
this._effectEnabled = true;
}

/**
Expand All @@ -642,6 +643,7 @@ export default class JitsiLocalTrack extends JitsiTrack {
this._setStream(this._originalStream);
this._originalStream = null;
this.track = this.stream ? this.stream.getTracks()[0] : null;
this._effectEnabled = false;
}
}

Expand Down Expand Up @@ -1210,20 +1212,22 @@ export default class JitsiLocalTrack extends JitsiTrack {
this.stopStream();

const initialSettings = this.track.getSettings();
const constraintsToApply = {
...initialSettings,
...constraints
};

const deviceId = initialSettings?.deviceId;
const deviceIdKey = mediaType === MediaType.AUDIO ? 'micDeviceId' : 'cameraDeviceId';
let mediaStreamData: IStreamInfo | undefined;

try {
logger.debug(`applyConstraints for track ${this} with constraints: ${JSON.stringify(constraints)}`);

const constraintsToApply = {
...initialSettings,
...constraints
};

[ mediaStreamData ] = await RTCUtils.obtainAudioAndVideoPermissions({
constraints: { [mediaType]: constraintsToApply },
[deviceIdKey]: constraintsToApply.deviceId,
...(deviceId && { [deviceIdKey]: deviceId }),
devices: [ mediaType ]
});

Expand All @@ -1241,7 +1245,7 @@ export default class JitsiLocalTrack extends JitsiTrack {

[ mediaStreamData ] = await RTCUtils.obtainAudioAndVideoPermissions({
constraints: { [mediaType]: initialSettings },
[deviceIdKey]: constraintsToApply.deviceId,
...(deviceId && { [deviceIdKey]: deviceId }),
devices: [ mediaType ]
});
}
Expand Down
22 changes: 21 additions & 1 deletion modules/RTC/RTCUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { RTCEvents } from '../../service/RTC/RTCEvents';
import Resolutions from '../../service/RTC/Resolutions';
import { VideoType } from '../../service/RTC/VideoType';
import { AnalyticsEvents } from '../../service/statistics/AnalyticsEvents';
import RTCStats from '../RTCStats/RTCStats';
import { RTCStatsEvents } from '../RTCStats/RTCStatsEvents';
import browser from '../browser';
import Statistics from '../statistics/statistics';
import Listenable from '../util/Listenable';
Expand Down Expand Up @@ -399,7 +401,16 @@ class RTCUtils extends Listenable {
gumTimeout = setTimeout(() => {
timeoutExpired = true;
gumTimeout = undefined;
reject(new JitsiTrackError(JitsiTrackErrors.TIMEOUT));

const timeoutError = new JitsiTrackError(JitsiTrackErrors.TIMEOUT);

RTCStats.sendStatsEntry(RTCStatsEvents.GET_USER_MEDIA_ERROR_EVENT, null, {
devices: umDevices,
message: timeoutError.message,
name: timeoutError.name
});

reject(timeoutError);
}, timeout);
}

Expand All @@ -419,6 +430,15 @@ class RTCUtils extends Listenable {
const jitsiError = new JitsiTrackError(error, constraints, umDevices);

if (!timeoutExpired) {
// Skip emitting when the timeout already fired and reported the failure,
// to avoid double-reporting a single gUM call.
RTCStats.sendStatsEntry(RTCStatsEvents.GET_USER_MEDIA_ERROR_EVENT, null, {
constraint: error.constraint,
devices: umDevices,
message: error.message,
name: error.name
});

if (typeof gumTimeout !== 'undefined') {
clearTimeout(gumTimeout);
}
Expand Down
Loading
Loading