feat(rtc-stats): support for react native and breakout rooms (#11835)
* feat(rtc-stats): mobile flow * fix(rtc-stats): create websocket connection event * feat(rtc-stats): separate middlewares and filter callstats pcs * fix: linting problems * fix: linting problems 2 * fix(rtc-stats): middlewares * ref(rtc-stats): rewrite in typescript * remove blank line and flow tag * fix: linting problems * remove redundant comment * remove index file * fix: sort interface keys * feat(rtc-stats): support for breakout rooms * ref(rtc-stats): send isBreakoutRoom flag when connecting to rtc stats * code review * fix(rtc-stats): rebase issues * remove empty lines * chore(rtc-stats): update rtc-stats to latest version * fix: linting issues
This commit is contained in:
parent
06842c724a
commit
9323b86e3c
|
@ -30,7 +30,7 @@
|
|||
"@jitsi/js-utils": "2.0.3",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
"@jitsi/rnnoise-wasm": "0.1.0",
|
||||
"@jitsi/rtcstats": "9.2.0",
|
||||
"@jitsi/rtcstats": "9.3.0",
|
||||
"@material-ui/core": "4.11.3",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
|
@ -3481,9 +3481,9 @@
|
|||
"integrity": "sha512-JujivPbOUvdRYa2xqByHYKfKGNGa7ZPyNLaNuh8hEp9XsiNfjaJAHdboq6M1VY9TP+765nyxC0LjpAw1VkikOQ=="
|
||||
},
|
||||
"node_modules/@jitsi/rtcstats": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.2.0.tgz",
|
||||
"integrity": "sha512-bGQRLFio25++bi3s0em0xKD+WIqhDwg8OQ71K4BXZNOVL3eVX3R/bxbSEok6UYjFQxwoVjfdM4J8cHUu7RwrQw==",
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.3.0.tgz",
|
||||
"integrity": "sha512-aipr1Tt/vfouMmgISCSu64Np3pD1u51y/2SztYNDt5bd6f79Qrieceu0JFqZWxC9KQRsamoJL7Mb9qxo2KkULg==",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "^2.0.0",
|
||||
"sdp": "^3.0.3",
|
||||
|
@ -22421,9 +22421,9 @@
|
|||
"integrity": "sha512-JujivPbOUvdRYa2xqByHYKfKGNGa7ZPyNLaNuh8hEp9XsiNfjaJAHdboq6M1VY9TP+765nyxC0LjpAw1VkikOQ=="
|
||||
},
|
||||
"@jitsi/rtcstats": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.2.0.tgz",
|
||||
"integrity": "sha512-bGQRLFio25++bi3s0em0xKD+WIqhDwg8OQ71K4BXZNOVL3eVX3R/bxbSEok6UYjFQxwoVjfdM4J8cHUu7RwrQw==",
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@jitsi/rtcstats/-/rtcstats-9.3.0.tgz",
|
||||
"integrity": "sha512-aipr1Tt/vfouMmgISCSu64Np3pD1u51y/2SztYNDt5bd6f79Qrieceu0JFqZWxC9KQRsamoJL7Mb9qxo2KkULg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "^2.0.0",
|
||||
"sdp": "^3.0.3",
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"@jitsi/js-utils": "2.0.3",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
"@jitsi/rnnoise-wasm": "0.1.0",
|
||||
"@jitsi/rtcstats": "9.2.0",
|
||||
"@jitsi/rtcstats": "9.3.0",
|
||||
"@material-ui/core": "4.11.3",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "3.0.1",
|
||||
|
|
|
@ -110,6 +110,9 @@ function _addConferenceListeners(conference, dispatch, state) {
|
|||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_JOINED,
|
||||
(...args) => dispatch(conferenceJoined(conference, ...args)));
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET,
|
||||
(...args) => dispatch(conferenceUniqueIdSet(conference, ...args)));
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS,
|
||||
(...args) => dispatch(conferenceJoinInProgress(conference, ...args)));
|
||||
|
|
|
@ -100,6 +100,8 @@ export interface IConfig {
|
|||
rtcstatsEnabled?: boolean;
|
||||
rtcstatsEndpoint?: string;
|
||||
rtcstatsPollInterval?: number;
|
||||
rtcstatsSendSdp?: boolean;
|
||||
rtcstatsUseLegacy?: boolean;
|
||||
scriptURLs?: Array<string>;
|
||||
};
|
||||
apiLogLevels?: Array<'warn' | 'log' | 'error' | 'info' | 'debug'>;
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
/* eslint-disable import/order */
|
||||
// @ts-ignore
|
||||
import rtcstatsInit from '@jitsi/rtcstats/rtcstats';
|
||||
|
||||
// @ts-ignore
|
||||
import traceInit from '@jitsi/rtcstats/trace-ws';
|
||||
|
||||
import {
|
||||
createRTCStatsTraceCloseEvent,
|
||||
sendAnalytics
|
||||
} from '../analytics';
|
||||
// @ts-ignore
|
||||
import { createRTCStatsTraceCloseEvent, sendAnalytics } from '../analytics';
|
||||
|
||||
import logger from './logger';
|
||||
import {
|
||||
DominantSpeakerData,
|
||||
E2ERTTData,
|
||||
FaceLandmarksData,
|
||||
InitOptions,
|
||||
VideoTypeData
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Filter out RTCPeerConnection that are created by callstats.io.
|
||||
|
@ -14,10 +23,10 @@ import logger from './logger';
|
|||
* @param {*} config - Config object sent to the PC c'tor.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function connectionFilter(config) {
|
||||
function connectionFilter(config: any) {
|
||||
if (config && config.iceServers[0] && config.iceServers[0].urls) {
|
||||
for (const iceUrl of config.iceServers[0].urls) {
|
||||
if (iceUrl.indexOf('taas.callstats.io') >= 0) {
|
||||
if (iceUrl.indexOf('callstats.io') >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +38,9 @@ function connectionFilter(config) {
|
|||
* initialized once.
|
||||
*/
|
||||
class RTCStats {
|
||||
trace: any;
|
||||
initialized = false;
|
||||
|
||||
/**
|
||||
* Initialize the rtcstats components. First off we initialize the trace, which is a wrapped websocket
|
||||
* that does the actual communication with the server. Secondly, the rtcstats component is initialized,
|
||||
|
@ -38,18 +50,20 @@ class RTCStats {
|
|||
*
|
||||
* @param {Object} options -.
|
||||
* @param {string} options.endpoint - The Amplitude app key required.
|
||||
* @param {string} options.useLegacy - Switch to legacy chrome webrtc statistics. Parameter will only have
|
||||
* @param {string} options.meetingFqn - The meeting fqn.
|
||||
* @param {boolean} options.useLegacy - Switch to legacy chrome webrtc statistics. Parameter will only have
|
||||
* an effect on chrome based applications.
|
||||
* @param {number} options.pollInterval - The getstats poll interval in ms.
|
||||
* @param {boolean} options.sendSdp - Determines if the client sends SDP to the rtcstats server.
|
||||
* @returns {void}
|
||||
*/
|
||||
init(options) {
|
||||
init(options: InitOptions) {
|
||||
|
||||
const { endpoint, useLegacy, pollInterval, sendSdp } = options;
|
||||
const { endpoint, meetingFqn, useLegacy, pollInterval, sendSdp } = options;
|
||||
|
||||
const traceOptions = {
|
||||
endpoint,
|
||||
meetingFqn,
|
||||
onCloseCallback: this.handleTraceWSClose.bind(this),
|
||||
useLegacy
|
||||
};
|
||||
|
@ -80,10 +94,10 @@ class RTCStats {
|
|||
* It can be generally used to send additional metadata that might be relevant such as amplitude user data
|
||||
* or deployment specific information.
|
||||
*
|
||||
* @param {Object} identityData - Metadata object to send as identity.
|
||||
* @param {any} identityData - Metadata object to send as identity.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendIdentityData(identityData) {
|
||||
sendIdentityData(identityData: any) {
|
||||
this.trace && this.trace.identity('identity', null, identityData);
|
||||
}
|
||||
|
||||
|
@ -93,7 +107,7 @@ class RTCStats {
|
|||
* @param {Array<string|any>} logEntries - The log entries to send to the rtcstats server.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendLogs(logEntries) {
|
||||
sendLogs(logEntries: Array<string|any>) {
|
||||
this.trace && this.trace.statsEntry('logs', null, logEntries);
|
||||
}
|
||||
|
||||
|
@ -103,7 +117,7 @@ class RTCStats {
|
|||
* @param {Object} dominantSpeakerData - Dominant speaker data to be saved in the rtcstats dump.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendDominantSpeakerData(dominantSpeakerData) {
|
||||
sendDominantSpeakerData(dominantSpeakerData: DominantSpeakerData) {
|
||||
this.trace && this.trace.statsEntry('dominantSpeaker', null, dominantSpeakerData);
|
||||
}
|
||||
|
||||
|
@ -113,7 +127,7 @@ class RTCStats {
|
|||
* @param {Object} e2eRttData - The object that holds the e2e data.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendE2eRttData(e2eRttData) {
|
||||
sendE2eRttData(e2eRttData: E2ERTTData) {
|
||||
this.trace && this.trace.statsEntry('e2eRtt', null, e2eRttData);
|
||||
}
|
||||
|
||||
|
@ -124,7 +138,7 @@ class RTCStats {
|
|||
* @param {Object} timestamp - The object which contains the timestamp.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendConferenceTimestamp(timestamp) {
|
||||
sendConferenceTimestamp(timestamp: number) {
|
||||
this.trace && this.trace.statsEntry('conferenceStartTimestamp', null, timestamp);
|
||||
}
|
||||
|
||||
|
@ -134,18 +148,18 @@ class RTCStats {
|
|||
* @param {Object} videoTypeData - The object that holds the videoType data.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendVideoTypeData(videoTypeData) {
|
||||
sendVideoTypeData(videoTypeData: VideoTypeData) {
|
||||
this.trace && this.trace.statsEntry('setVideoType', null, videoTypeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send face expression data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
* Send face landmarks data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
*
|
||||
* @param {Object} faceExpressionData - Face expression data to be saved in the rtcstats dump.
|
||||
* @param {Object} faceLandmarksData - Face landmarks data to be saved in the rtcstats dump.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendFaceExpressionData(faceExpressionData) {
|
||||
this.trace && this.trace.statsEntry('faceLandmarks', null, faceExpressionData);
|
||||
sendFaceLandmarksData(faceLandmarksData: FaceLandmarksData) {
|
||||
this.trace && this.trace.statsEntry('faceLandmarks', null, faceLandmarksData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,10 +167,11 @@ class RTCStats {
|
|||
* connect successfully initializes, however calls to GUM are recorded in an internal buffer even if not
|
||||
* connected and sent once it is established.
|
||||
*
|
||||
* @param {boolean} isBreakoutRoom - Flag indicating if the user is in a breakout room.
|
||||
* @returns {void}
|
||||
*/
|
||||
connect() {
|
||||
this.trace && this.trace.connect();
|
||||
connect(isBreakoutRoom: boolean) {
|
||||
this.trace && this.trace.connect(isBreakoutRoom);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,7 +195,7 @@ class RTCStats {
|
|||
* @param {Object} closeEvent - Event sent by ws onclose.
|
||||
* @returns {void}
|
||||
*/
|
||||
handleTraceWSClose(closeEvent) {
|
||||
handleTraceWSClose(closeEvent: any) {
|
||||
logger.info('RTCStats trace ws closed', closeEvent);
|
||||
|
||||
sendAnalytics(createRTCStatsTraceCloseEvent(closeEvent));
|
|
@ -1,34 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
import RTCStats from './RTCStats';
|
||||
|
||||
/**
|
||||
* Checks whether rtcstats is enabled or not.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isRtcstatsEnabled(stateful: Function | Object) {
|
||||
// TODO: Remove when rtcstats is fully cimpatible with mobile.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = toState(stateful);
|
||||
const config = state['features/base/config'];
|
||||
|
||||
return config?.analytics?.rtcstatsEnabled ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the rtcstats service send data.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function canSendRtcstatsData(stateful: Function | Object) {
|
||||
|
||||
return isRtcstatsEnabled(stateful) && RTCStats.isInitialized();
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/* eslint-disable import/order */
|
||||
// @ts-ignore
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
// @ts-ignore
|
||||
import { getAmplitudeIdentity } from '../analytics';
|
||||
import {
|
||||
getConferenceOptions,
|
||||
getAnalyticsRoomName
|
||||
|
||||
// @ts-ignore
|
||||
} from '../base/conference';
|
||||
|
||||
// @ts-ignore
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
|
||||
// @ts-ignore
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
import RTCStats from './RTCStats';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Checks whether rtcstats is enabled or not.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isRtcstatsEnabled(stateful: Function | Object) {
|
||||
const state = toState(stateful);
|
||||
const config = state['features/base/config'];
|
||||
|
||||
return config?.analytics?.rtcstatsEnabled ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the rtcstats service send data.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function canSendRtcstatsData(stateful: Function | Object) {
|
||||
return isRtcstatsEnabled(stateful) && RTCStats.isInitialized();
|
||||
}
|
||||
|
||||
type Identity = {
|
||||
isBreakoutRoom: boolean;
|
||||
|
||||
// Unique identifier for a conference session, not to be confused with meeting name
|
||||
// i.e. If all participants leave a meeting it will have a different value on the next join.
|
||||
meetingUniqueId?: string;
|
||||
roomId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the rtcstats service and sends the identity data.
|
||||
*
|
||||
* @param {Function} dispatch - The redux dispatch function.
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @param {Identity} identity - Identity data for the client.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function connectAndSendIdentity(dispatch: Function, stateful: Function | Object, identity: Identity) {
|
||||
const state = toState(stateful);
|
||||
|
||||
if (canSendRtcstatsData(state)) {
|
||||
|
||||
// Once the conference started connect to the rtcstats server and send data.
|
||||
try {
|
||||
RTCStats.connect(identity.isBreakoutRoom);
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const options = getConferenceOptions(state);
|
||||
|
||||
// The current implementation of rtcstats-server is configured to send data to amplitude, thus
|
||||
// we add identity specific information so we can correlate on the amplitude side. If amplitude is
|
||||
// not configured an empty object will be sent.
|
||||
// The current configuration of the conference is also sent as metadata to rtcstats server.
|
||||
// This is done in order to facilitate queries based on different conference configurations.
|
||||
// e.g. Find all RTCPeerConnections that connect to a specific shard or were created in a
|
||||
// conference with a specific version.
|
||||
// XXX(george): we also want to be able to correlate between rtcstats and callstats, so we're
|
||||
// appending the callstats user name (if it exists) to the display name.
|
||||
const displayName = options.statisticsId
|
||||
|| options.statisticsDisplayName
|
||||
|| jitsiLocalStorage.getItem('callStatsUserName');
|
||||
|
||||
RTCStats.sendIdentityData({
|
||||
...getAmplitudeIdentity(),
|
||||
...options,
|
||||
endpointId: localParticipant?.id,
|
||||
confName: getAnalyticsRoomName(state, dispatch),
|
||||
displayName,
|
||||
...identity
|
||||
});
|
||||
} catch (error) {
|
||||
// If the connection failed do not impact jitsi-meet just silently fail.
|
||||
logger.error('RTCStats connect failed with: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
import './middleware';
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
|
||||
// @ts-ignore
|
||||
import { getLogger } from '../base/logging/functions';
|
||||
|
||||
export default getLogger('features/rtcstats');
|
|
@ -1,23 +1,34 @@
|
|||
// @flow
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { getAmplitudeIdentity } from '../analytics';
|
||||
/* eslint-disable import/order */
|
||||
import { IStore } from '../app/types';
|
||||
import {
|
||||
CONFERENCE_UNIQUE_ID_SET,
|
||||
E2E_RTT_CHANGED,
|
||||
CONFERENCE_JOINED,
|
||||
CONFERENCE_TIMESTAMP_CHANGED,
|
||||
getConferenceOptions,
|
||||
getAnalyticsRoomName
|
||||
CONFERENCE_UNIQUE_ID_SET,
|
||||
CONFERENCE_WILL_LEAVE
|
||||
|
||||
// @ts-ignore
|
||||
} from '../base/conference';
|
||||
import { LIB_WILL_INIT } from '../base/lib-jitsi-meet/actionTypes';
|
||||
import { DOMINANT_SPEAKER_CHANGED, getLocalParticipant } from '../base/participants';
|
||||
|
||||
// @ts-ignore
|
||||
import { DOMINANT_SPEAKER_CHANGED } from '../base/participants';
|
||||
|
||||
// @ts-ignore
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
// @ts-ignore
|
||||
import { TRACK_ADDED, TRACK_UPDATED } from '../base/tracks';
|
||||
|
||||
// @ts-ignore
|
||||
import { isInBreakoutRoom, getCurrentRoomId } from '../breakout-rooms/functions';
|
||||
|
||||
// @ts-ignore
|
||||
import { extractFqnFromPath } from '../dynamic-branding/functions.any';
|
||||
import { ADD_FACE_EXPRESSION } from '../face-landmarks/actionTypes';
|
||||
|
||||
import RTCStats from './RTCStats';
|
||||
import { canSendRtcstatsData, isRtcstatsEnabled } from './functions';
|
||||
import { canSendRtcstatsData, connectAndSendIdentity, isRtcstatsEnabled } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
|
@ -27,30 +38,30 @@ import logger from './logger';
|
|||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
const state = store.getState();
|
||||
const { dispatch } = store;
|
||||
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: any) => {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
const config = state['features/base/config'];
|
||||
const { analytics, faceLandmarks } = config;
|
||||
|
||||
switch (action.type) {
|
||||
case LIB_WILL_INIT: {
|
||||
if (isRtcstatsEnabled(state)) {
|
||||
if (isRtcstatsEnabled(state) && !RTCStats.isInitialized()) {
|
||||
// RTCStats "proxies" WebRTC functions such as GUM and RTCPeerConnection by rewriting the global
|
||||
// window functions. Because lib-jitsi-meet uses references to those functions that are taken on
|
||||
// init, we need to add these proxies before it initializes, otherwise lib-jitsi-meet will use the
|
||||
// original non proxy versions of these functions.
|
||||
try {
|
||||
// Default poll interval is 10000ms and standard stats will be used, if not provided in the config.
|
||||
const pollInterval = analytics.rtcstatsPollInterval || 10000;
|
||||
const useLegacy = analytics.rtcstatsUseLegacy || false;
|
||||
const sendSdp = analytics.rtcstatsSendSdp || false;
|
||||
|
||||
const pollInterval = analytics?.rtcstatsPollInterval || 10000;
|
||||
const useLegacy = analytics?.rtcstatsUseLegacy || false;
|
||||
const sendSdp = analytics?.rtcstatsSendSdp || false;
|
||||
|
||||
// Initialize but don't connect to the rtcstats server wss, as it will start sending data for all
|
||||
// media calls made even before the conference started.
|
||||
RTCStats.init({
|
||||
endpoint: analytics.rtcstatsEndpoint,
|
||||
endpoint: analytics?.rtcstatsEndpoint,
|
||||
meetingFqn: extractFqnFromPath(state),
|
||||
useLegacy,
|
||||
pollInterval,
|
||||
sendSdp
|
||||
|
@ -61,6 +72,43 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Used for connecting to rtcstats server when joining a breakout room.
|
||||
// Breakout rooms do not have a meetingUniqueId.
|
||||
case CONFERENCE_JOINED: {
|
||||
if (isInBreakoutRoom(getState())) {
|
||||
connectAndSendIdentity(
|
||||
dispatch,
|
||||
state,
|
||||
{
|
||||
isBreakoutRoom: true,
|
||||
roomId: getCurrentRoomId(getState())
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Used for connecting to rtcstats server when joining the main room.
|
||||
// Using this event to be sure the meetingUniqueId can be retrieved.
|
||||
case CONFERENCE_UNIQUE_ID_SET: {
|
||||
if (!isInBreakoutRoom(getState())) {
|
||||
// Unique identifier for a conference session, not to be confused with meeting name
|
||||
// i.e. If all participants leave a meeting it will have a different value on the next join.
|
||||
const { conference } = action;
|
||||
const meetingUniqueId = conference && conference.getMeetingUniqueId();
|
||||
|
||||
connectAndSendIdentity(
|
||||
dispatch,
|
||||
state,
|
||||
{
|
||||
isBreakoutRoom: false,
|
||||
meetingUniqueId
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TRACK_ADDED: {
|
||||
if (canSendRtcstatsData(state)) {
|
||||
const jitsiTrack = action?.track?.jitsiTrack;
|
||||
|
@ -93,50 +141,6 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case CONFERENCE_UNIQUE_ID_SET: {
|
||||
if (canSendRtcstatsData(state)) {
|
||||
|
||||
// Once the conference started connect to the rtcstats server and send data.
|
||||
try {
|
||||
RTCStats.connect();
|
||||
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const options = getConferenceOptions(state);
|
||||
|
||||
|
||||
// Unique identifier for a conference session, not to be confused with meeting name
|
||||
// i.e. If all participants leave a meeting it will have a different value on the next join.
|
||||
const { conference } = action;
|
||||
const meetingUniqueId = conference && conference.getMeetingUniqueId();
|
||||
|
||||
// The current implementation of rtcstats-server is configured to send data to amplitude, thus
|
||||
// we add identity specific information so we can correlate on the amplitude side. If amplitude is
|
||||
// not configured an empty object will be sent.
|
||||
// The current configuration of the conference is also sent as metadata to rtcstats server.
|
||||
// This is done in order to facilitate queries based on different conference configurations.
|
||||
// e.g. Find all RTCPeerConnections that connect to a specific shard or were created in a
|
||||
// conference with a specific version.
|
||||
// XXX(george): we also want to be able to correlate between rtcstats and callstats, so we're
|
||||
// appending the callstats user name (if it exists) to the display name.
|
||||
const displayName = options.statisticsId
|
||||
|| options.statisticsDisplayName
|
||||
|| jitsiLocalStorage.getItem('callStatsUserName');
|
||||
|
||||
RTCStats.sendIdentityData({
|
||||
...getAmplitudeIdentity(),
|
||||
...options,
|
||||
endpointId: localParticipant?.id,
|
||||
confName: getAnalyticsRoomName(state, dispatch),
|
||||
displayName,
|
||||
meetingUniqueId
|
||||
});
|
||||
} catch (error) {
|
||||
// If the connection failed do not impact jitsi-meet just silently fail.
|
||||
logger.error('RTCStats connect failed with: ', error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DOMINANT_SPEAKER_CHANGED: {
|
||||
if (canSendRtcstatsData(state)) {
|
||||
const { id, previousSpeakers } = action.participant;
|
||||
|
@ -162,7 +166,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
if (canSendRtcstatsData(state) && faceLandmarks && faceLandmarks.enableRTCStats) {
|
||||
const { duration, faceExpression, timestamp } = action;
|
||||
|
||||
RTCStats.sendFaceExpressionData({
|
||||
RTCStats.sendFaceLandmarksData({
|
||||
duration,
|
||||
faceLandmarks: faceExpression,
|
||||
timestamp
|
||||
|
@ -172,12 +176,18 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
}
|
||||
case CONFERENCE_TIMESTAMP_CHANGED: {
|
||||
if (canSendRtcstatsData(state)) {
|
||||
const conferenceTimestamp = action.conferenceTimestamp;
|
||||
const { conferenceTimestamp } = action;
|
||||
|
||||
RTCStats.sendConferenceTimestamp(conferenceTimestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONFERENCE_WILL_LEAVE: {
|
||||
if (canSendRtcstatsData(state)) {
|
||||
RTCStats.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
|
@ -0,0 +1,29 @@
|
|||
export type InitOptions = {
|
||||
endpoint?: string;
|
||||
meetingFqn: string;
|
||||
pollInterval: number;
|
||||
sendSdp: boolean;
|
||||
useLegacy: boolean;
|
||||
}
|
||||
|
||||
export type VideoTypeData = {
|
||||
ssrc: number;
|
||||
videoType: string;
|
||||
}
|
||||
|
||||
export type DominantSpeakerData = {
|
||||
dominantSpeakerEndpoint: string;
|
||||
previousSpeakers: string[];
|
||||
}
|
||||
|
||||
export type E2ERTTData = {
|
||||
remoteEndpointId: string;
|
||||
remoteRegion: string;
|
||||
rtt: number;
|
||||
}
|
||||
|
||||
export type FaceLandmarksData = {
|
||||
duration: number;
|
||||
faceLandmarks: string;
|
||||
timestamp: number;
|
||||
}
|
Loading…
Reference in New Issue