@matterbridge/core 3.10.14 → 3.10.15-dev-20261018-1598399

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.
@@ -123,7 +123,11 @@ export function runFfmpeg(args) {
123
123
  throw new Error('Cannot run ffmpeg: not found on this host');
124
124
  }
125
125
  log.debug(`Spawning ffmpeg: ${ffmpegCommand} ${args.map(redactSource).join(' ')}`);
126
- return spawn(ffmpegCommand, args);
126
+ const child = spawn(ffmpegCommand, args);
127
+ child.once('exit', (code, signal) => {
128
+ log.debug(`ffmpeg process ${child.pid ?? 'unknown'} exited with ${signal ? `signal ${signal}` : `code ${code}`}`);
129
+ });
130
+ return child;
127
131
  }
128
132
  async function ffmpegOutput(args) {
129
133
  return new Promise((resolve, reject) => {
@@ -16,6 +16,7 @@ export declare class MatterbridgeWebRtcTransportProviderServer extends WebRtcTra
16
16
  provideAnswer(request: WebRtcTransportProvider.ProvideAnswerRequest): Promise<void>;
17
17
  provideIceCandidates(request: WebRtcTransportProvider.ProvideIceCandidatesRequest): Promise<void>;
18
18
  endSession(request: WebRtcTransportProvider.EndSessionRequest): Promise<void>;
19
+ closeAllSessions(): Promise<void>;
19
20
  }
20
21
  export declare namespace MatterbridgeWebRtcTransportProviderServer {
21
22
  class State extends WebRtcTransportProviderServer.State {
@@ -28,3 +29,4 @@ export declare namespace MatterbridgeWebRtcTransportProviderServer {
28
29
  }
29
30
  }
30
31
  export declare function createDefaultWebRtcTransportProviderClusterServer(endpoint: MatterbridgeEndpoint, weriftOfferOptions?: WeriftOfferOptions): MatterbridgeEndpoint;
32
+ export declare function closeWebRtcSessions(device: MatterbridgeEndpoint): Promise<void>;
@@ -441,6 +441,22 @@ export class MatterbridgeWebRtcTransportProviderServer extends WebRtcTransportPr
441
441
  device.log.info(`MatterbridgeWebRtcTransportProviderServer.endSession: ended webRTC session ${request.webRtcSessionId} (reason ${request.reason}) (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
442
442
  this.endpoint.emitCommand(WebRtcTransportProvider, 'endSession', request, this.context);
443
443
  }
444
+ async closeAllSessions() {
445
+ const device = this.endpoint.stateOf(MatterbridgeServer);
446
+ for (const session of this.state.currentSessions)
447
+ this.#updateStreamReferenceCounts(session.videoStreams, session.audioStreams, -1);
448
+ this.state.currentSessions = [];
449
+ this.internal.trickledSessions.clear();
450
+ const webRtcPeers = [...this.internal.sessions.entries()];
451
+ this.internal.sessions.clear();
452
+ const results = await Promise.allSettled(webRtcPeers.map(async ([, webRtcPeer]) => webRtcPeer.close()));
453
+ for (const [index, result] of results.entries()) {
454
+ if (result.status === 'rejected') {
455
+ device.log.error(`MatterbridgeWebRtcTransportProviderServer.closeAllSessions: failed to close webRTC session ${webRtcPeers[index][0]}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
456
+ }
457
+ }
458
+ device.log.info(`MatterbridgeWebRtcTransportProviderServer.closeAllSessions: closed ${webRtcPeers.length} webRTC session(s) (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
459
+ }
444
460
  }
445
461
  (function (MatterbridgeWebRtcTransportProviderServer) {
446
462
  class State extends WebRtcTransportProviderServer.State {
@@ -458,3 +474,8 @@ export function createDefaultWebRtcTransportProviderClusterServer(endpoint, weri
458
474
  endpoint.behaviors.require(MatterbridgeWebRtcTransportProviderServer, { currentSessions: [], weriftOfferOptions });
459
475
  return endpoint;
460
476
  }
477
+ export async function closeWebRtcSessions(device) {
478
+ await Promise.all([device, ...device.getChildEndpoints()]
479
+ .filter((endpoint) => endpoint.behaviors.has(MatterbridgeWebRtcTransportProviderServer))
480
+ .map(async (endpoint) => endpoint.act(async (agent) => agent.get(MatterbridgeWebRtcTransportProviderServer).closeAllSessions())));
481
+ }
@@ -52,6 +52,7 @@ export declare class WeriftWebRtcSession {
52
52
  private getConfiguredVideoSource;
53
53
  private buildFfmpegVideoInputArgs;
54
54
  private logFfmpegStderr;
55
+ private watchGeneratorExit;
55
56
  private generateVideoTrack;
56
57
  private preferVideoCodecOnTransceivers;
57
58
  private preferAudioCodecOnTransceivers;
@@ -70,6 +71,5 @@ export declare class WeriftWebRtcSession {
70
71
  private logDtlsTransportStateChanges;
71
72
  private logDiagnosticsSnapshot;
72
73
  close(): Promise<void>;
73
- static closeAll(): Promise<void>;
74
74
  }
75
75
  export {};
@@ -78,7 +78,7 @@ export class WeriftWebRtcSession {
78
78
  this.log.info(`Peer connection state: ${state}`);
79
79
  });
80
80
  this.log.debug(`Created RTCPeerConnection with codecs: audio=[OPUS, PCMU], video=[H264, VP8] for session ${webRtcSessionId}`);
81
- this.diagnosticsInterval = setInterval(() => void this.logDiagnosticsSnapshot(), WeriftWebRtcSession.DIAGNOSTICS_INTERVAL_MS);
81
+ this.diagnosticsInterval = setInterval(() => void this.logDiagnosticsSnapshot(), WeriftWebRtcSession.DIAGNOSTICS_INTERVAL_MS).unref();
82
82
  WeriftWebRtcSession.activeSessions.add(this);
83
83
  if (!WeriftWebRtcSession.exitHandlerRegistered) {
84
84
  WeriftWebRtcSession.exitHandlerRegistered = true;
@@ -253,6 +253,14 @@ export class WeriftWebRtcSession {
253
253
  this.log.debug(`Ffmpeg ${kind} generator: ${message}`);
254
254
  });
255
255
  }
256
+ watchGeneratorExit(generator, kind) {
257
+ generator.once('exit', (code, signal) => {
258
+ const attached = kind === 'video' ? this.testVideoGenerator : this.testAudioGenerator;
259
+ if (attached !== generator)
260
+ return;
261
+ this.log.warn(`Ffmpeg ${kind} generator exited unexpectedly with ${signal ? `signal ${signal}` : `code ${code}`}: the ${kind} track carries no more media`);
262
+ });
263
+ }
256
264
  async generateVideoTrack(codec) {
257
265
  if (this.testVideoAttached)
258
266
  return;
@@ -315,6 +323,7 @@ export class WeriftWebRtcSession {
315
323
  ];
316
324
  const generator = runFfmpeg(ffmpegArgs);
317
325
  this.logFfmpegStderr(generator, 'video');
326
+ this.watchGeneratorExit(generator, 'video');
318
327
  generator.once('error', (error) => {
319
328
  this.log.warn(`Video generator failed: ${getErrorMessage(error)}`);
320
329
  });
@@ -448,6 +457,7 @@ export class WeriftWebRtcSession {
448
457
  ];
449
458
  const generator = runFfmpeg(ffmpegArgs);
450
459
  this.logFfmpegStderr(generator, 'audio');
460
+ this.watchGeneratorExit(generator, 'audio');
451
461
  generator.once('error', (error) => {
452
462
  this.log.warn(`Audio generator failed: ${getErrorMessage(error)}`);
453
463
  });
@@ -462,6 +472,7 @@ export class WeriftWebRtcSession {
462
472
  }
463
473
  cleanupTestVideoArtifacts() {
464
474
  if (this.testVideoGenerator) {
475
+ this.log.debug(`Stopping ffmpeg video generator (pid ${this.testVideoGenerator.pid ?? 'unknown'})`);
465
476
  this.testVideoGenerator.kill('SIGTERM');
466
477
  this.testVideoGenerator = undefined;
467
478
  }
@@ -471,6 +482,7 @@ export class WeriftWebRtcSession {
471
482
  }
472
483
  cleanupTestAudioArtifacts() {
473
484
  if (this.testAudioGenerator) {
485
+ this.log.debug(`Stopping ffmpeg audio generator (pid ${this.testAudioGenerator.pid ?? 'unknown'})`);
474
486
  this.testAudioGenerator.kill('SIGTERM');
475
487
  this.testAudioGenerator = undefined;
476
488
  }
@@ -612,10 +624,4 @@ export class WeriftWebRtcSession {
612
624
  WeriftWebRtcSession.activeSessions.delete(this);
613
625
  this.log.info(`RTCPeerConnection closed (connectionState=${this.peerConnection.connectionState})`);
614
626
  }
615
- static async closeAll() {
616
- for (const session of WeriftWebRtcSession.activeSessions) {
617
- session.log.info(`Closing session ${session.webRtcSessionId} as part of closeAll()`);
618
- await session.close();
619
- }
620
- }
621
627
  }
@@ -7,3 +7,4 @@ export * from '../floodlightCamera.js';
7
7
  export * from '../intercom.js';
8
8
  export * from '../snapshotCamera.js';
9
9
  export * from '../videoDoorbell.js';
10
+ export { closeWebRtcSessions } from '../../behaviors/webRtcTransportProviderServer.js';
@@ -7,3 +7,4 @@ export * from '../floodlightCamera.js';
7
7
  export * from '../intercom.js';
8
8
  export * from '../snapshotCamera.js';
9
9
  export * from '../videoDoorbell.js';
10
+ export { closeWebRtcSessions } from '../../behaviors/webRtcTransportProviderServer.js';
@@ -22,7 +22,6 @@ export class Camera extends MatterbridgeEndpoint {
22
22
  if (identifyType !== Identify.IdentifyType.None) {
23
23
  this.createDefaultIdentifyClusterServer(identifyTime, identifyType);
24
24
  }
25
- this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Chime');
26
25
  switch (powerSourceType) {
27
26
  case 'Rechargeable':
28
27
  this.createDefaultPowerSourceRechargeableBatteryClusterServer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.14",
3
+ "version": "3.10.15-dev-20261018-1598399",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -136,12 +136,12 @@
136
136
  },
137
137
  "dependencies": {
138
138
  "@matter/main": "0.17.9",
139
- "@matterbridge/dgram": "3.10.14",
140
- "@matterbridge/thread": "3.10.14",
141
- "@matterbridge/types": "3.10.14",
142
- "@matterbridge/utils": "3.10.14",
139
+ "@matterbridge/dgram": "3.10.15-dev-20261018-1598399",
140
+ "@matterbridge/thread": "3.10.15-dev-20261018-1598399",
141
+ "@matterbridge/types": "3.10.15-dev-20261018-1598399",
142
+ "@matterbridge/utils": "3.10.15-dev-20261018-1598399",
143
143
  "escape-html": "1.0.3",
144
- "express": "5.2.1",
144
+ "express": "5.3.0",
145
145
  "express-rate-limit": "8.7.1",
146
146
  "multer": "2.4.0",
147
147
  "node-ansi-logger": "3.3.1",