@matterbridge/core 3.10.9-dev-20260908-a3f4064 → 3.10.9-dev-20260910-ca91bcb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/behaviors/cameraAvSettingsUserLevelManagementServer.d.ts +24 -0
- package/dist/behaviors/cameraAvSettingsUserLevelManagementServer.js +53 -0
- package/dist/behaviors/cameraAvStreamManagementServer.d.ts +80 -0
- package/dist/behaviors/cameraAvStreamManagementServer.js +414 -0
- package/dist/behaviors/chimeServer.d.ts +12 -0
- package/dist/behaviors/chimeServer.js +37 -0
- package/dist/behaviors/clients.d.ts +4 -0
- package/dist/behaviors/clients.js +33 -0
- package/dist/behaviors/ffmpeg.d.ts +4 -0
- package/dist/behaviors/ffmpeg.js +103 -0
- package/dist/behaviors/snapshot.d.ts +15 -0
- package/dist/behaviors/snapshot.js +139 -0
- package/dist/behaviors/webRtcTransportProviderServer.d.ts +28 -0
- package/dist/behaviors/webRtcTransportProviderServer.js +422 -0
- package/dist/behaviors/webRtcTransportRequestorServer.d.ts +10 -0
- package/dist/behaviors/webRtcTransportRequestorServer.js +24 -0
- package/dist/behaviors/weriftSession.d.ts +63 -0
- package/dist/behaviors/weriftSession.js +538 -0
- package/dist/devices/audioDoorbell.d.ts +20 -0
- package/dist/devices/audioDoorbell.js +49 -0
- package/dist/devices/camera.d.ts +37 -0
- package/dist/devices/camera.js +64 -0
- package/dist/devices/chime.d.ts +16 -0
- package/dist/devices/chime.js +37 -0
- package/dist/devices/doorbell.d.ts +11 -0
- package/dist/devices/doorbell.js +31 -0
- package/dist/devices/floodlightCamera.d.ts +19 -0
- package/dist/devices/floodlightCamera.js +81 -0
- package/dist/devices/intercom.d.ts +22 -0
- package/dist/devices/intercom.js +55 -0
- package/dist/devices/snapshotCamera.d.ts +20 -0
- package/dist/devices/snapshotCamera.js +51 -0
- package/dist/devices/videoDoorbell.d.ts +21 -0
- package/dist/devices/videoDoorbell.js +82 -0
- package/dist/matterbridgeEndpoint.d.ts +4 -1
- package/dist/matterbridgeEndpoint.js +4 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +3 -0
- package/dist/matterbridgeEndpointHelpers.js +51 -2
- package/dist/matterbridgeEndpointTypes.d.ts +15 -0
- package/dist/matterbridgeEndpointTypes.js +1 -2
- package/package.json +6 -5
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ChimeClient } from '@matter/node/behaviors/chime';
|
|
2
|
+
import { WebRtcTransportProviderClient } from '@matter/node/behaviors/web-rtc-transport-provider';
|
|
3
|
+
import { WebRtcTransportRequestorClient } from '@matter/node/behaviors/web-rtc-transport-requestor';
|
|
4
|
+
import { Chime } from '@matter/types/clusters/chime';
|
|
5
|
+
import { WebRtcTransportProvider } from '@matter/types/clusters/web-rtc-transport-provider';
|
|
6
|
+
import { WebRtcTransportRequestor } from '@matter/types/clusters/web-rtc-transport-requestor';
|
|
7
|
+
import { MatterbridgeBindingServer } from './bindingServer.js';
|
|
8
|
+
function addBindingClientListEntry(endpoint, clusterId) {
|
|
9
|
+
if (endpoint.behaviors.has(MatterbridgeBindingServer)) {
|
|
10
|
+
const existing = endpoint.behaviors.optionsFor(MatterbridgeBindingServer)?.clientList ?? [];
|
|
11
|
+
if (!existing.includes(clusterId)) {
|
|
12
|
+
endpoint.behaviors.inject(MatterbridgeBindingServer, { clientList: [...existing, clusterId] });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
endpoint.behaviors.require(MatterbridgeBindingServer, { clientList: [clusterId] });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function addChimeClient(endpoint) {
|
|
20
|
+
addBindingClientListEntry(endpoint, Chime.id);
|
|
21
|
+
endpoint.type.clientClusters.chime ??= ChimeClient;
|
|
22
|
+
return endpoint;
|
|
23
|
+
}
|
|
24
|
+
export function addWebRtcTransportRequestorClient(endpoint) {
|
|
25
|
+
addBindingClientListEntry(endpoint, WebRtcTransportRequestor.id);
|
|
26
|
+
endpoint.type.clientClusters.webRtcTransportRequestor ??= WebRtcTransportRequestorClient;
|
|
27
|
+
return endpoint;
|
|
28
|
+
}
|
|
29
|
+
export function addWebRtcTransportProviderClient(endpoint) {
|
|
30
|
+
addBindingClientListEntry(endpoint, WebRtcTransportProvider.id);
|
|
31
|
+
endpoint.type.clientClusters.webRtcTransportProvider ??= WebRtcTransportProviderClient;
|
|
32
|
+
return endpoint;
|
|
33
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { access, readdir } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { AnsiLogger, MAGENTA } from 'node-ansi-logger';
|
|
6
|
+
const log = new AnsiLogger({ logName: 'Ffmpeg', logLevel: "debug", logNameColor: MAGENTA, logTimestampFormat: 4 });
|
|
7
|
+
async function runProbe(command, args) {
|
|
8
|
+
await new Promise((resolve, reject) => {
|
|
9
|
+
const child = spawn(command, args, { stdio: 'ignore' });
|
|
10
|
+
child.once('error', reject);
|
|
11
|
+
child.once('exit', (code) => {
|
|
12
|
+
if (code === 0) {
|
|
13
|
+
resolve();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
reject(new Error(`${command} exited with code ${code ?? -1}`));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async function isRunnable(command) {
|
|
21
|
+
for (const versionArg of ['--version', '-version']) {
|
|
22
|
+
try {
|
|
23
|
+
await runProbe(command, [versionArg]);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
async function getWindowsCommandCandidates() {
|
|
32
|
+
if (process.platform !== 'win32')
|
|
33
|
+
return [];
|
|
34
|
+
const candidates = [];
|
|
35
|
+
if (process.env.LOCALAPPDATA) {
|
|
36
|
+
const wingetPackages = path.join(process.env.LOCALAPPDATA, 'Microsoft', 'WinGet', 'Packages');
|
|
37
|
+
try {
|
|
38
|
+
const packageDirs = await readdir(wingetPackages, { withFileTypes: true });
|
|
39
|
+
for (const packageDir of packageDirs) {
|
|
40
|
+
if (!packageDir.isDirectory() || !packageDir.name.startsWith('Gyan.FFmpeg_'))
|
|
41
|
+
continue;
|
|
42
|
+
const packagePath = path.join(wingetPackages, packageDir.name);
|
|
43
|
+
try {
|
|
44
|
+
const versionDirs = await readdir(packagePath, { withFileTypes: true });
|
|
45
|
+
for (const versionDir of versionDirs) {
|
|
46
|
+
if (versionDir.isDirectory() && versionDir.name.startsWith('ffmpeg-'))
|
|
47
|
+
candidates.push(path.join(packagePath, versionDir.name, 'bin', 'ffmpeg.exe'));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const programFiles of [process.env.ProgramFiles, process.env['ProgramFiles(x86)']]) {
|
|
58
|
+
if (!programFiles)
|
|
59
|
+
continue;
|
|
60
|
+
candidates.push(path.join(programFiles, 'ffmpeg', 'bin', 'ffmpeg.exe'), path.join(programFiles, 'Gyan', 'FFmpeg', 'bin', 'ffmpeg.exe'));
|
|
61
|
+
}
|
|
62
|
+
return candidates;
|
|
63
|
+
}
|
|
64
|
+
async function resolveFfmpeg() {
|
|
65
|
+
const candidates = ['ffmpeg', '/usr/bin/ffmpeg', '/bin/ffmpeg', '/usr/local/bin/ffmpeg', ...(await getWindowsCommandCandidates())];
|
|
66
|
+
log.debug(`Resolving ffmpeg: trying ${candidates.length} candidate(s)`);
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (path.isAbsolute(candidate)) {
|
|
69
|
+
try {
|
|
70
|
+
await access(candidate, constants.X_OK);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
log.debug(`Candidate ${candidate} is not accessible`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (await isRunnable(candidate)) {
|
|
78
|
+
log.debug(`Found ffmpeg in ${candidate}`);
|
|
79
|
+
return candidate;
|
|
80
|
+
}
|
|
81
|
+
log.debug(`Candidate ${candidate} did not run successfully`);
|
|
82
|
+
}
|
|
83
|
+
log.debug('Could not resolve ffmpeg');
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const ffmpegCommand = await resolveFfmpeg();
|
|
87
|
+
if (ffmpegCommand)
|
|
88
|
+
log.debug(`Using ffmpeg: ${ffmpegCommand}`);
|
|
89
|
+
else
|
|
90
|
+
log.warn('ffmpeg could not be resolved on this host; ffmpeg-dependent features will not work');
|
|
91
|
+
export function hasFfmpeg() {
|
|
92
|
+
return ffmpegCommand !== undefined;
|
|
93
|
+
}
|
|
94
|
+
export function redactSource(source) {
|
|
95
|
+
return source.replace(/^([a-z][a-z\d+.-]*:\/\/)[^/]*@/i, '$1***@');
|
|
96
|
+
}
|
|
97
|
+
export function runFfmpeg(args) {
|
|
98
|
+
if (!ffmpegCommand) {
|
|
99
|
+
throw new Error('Cannot run ffmpeg: not found on this host');
|
|
100
|
+
}
|
|
101
|
+
log.debug(`Spawning ffmpeg: ${ffmpegCommand} ${args.map(redactSource).join(' ')}`);
|
|
102
|
+
return spawn(ffmpegCommand, args);
|
|
103
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface SnapshotRequest {
|
|
2
|
+
src: string;
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
maxBytes?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SnapshotResult {
|
|
8
|
+
data: Buffer;
|
|
9
|
+
byteLength: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
quality: number;
|
|
13
|
+
downgraded: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function captureSnapshot(request: SnapshotRequest): Promise<SnapshotResult>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { AnsiLogger, MAGENTA } from 'node-ansi-logger';
|
|
5
|
+
import { hasFfmpeg, redactSource, runFfmpeg } from './ffmpeg.js';
|
|
6
|
+
const MAX_BYTES = 64000;
|
|
7
|
+
const QUALITIES = [6, 10, 14, 18, 24, 31];
|
|
8
|
+
const FALLBACK_WIDTH = 640;
|
|
9
|
+
const WEBCAM_WARMUP_SECONDS = 1;
|
|
10
|
+
const CAPTURE_TIMEOUT_MS = 10_000;
|
|
11
|
+
const TERMINATION_GRACE_MS = 1_000;
|
|
12
|
+
const log = new AnsiLogger({ logName: 'Snapshot', logLevel: "debug", logNameColor: MAGENTA, logTimestampFormat: 4 });
|
|
13
|
+
export async function captureSnapshot(request) {
|
|
14
|
+
const { src, width, height, maxBytes = MAX_BYTES } = request;
|
|
15
|
+
if (typeof src !== 'string' || src.trim().length === 0) {
|
|
16
|
+
throw new Error('Snapshot source must not be empty');
|
|
17
|
+
}
|
|
18
|
+
if (!Number.isFinite(width) || !Number.isInteger(width) || width <= 0) {
|
|
19
|
+
throw new Error(`Snapshot width must be a positive integer, got ${width}`);
|
|
20
|
+
}
|
|
21
|
+
if (!Number.isFinite(height) || !Number.isInteger(height) || height <= 0) {
|
|
22
|
+
throw new Error(`Snapshot height must be a positive integer, got ${height}`);
|
|
23
|
+
}
|
|
24
|
+
if (!Number.isFinite(maxBytes) || !Number.isInteger(maxBytes) || maxBytes <= 0) {
|
|
25
|
+
throw new Error(`Snapshot maxBytes must be a positive integer, got ${maxBytes}`);
|
|
26
|
+
}
|
|
27
|
+
const redactedSrc = redactSource(src);
|
|
28
|
+
log.debug(`Capturing snapshot from ${redactedSrc} at ${width}x${height}, maxBytes=${maxBytes}`);
|
|
29
|
+
let last = Buffer.alloc(0);
|
|
30
|
+
let lastQuality = QUALITIES[QUALITIES.length - 1];
|
|
31
|
+
log.debug(`Pass 1: trying ${width}x${height} at qualities [${QUALITIES.join(', ')}]`);
|
|
32
|
+
for (const q of QUALITIES) {
|
|
33
|
+
log.debug(`Pass 1: grabbing ${width}x${height} at quality ${q}`);
|
|
34
|
+
const buf = await grab(src, width, height, q);
|
|
35
|
+
log.debug(`Pass 1: grabbed ${buf.length}B at quality ${q} (maxBytes=${maxBytes})`);
|
|
36
|
+
last = buf;
|
|
37
|
+
lastQuality = q;
|
|
38
|
+
if (buf.length <= maxBytes) {
|
|
39
|
+
log.debug(`Pass 1: accepted ${buf.length}B at ${width}x${height}, quality ${q}`);
|
|
40
|
+
return { data: buf, byteLength: buf.length, width, height, quality: q, downgraded: false };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const fallbackWidth = Math.min(width, FALLBACK_WIDTH);
|
|
44
|
+
log.debug(`Pass 2: falling back to ${fallbackWidth}px wide at qualities [${QUALITIES.join(', ')}]`);
|
|
45
|
+
for (const q of QUALITIES) {
|
|
46
|
+
log.debug(`Pass 2: grabbing ${fallbackWidth}px wide at quality ${q}`);
|
|
47
|
+
const buf = await grab(src, fallbackWidth, -1, q);
|
|
48
|
+
log.debug(`Pass 2: grabbed ${buf.length}B at quality ${q} (maxBytes=${maxBytes})`);
|
|
49
|
+
last = buf;
|
|
50
|
+
lastQuality = q;
|
|
51
|
+
if (buf.length <= maxBytes) {
|
|
52
|
+
log.warn(`Snapshot downgraded to ${fallbackWidth}px to fit ${maxBytes}B (requested ${width}x${height})`);
|
|
53
|
+
return { data: buf, byteLength: buf.length, width: fallbackWidth, height: -1, quality: q, downgraded: true };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Snapshot capture from ${redactedSrc} exceeds ${maxBytes}B at minimum quality/resolution (${last.length}B at quality ${lastQuality})`);
|
|
57
|
+
}
|
|
58
|
+
function buildInputArgs(src, width, height) {
|
|
59
|
+
if (src.startsWith('rtsp://')) {
|
|
60
|
+
return ['-rtsp_transport', 'tcp', '-i', src];
|
|
61
|
+
}
|
|
62
|
+
const warmup = ['-ss', String(WEBCAM_WARMUP_SECONDS)];
|
|
63
|
+
const videoSize = height === -1 ? [] : ['-video_size', `${width}x${height}`];
|
|
64
|
+
switch (process.platform) {
|
|
65
|
+
case 'linux':
|
|
66
|
+
return ['-f', 'v4l2', ...videoSize, '-i', src, ...warmup];
|
|
67
|
+
case 'darwin':
|
|
68
|
+
return ['-f', 'avfoundation', ...videoSize, '-i', src, ...warmup];
|
|
69
|
+
case 'win32':
|
|
70
|
+
return ['-f', 'dshow', ...videoSize, '-i', `video=${src}`, ...warmup];
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Webcam capture via ffmpeg is not supported on platform "${process.platform}"`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function grab(src, width, height, q) {
|
|
76
|
+
if (!hasFfmpeg()) {
|
|
77
|
+
log.warn('Cannot capture snapshot: missing dependency ffmpeg');
|
|
78
|
+
throw new Error('Cannot capture snapshot: missing dependency ffmpeg');
|
|
79
|
+
}
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const scaleExpr = height === -1 ? `scale=${width}:-1` : `scale=${width}:${height}`;
|
|
82
|
+
const args = ['-y', ...buildInputArgs(src, width, height), '-frames:v', '1', '-vf', scaleExpr, '-q:v', String(q), '-f', 'mjpeg', 'pipe:1'];
|
|
83
|
+
const proc = runFfmpeg(args);
|
|
84
|
+
const chunks = [];
|
|
85
|
+
let stderr = '';
|
|
86
|
+
let forceKillTimeout;
|
|
87
|
+
const timeout = setTimeout(() => {
|
|
88
|
+
proc.kill('SIGTERM');
|
|
89
|
+
forceKillTimeout = setTimeout(() => {
|
|
90
|
+
proc.kill('SIGKILL');
|
|
91
|
+
}, TERMINATION_GRACE_MS);
|
|
92
|
+
reject(new Error(`Snapshot capture from ${redactSource(src)} timed out after ${CAPTURE_TIMEOUT_MS}ms`));
|
|
93
|
+
}, CAPTURE_TIMEOUT_MS);
|
|
94
|
+
proc.stdout?.on('data', (d) => chunks.push(d));
|
|
95
|
+
proc.stderr?.on('data', (d) => {
|
|
96
|
+
stderr += d.toString();
|
|
97
|
+
});
|
|
98
|
+
proc.on('close', (code) => {
|
|
99
|
+
clearTimeout(timeout);
|
|
100
|
+
if (forceKillTimeout)
|
|
101
|
+
clearTimeout(forceKillTimeout);
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
const data = Buffer.concat(chunks);
|
|
104
|
+
if (data.length === 0) {
|
|
105
|
+
reject(new Error(`Snapshot capture from ${redactSource(src)} produced no data`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
log.debug(`ffmpeg exited 0, captured ${data.length}B`);
|
|
109
|
+
resolve(data);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
log.debug(`ffmpeg exited ${code}: ${stderr}`);
|
|
113
|
+
reject(new Error(`ffmpeg exited ${code}: ${stderr}`));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
proc.on('error', (error) => {
|
|
117
|
+
clearTimeout(timeout);
|
|
118
|
+
if (forceKillTimeout)
|
|
119
|
+
clearTimeout(forceKillTimeout);
|
|
120
|
+
reject(error);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
125
|
+
const src = process.argv[2] ?? 'Surface Camera Front';
|
|
126
|
+
const outputDir = path.join(process.cwd(), 'temp');
|
|
127
|
+
await mkdir(outputDir, { recursive: true });
|
|
128
|
+
const resolutions = [
|
|
129
|
+
{ width: 640, height: 480 },
|
|
130
|
+
{ width: 1280, height: 720 },
|
|
131
|
+
{ width: 1920, height: 1080 },
|
|
132
|
+
];
|
|
133
|
+
for (const { width, height } of resolutions) {
|
|
134
|
+
const result = await captureSnapshot({ src, width, height });
|
|
135
|
+
const outputFile = path.join(outputDir, `snapshot-${width}x${height}.jpeg`);
|
|
136
|
+
await writeFile(outputFile, result.data);
|
|
137
|
+
log.info(`Saved snapshot to ${outputFile} (${result.byteLength}B, ${result.width}x${result.height}, quality=${result.quality}, downgraded=${result.downgraded})`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WebRtcTransportProviderServer } from '@matter/node/behaviors/web-rtc-transport-provider';
|
|
2
|
+
import type { WebRtcTransportProvider } from '@matter/types/clusters/web-rtc-transport-provider';
|
|
3
|
+
import type { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
|
|
4
|
+
import { type WeriftOfferOptions, WeriftWebRtcSession } from './weriftSession.js';
|
|
5
|
+
export declare function allocateWebRtcSessionId(nextCandidate: number, activeSessionIds: ReadonlySet<number>): {
|
|
6
|
+
webRtcSessionId: number;
|
|
7
|
+
nextCandidate: number;
|
|
8
|
+
};
|
|
9
|
+
export declare class MatterbridgeWebRtcTransportProviderServer extends WebRtcTransportProviderServer {
|
|
10
|
+
#private;
|
|
11
|
+
state: MatterbridgeWebRtcTransportProviderServer.State;
|
|
12
|
+
internal: MatterbridgeWebRtcTransportProviderServer.Internal;
|
|
13
|
+
solicitOffer(request: WebRtcTransportProvider.SolicitOfferRequest): Promise<WebRtcTransportProvider.SolicitOfferResponse>;
|
|
14
|
+
provideOffer(request: WebRtcTransportProvider.ProvideOfferRequest): Promise<WebRtcTransportProvider.ProvideOfferResponse>;
|
|
15
|
+
provideAnswer(request: WebRtcTransportProvider.ProvideAnswerRequest): Promise<void>;
|
|
16
|
+
provideIceCandidates(request: WebRtcTransportProvider.ProvideIceCandidatesRequest): Promise<void>;
|
|
17
|
+
endSession(request: WebRtcTransportProvider.EndSessionRequest): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare namespace MatterbridgeWebRtcTransportProviderServer {
|
|
20
|
+
class State extends WebRtcTransportProviderServer.State {
|
|
21
|
+
weriftOfferOptions: WeriftOfferOptions;
|
|
22
|
+
}
|
|
23
|
+
class Internal {
|
|
24
|
+
nextWebRtcSessionId: number;
|
|
25
|
+
sessions: Map<number, WeriftWebRtcSession>;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export declare function createDefaultWebRtcTransportProviderClusterServer(endpoint: MatterbridgeEndpoint, weriftOfferOptions?: WeriftOfferOptions): MatterbridgeEndpoint;
|