@observertc/observer-js
Version:
Server Side NodeJS Library for processing ObserveRTC Samples
161 lines (160 loc) • 6.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Observer = void 0;
const logger_1 = require("./common/logger");
const ObservedCall_1 = require("./ObservedCall");
const events_1 = require("events");
const ObservedTURN_1 = require("./ObservedTURN");
const Detectors_1 = require("./detectors/Detectors");
const OnIntervalUpdater_1 = require("./updaters/OnIntervalUpdater");
const OnAllCallObserverUpdater_1 = require("./updaters/OnAllCallObserverUpdater");
const OnAnyCallObserverUpdater_1 = require("./updaters/OnAnyCallObserverUpdater");
const ObserverEventMonitor_1 = require("./ObserverEventMonitor");
const MediasoupRemoteTrackResolver_1 = require("./utils/MediasoupRemoteTrackResolver");
const logger = (0, logger_1.createLogger)('Observer');
class Observer extends events_1.EventEmitter {
config;
detectors;
observedTURN = new ObservedTURN_1.ObservedTURN();
observedCalls = new Map();
updater;
closed = false;
totalAddedCall = 0;
totalRemovedCall = 0;
numberOfClientsUsingTurn = 0;
numberOfClients = 0;
numberOfInboundRtpStreams = 0;
numberOfOutboundRtpStreams = 0;
numberOfDataChannels = 0;
numberOfPeerConnections = 0;
get numberOfCalls() {
return this.observedCalls.size;
}
_timer;
constructor(config = {
updatePolicy: 'update-when-all-call-updated',
updateIntervalInMs: undefined,
appData: {},
}) {
super();
this.config = config;
this.setMaxListeners(Infinity);
this.update = this.update.bind(this);
const currentUpdatePolicy = (config?.updatePolicy) ?? 'update-when-all-call-updated';
switch (currentUpdatePolicy) {
case 'update-on-any-call-updated':
this.updater = new OnAnyCallObserverUpdater_1.OnAnyCallObserverUpdater(this);
break;
case 'update-when-all-call-updated':
this.updater = new OnAllCallObserverUpdater_1.OnAllCallObserverUpdater(this);
break;
case 'update-on-interval': {
const interval = config?.updateIntervalInMs;
if (!interval) {
throw new Error('updateIntervalInMs setting in config must be set if updatePolicy is update-on-interval');
}
this.updater = new OnIntervalUpdater_1.OnIntervalUpdater(interval, this.update.bind(this));
break;
}
}
this.detectors = new Detectors_1.Detectors();
}
get appData() {
return this.config.appData;
}
getObservedCall(callId) {
if (this.closed || !this.observedCalls.has(callId))
return;
return this.observedCalls.get(callId);
}
createObservedCall(settings) {
if (this.closed) {
throw new Error('Attempted to create a call source on a closed observer');
}
if (!settings.updatePolicy) {
settings.updatePolicy = this.config.defaultCallUpdatePolicy;
settings.updateIntervalInMs = this.config.defaultCallUpdateIntervalInMs;
}
if (!settings.closeCallIfEmptyForMs) {
settings.closeCallIfEmptyForMs = this.config.closeCallIfEmptyForMs;
}
const observedCall = new ObservedCall_1.ObservedCall(settings, this);
const onCallUpdated = () => this._onObservedCallUpdated(observedCall);
if (this.observedCalls.has(observedCall.callId))
throw new Error(`Observed Call with id ${observedCall.callId} already exists`);
if (settings.remoteTrackResolvePolicy === 'mediasoup-sfu') {
observedCall.remoteTrackResolver = new MediasoupRemoteTrackResolver_1.MediasoupRemoteTrackResolver(observedCall);
}
else if (settings.remoteTrackResolvePolicy === 'p2p') {
// For future implementation
}
else if (settings.remoteTrackResolvePolicy === 'none' || !settings.remoteTrackResolvePolicy) {
// do nothing
}
observedCall.once('close', () => {
this.observedCalls.delete(observedCall.callId);
observedCall.off('update', onCallUpdated);
++this.totalRemovedCall;
});
this.observedCalls.set(observedCall.callId, observedCall);
observedCall.on('update', onCallUpdated);
++this.totalAddedCall;
this.emit('newcall', observedCall);
return observedCall;
}
close() {
if (this.closed) {
return logger.debug('Attempted to close twice');
}
this.closed = true;
clearInterval(this._timer);
this._timer = undefined;
this.observedCalls.forEach((call) => call.close());
this.emit('close');
}
accept(sample) {
if (this.closed)
return;
if (!sample.callId)
return logger.warn('Received sample without callId. %o', sample);
if (!sample.clientId)
return logger.warn('Received sample without clientId %o', sample);
const call = this.getObservedCall(sample.callId) ?? this.createObservedCall({
callId: sample.callId,
updateIntervalInMs: this.config.defaultCallUpdateIntervalInMs,
updatePolicy: this.config.defaultCallUpdatePolicy,
});
const client = call.getObservedClient(sample.clientId) ?? call.createObservedClient({
clientId: sample.clientId,
});
client.accept(sample);
}
update() {
if (this.closed) {
return;
}
this.numberOfInboundRtpStreams = 0;
this.numberOfOutboundRtpStreams = 0;
this.numberOfPeerConnections = 0;
this.numberOfDataChannels = 0;
this.numberOfClients = 0;
this.numberOfClientsUsingTurn = 0;
for (const call of this.observedCalls.values()) {
this.numberOfInboundRtpStreams += call.numberOfInboundRtpStreams;
this.numberOfOutboundRtpStreams += call.numberOfOutboundRtpStreams;
this.numberOfPeerConnections += call.numberOfPeerConnections;
this.numberOfDataChannels += call.numberOfDataChannels;
this.numberOfClients += call.numberOfClients;
this.numberOfClientsUsingTurn += call.clientsUsedTurn.size;
}
this.observedTURN.update();
this.emit('update');
}
_onObservedCallUpdated(call) {
call;
}
createEventMonitor(ctx) {
return new ObserverEventMonitor_1.ObserverEventMonitor(this, ctx ?? {});
}
}
exports.Observer = Observer;