mediasoup 3.20.8 → 3.20.9

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.
Files changed (135) hide show
  1. package/README.md +8 -2
  2. package/node/lib/WebRtcServer.d.ts.map +1 -1
  3. package/node/lib/WebRtcServer.js +0 -7
  4. package/node/lib/WebRtcServerTypes.d.ts +0 -5
  5. package/node/lib/WebRtcServerTypes.d.ts.map +1 -1
  6. package/node/lib/Worker.d.ts +1 -1
  7. package/node/lib/Worker.d.ts.map +1 -1
  8. package/node/lib/Worker.js +1 -11
  9. package/node/lib/WorkerTypes.d.ts +0 -9
  10. package/node/lib/WorkerTypes.d.ts.map +1 -1
  11. package/node/lib/fbs/plain-transport/connect-response.d.ts.map +1 -1
  12. package/node/lib/fbs/plain-transport/connect-response.js +0 -1
  13. package/node/lib/fbs/web-rtc-server/dump-response.d.ts +2 -9
  14. package/node/lib/fbs/web-rtc-server/dump-response.d.ts.map +1 -1
  15. package/node/lib/fbs/web-rtc-server/dump-response.js +5 -33
  16. package/node/lib/fbs/web-rtc-server.d.ts +0 -1
  17. package/node/lib/fbs/web-rtc-server.d.ts.map +1 -1
  18. package/node/lib/fbs/web-rtc-server.js +1 -4
  19. package/node/lib/fbs/worker/dump-response.d.ts +1 -5
  20. package/node/lib/fbs/worker/dump-response.d.ts.map +1 -1
  21. package/node/lib/fbs/worker/dump-response.js +3 -16
  22. package/node/lib/index.d.ts +1 -1
  23. package/node/lib/index.d.ts.map +1 -1
  24. package/node/lib/index.js +1 -2
  25. package/node/lib/test/test-PlainTransport.js +30 -0
  26. package/node/lib/test/test-WebRtcServer.js +0 -7
  27. package/node/lib/test/test-Worker.js +0 -1
  28. package/npm-scripts.mjs +132 -55
  29. package/package.json +6 -4
  30. package/worker/fbs/webRtcServer.fbs +0 -6
  31. package/worker/include/RTC/TransportTuple.hpp +70 -14
  32. package/worker/include/RTC/WebRtcServer.hpp +5 -2
  33. package/worker/include/Utils.hpp +6 -0
  34. package/worker/src/RTC/PortManager.cpp +16 -20
  35. package/worker/src/RTC/SeqManager.cpp +4 -1
  36. package/worker/src/RTC/TransportTuple.cpp +87 -50
  37. package/worker/src/RTC/WebRtcServer.cpp +20 -29
  38. package/worker/test/src/RTC/TestSeqManager.cpp +167 -62
  39. package/worker/test/src/RTC/TestTransportTuple.cpp +21 -14
  40. package/node/lib/fbs/data-consumer/binary.d.ts +0 -25
  41. package/node/lib/fbs/data-consumer/binary.d.ts.map +0 -1
  42. package/node/lib/fbs/data-consumer/binary.js +0 -110
  43. package/node/lib/fbs/data-consumer/data.d.ts +0 -10
  44. package/node/lib/fbs/data-consumer/data.d.ts.map +0 -1
  45. package/node/lib/fbs/data-consumer/data.js +0 -30
  46. package/node/lib/fbs/data-consumer/string.d.ts +0 -22
  47. package/node/lib/fbs/data-consumer/string.d.ts.map +0 -1
  48. package/node/lib/fbs/data-consumer/string.js +0 -92
  49. package/node/lib/fbs/data-producer/binary.d.ts +0 -25
  50. package/node/lib/fbs/data-producer/binary.d.ts.map +0 -1
  51. package/node/lib/fbs/data-producer/binary.js +0 -110
  52. package/node/lib/fbs/data-producer/data.d.ts +0 -10
  53. package/node/lib/fbs/data-producer/data.d.ts.map +0 -1
  54. package/node/lib/fbs/data-producer/data.js +0 -30
  55. package/node/lib/fbs/data-producer/string.d.ts +0 -22
  56. package/node/lib/fbs/data-producer/string.d.ts.map +0 -1
  57. package/node/lib/fbs/data-producer/string.js +0 -92
  58. package/node/lib/fbs/lib-uring/dump.d.ts +0 -27
  59. package/node/lib/fbs/lib-uring/dump.d.ts.map +0 -1
  60. package/node/lib/fbs/lib-uring/dump.js +0 -113
  61. package/node/lib/fbs/lib-uring.d.ts +0 -2
  62. package/node/lib/fbs/lib-uring.d.ts.map +0 -1
  63. package/node/lib/fbs/lib-uring.js +0 -8
  64. package/node/lib/fbs/message/type.d.ts +0 -7
  65. package/node/lib/fbs/message/type.d.ts.map +0 -1
  66. package/node/lib/fbs/message/type.js +0 -11
  67. package/node/lib/fbs/message-from-worker/body.d.ts +0 -12
  68. package/node/lib/fbs/message-from-worker/body.d.ts.map +0 -1
  69. package/node/lib/fbs/message-from-worker/body.js +0 -34
  70. package/node/lib/fbs/message-from-worker/message.d.ts +0 -30
  71. package/node/lib/fbs/message-from-worker/message.d.ts.map +0 -1
  72. package/node/lib/fbs/message-from-worker/message.js +0 -122
  73. package/node/lib/fbs/message-from-worker.d.ts +0 -3
  74. package/node/lib/fbs/message-from-worker.d.ts.map +0 -1
  75. package/node/lib/fbs/message-from-worker.js +0 -9
  76. package/node/lib/fbs/message-to-worker/body.d.ts +0 -10
  77. package/node/lib/fbs/message-to-worker/body.d.ts.map +0 -1
  78. package/node/lib/fbs/message-to-worker/body.js +0 -30
  79. package/node/lib/fbs/message-to-worker/message.d.ts +0 -29
  80. package/node/lib/fbs/message-to-worker/message.d.ts.map +0 -1
  81. package/node/lib/fbs/message-to-worker/message.js +0 -122
  82. package/node/lib/fbs/message-to-worker.d.ts +0 -3
  83. package/node/lib/fbs/message-to-worker.d.ts.map +0 -1
  84. package/node/lib/fbs/message-to-worker.js +0 -9
  85. package/node/lib/fbs/notification-from-worker/body.d.ts +0 -44
  86. package/node/lib/fbs/notification-from-worker/body.d.ts.map +0 -1
  87. package/node/lib/fbs/notification-from-worker/body.js +0 -98
  88. package/node/lib/fbs/notification-from-worker/event.d.ts +0 -31
  89. package/node/lib/fbs/notification-from-worker/event.d.ts.map +0 -1
  90. package/node/lib/fbs/notification-from-worker/event.js +0 -35
  91. package/node/lib/fbs/notification-from-worker/notification.d.ts +0 -52
  92. package/node/lib/fbs/notification-from-worker/notification.d.ts.map +0 -1
  93. package/node/lib/fbs/notification-from-worker/notification.js +0 -140
  94. package/node/lib/fbs/notification-from-worker.d.ts +0 -4
  95. package/node/lib/fbs/notification-from-worker.d.ts.map +0 -1
  96. package/node/lib/fbs/notification-from-worker.js +0 -11
  97. package/node/lib/fbs/notification-to-worker/body.d.ts +0 -12
  98. package/node/lib/fbs/notification-to-worker/body.d.ts.map +0 -1
  99. package/node/lib/fbs/notification-to-worker/body.js +0 -34
  100. package/node/lib/fbs/notification-to-worker/event.d.ts +0 -6
  101. package/node/lib/fbs/notification-to-worker/event.d.ts.map +0 -1
  102. package/node/lib/fbs/notification-to-worker/event.js +0 -10
  103. package/node/lib/fbs/notification-to-worker/notification.d.ts +0 -36
  104. package/node/lib/fbs/notification-to-worker/notification.d.ts.map +0 -1
  105. package/node/lib/fbs/notification-to-worker/notification.js +0 -140
  106. package/node/lib/fbs/notification-to-worker.d.ts +0 -4
  107. package/node/lib/fbs/notification-to-worker.d.ts.map +0 -1
  108. package/node/lib/fbs/notification-to-worker.js +0 -11
  109. package/node/lib/fbs/sctp-parameters/num-sctp-streams.d.ts +0 -24
  110. package/node/lib/fbs/sctp-parameters/num-sctp-streams.d.ts.map +0 -1
  111. package/node/lib/fbs/sctp-parameters/num-sctp-streams.js +0 -102
  112. package/node/lib/fbs/trace-events/fir-trace-info.d.ts +0 -21
  113. package/node/lib/fbs/trace-events/fir-trace-info.d.ts.map +0 -1
  114. package/node/lib/fbs/trace-events/fir-trace-info.js +0 -90
  115. package/node/lib/fbs/trace-events/key-frame-trace-info.d.ts +0 -25
  116. package/node/lib/fbs/trace-events/key-frame-trace-info.d.ts.map +0 -1
  117. package/node/lib/fbs/trace-events/key-frame-trace-info.js +0 -104
  118. package/node/lib/fbs/trace-events/pli-trace-info.d.ts +0 -21
  119. package/node/lib/fbs/trace-events/pli-trace-info.d.ts.map +0 -1
  120. package/node/lib/fbs/trace-events/pli-trace-info.js +0 -90
  121. package/node/lib/fbs/trace-events/rtp-trace-info.d.ts +0 -25
  122. package/node/lib/fbs/trace-events/rtp-trace-info.d.ts.map +0 -1
  123. package/node/lib/fbs/trace-events/rtp-trace-info.js +0 -104
  124. package/node/lib/fbs/trace-events/trace-event-type.d.ts +0 -8
  125. package/node/lib/fbs/trace-events/trace-event-type.d.ts.map +0 -1
  126. package/node/lib/fbs/trace-events/trace-event-type.js +0 -12
  127. package/node/lib/fbs/trace-events/trace-info.d.ts +0 -14
  128. package/node/lib/fbs/trace-events/trace-info.d.ts.map +0 -1
  129. package/node/lib/fbs/trace-events/trace-info.js +0 -38
  130. package/node/lib/fbs/trace-events.d.ts +0 -7
  131. package/node/lib/fbs/trace-events.d.ts.map +0 -1
  132. package/node/lib/fbs/trace-events.js +0 -20
  133. package/node/lib/fbs/web-rtc-server/tuple-hash.d.ts +0 -25
  134. package/node/lib/fbs/web-rtc-server/tuple-hash.d.ts.map +0 -1
  135. package/node/lib/fbs/web-rtc-server/tuple-hash.js +0 -104
@@ -96,7 +96,6 @@ test('worker.createWebRtcServer() succeeds', async () => {
96
96
  tcpServers: [{ ip: '127.0.0.1', port: port2 }],
97
97
  webRtcTransportIds: [],
98
98
  localIceUsernameFragments: [],
99
- tupleHashes: [],
100
99
  });
101
100
  // API not exposed in the interface.
102
101
  expect(ctx.worker.webRtcServersForTesting.size).toBe(1);
@@ -154,7 +153,6 @@ test('worker.createWebRtcServer() with portRange succeeds', async () => {
154
153
  tcpServers: [{ ip: '127.0.0.1', port: port2 }],
155
154
  webRtcTransportIds: [],
156
155
  localIceUsernameFragments: [],
157
- tupleHashes: [],
158
156
  });
159
157
  // API not exposed in the interface.
160
158
  expect(ctx.worker.webRtcServersForTesting.size).toBe(1);
@@ -200,7 +198,6 @@ test('worker.createWebRtcServer() without specifying port/portRange succeeds', a
200
198
  tcpServers: [{ ip: '127.0.0.1', port: expect.any(Number) }],
201
199
  webRtcTransportIds: [],
202
200
  localIceUsernameFragments: [],
203
- tupleHashes: [],
204
201
  });
205
202
  // API not exposed in the interface.
206
203
  expect(ctx.worker.webRtcServersForTesting.size).toBe(1);
@@ -403,7 +400,6 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and transport is
403
400
  localIceUsernameFragments: [
404
401
  { /* localIceUsernameFragment: xxx, */ webRtcTransportId: transport.id },
405
402
  ],
406
- tupleHashes: [],
407
403
  });
408
404
  transport.close();
409
405
  expect(transport.closed).toBe(true);
@@ -419,7 +415,6 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and transport is
419
415
  tcpServers: [{ ip: '127.0.0.1', port: port2 }],
420
416
  webRtcTransportIds: [],
421
417
  localIceUsernameFragments: [],
422
- tupleHashes: [],
423
418
  });
424
419
  }, 2000);
425
420
  test('router.createWebRtcTransport() with webRtcServer succeeds and webRtcServer is closed', async () => {
@@ -482,7 +477,6 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and webRtcServer
482
477
  localIceUsernameFragments: [
483
478
  { /* localIceUsernameFragment: xxx, */ webRtcTransportId: transport.id },
484
479
  ],
485
- tupleHashes: [],
486
480
  });
487
481
  // Let's restart ICE in the transport so it should add a new entry in
488
482
  // localIceUsernameFragments in the WebRtcServer.
@@ -496,7 +490,6 @@ test('router.createWebRtcTransport() with webRtcServer succeeds and webRtcServer
496
490
  { /* localIceUsernameFragment: xxx, */ webRtcTransportId: transport.id },
497
491
  { /* localIceUsernameFragment: yyy, */ webRtcTransportId: transport.id },
498
492
  ],
499
- tupleHashes: [],
500
493
  });
501
494
  const onObserverClose = jest.fn();
502
495
  webRtcServer.observer.once('close', onObserverClose);
@@ -71,7 +71,6 @@ test('mediasoup.createWorker() succeeds', async () => {
71
71
  dtlsCertificateFile: path.join(__dirname, 'data', 'dtls-cert.pem'),
72
72
  dtlsPrivateKeyFile: path.join(__dirname, 'data', 'dtls-key.pem'),
73
73
  libwebrtcFieldTrials: 'WebRTC-Bwe-AlrLimitedBackoff/Disabled/',
74
- disableLiburing: true,
75
74
  appData: { foo: 456 },
76
75
  });
77
76
  expect(worker2.constructor.name).toBe('WorkerImpl');
package/npm-scripts.mjs CHANGED
@@ -8,7 +8,9 @@ import * as tar from 'tar';
8
8
  import pkg from './package.json' with { type: 'json' };
9
9
 
10
10
  const IS_WINDOWS = os.platform() === 'win32';
11
- const MAYOR_VERSION = pkg.version.split('.')[0];
11
+ // Main Git branch is 'v' concatenated with the major SEMVER number of the
12
+ // "version" field in package.json.
13
+ const MAIN_BRANCH = `v${pkg.version.split('.')[0]}`;
12
14
  const PYTHON = getPython();
13
15
  const PIP_INVOKE_DIR = path.resolve('worker/pip_invoke');
14
16
  const WORKER_RELEASE_DIR = 'worker/out/Release';
@@ -17,8 +19,6 @@ const WORKER_RELEASE_BIN = IS_WINDOWS
17
19
  : 'mediasoup-worker';
18
20
  const WORKER_RELEASE_BIN_PATH = `${WORKER_RELEASE_DIR}/${WORKER_RELEASE_BIN}`;
19
21
  const WORKER_PREBUILD_DIR = 'worker/prebuild';
20
- const GH_OWNER = 'versatica';
21
- const GH_REPO = 'mediasoup';
22
22
 
23
23
  // Paths for ESLint to check.
24
24
  const ESLINT_PATHS = [
@@ -27,6 +27,7 @@ const ESLINT_PATHS = [
27
27
  'knip.config.mjs',
28
28
  'node/src',
29
29
  'npm-scripts.mjs',
30
+ 'rust-scripts.mjs',
30
31
  'worker/scripts',
31
32
  ];
32
33
 
@@ -47,6 +48,7 @@ const PRETTIER_PATHS = [
47
48
  'node/src',
48
49
  'npm-scripts.mjs',
49
50
  'package.json',
51
+ 'rust-scripts.mjs',
50
52
  'tsconfig.json',
51
53
  'worker/scripts',
52
54
  ];
@@ -137,13 +139,13 @@ async function run() {
137
139
  }
138
140
 
139
141
  case 'typescript:build': {
140
- buildTypescript({ force: true });
142
+ buildTypescript({ force: true, args: taskArgs });
141
143
 
142
144
  break;
143
145
  }
144
146
 
145
147
  case 'typescript:watch': {
146
- watchTypescript();
148
+ watchTypescript({ args: taskArgs });
147
149
 
148
150
  break;
149
151
  }
@@ -215,7 +217,7 @@ async function run() {
215
217
  }
216
218
 
217
219
  case 'test:node': {
218
- testNode();
220
+ testNode({ args: taskArgs });
219
221
 
220
222
  break;
221
223
  }
@@ -227,7 +229,7 @@ async function run() {
227
229
  }
228
230
 
229
231
  case 'coverage:node': {
230
- coverageNode();
232
+ coverageNode({ args: taskArgs });
231
233
 
232
234
  break;
233
235
  }
@@ -245,7 +247,7 @@ async function run() {
245
247
  }
246
248
 
247
249
  case 'release': {
248
- await release();
250
+ await release({ args: taskArgs });
249
251
 
250
252
  break;
251
253
  }
@@ -317,7 +319,7 @@ function deleteNodeLib() {
317
319
  fs.rmSync('node/lib', { recursive: true, force: true });
318
320
  }
319
321
 
320
- function buildTypescript({ force }) {
322
+ function buildTypescript({ force, args = '' }) {
321
323
  // Skip JavaScript code generation if the output already exists, unless forced.
322
324
  if (!force && fs.existsSync('node/lib')) {
323
325
  return;
@@ -327,15 +329,15 @@ function buildTypescript({ force }) {
327
329
 
328
330
  deleteNodeLib();
329
331
 
330
- executeCmd(`tsc ${taskArgs}`);
332
+ executeCmd(`tsc ${args}`);
331
333
  }
332
334
 
333
- function watchTypescript() {
335
+ function watchTypescript({ args = '' } = {}) {
334
336
  logInfo('watchTypescript()');
335
337
 
336
338
  deleteNodeLib();
337
339
 
338
- executeCmd(`tsc --watch ${taskArgs}`);
340
+ executeCmd(`tsc --watch ${args}`);
339
341
  }
340
342
 
341
343
  function buildWorker() {
@@ -487,10 +489,10 @@ function flatcWorker() {
487
489
  executeCmd(`"${PYTHON}" -m invoke -r worker flatc`);
488
490
  }
489
491
 
490
- function testNode() {
492
+ function testNode({ args = '' } = {}) {
491
493
  logInfo('testNode()');
492
494
 
493
- executeCmd(`jest --silent false --detectOpenHandles ${taskArgs}`);
495
+ executeCmd(`jest --silent false --detectOpenHandles ${args}`);
494
496
  }
495
497
 
496
498
  function testWorker() {
@@ -501,10 +503,10 @@ function testWorker() {
501
503
  executeCmd(`"${PYTHON}" -m invoke -r worker test`);
502
504
  }
503
505
 
504
- function coverageNode() {
506
+ function coverageNode({ args = '' } = {}) {
505
507
  logInfo('coverageNode()');
506
508
 
507
- executeCmd(`jest --coverage ${taskArgs}`);
509
+ executeCmd(`jest --coverage ${args}`);
508
510
  executeCmd('open-cli coverage/lcov-report/index.html');
509
511
  }
510
512
 
@@ -542,6 +544,18 @@ function publishDryRun() {
542
544
  async function checkRelease() {
543
545
  logInfo('checkRelease()');
544
546
 
547
+ // Verify that CHANGELOG.md has an entry for the new version (and grab its
548
+ // changes, used as the GitHub release body) before the slow build steps.
549
+ let versionChanges;
550
+
551
+ try {
552
+ versionChanges = await getVersionChanges();
553
+ } catch (error) {
554
+ logError(error.message);
555
+
556
+ exitWithError();
557
+ }
558
+
545
559
  installNodeDeps();
546
560
  await flatcNode({ force: true });
547
561
  buildTypescript({ force: true });
@@ -553,41 +567,55 @@ async function checkRelease() {
553
567
  // Validate packaging (the `files` list in package.json) before the
554
568
  // irreversible release steps (git push, GitHub release, npm publish).
555
569
  publishDryRun();
570
+
571
+ return { versionChanges };
556
572
  }
557
573
 
558
- async function release() {
574
+ async function release({ args = '' } = {}) {
559
575
  logInfo('release()');
560
576
 
561
- let octokit;
562
- let versionChanges;
577
+ const version = args.trim();
563
578
 
564
- try {
565
- octokit = await getOctokit();
566
- versionChanges = await getVersionChanges();
567
- } catch (error) {
568
- logError(error.message);
579
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
580
+ logError(
581
+ `release() | a SEMVER 'x.y.z' argument is required, but got '${version}'`
582
+ );
583
+
584
+ exitWithError();
585
+ }
586
+
587
+ // Must be on the main branch.
588
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
589
+ encoding: 'utf-8',
590
+ }).trim();
591
+
592
+ if (branch !== MAIN_BRANCH) {
593
+ logError(
594
+ `release() | must be on '${MAIN_BRANCH}' branch, but it is on '${branch}' branch`
595
+ );
569
596
 
570
597
  exitWithError();
571
598
  }
572
599
 
600
+ // Clean working tree required before bumping the version.
601
+ checkGitClean();
602
+
603
+ // Lint, test, build, publish dry-run, and verify the CHANGELOG entry (of the
604
+ // previous version still in package.json, which is harmless).
573
605
  await checkRelease();
574
- executeCmd(`git commit -am '${pkg.version}'`);
575
- executeCmd(`git tag -a ${pkg.version} -m '${pkg.version}'`);
576
- executeCmd(`git push origin v${MAYOR_VERSION}`);
577
- executeCmd(`git push origin '${pkg.version}'`);
578
-
579
- logInfo('creating release in GitHub');
580
-
581
- await octokit.repos.createRelease({
582
- owner: GH_OWNER,
583
- repo: GH_REPO,
584
- name: pkg.version,
585
- body: versionChanges,
586
- tag_name: pkg.version,
587
- draft: false,
588
- });
589
606
 
590
- executeInteractiveCmd('npm publish');
607
+ // Bump the version in package.json + package-lock.json and in CHANGELOG.md.
608
+ executeCmd(`npm version ${version} --no-git-tag-version`);
609
+ await updateChangelog(version);
610
+
611
+ // Commit the bump, tag it, and push both. The pushed tag triggers
612
+ // mediasoup-npm-publish.yaml, which checks, creates the GitHub release and
613
+ // publishes to NPM; on its success mediasoup-worker-prebuild.yaml builds and
614
+ // uploads the prebuilt binaries.
615
+ executeCmd(`git commit -am 'version ${version}'`);
616
+ executeCmd(`git tag -a ${version} -m '${version}'`);
617
+ executeCmd(`git push origin ${MAIN_BRANCH}`);
618
+ executeCmd(`git push origin '${version}'`);
591
619
  }
592
620
 
593
621
  function ensureDir(dir) {
@@ -757,19 +785,21 @@ async function downloadPrebuiltWorker() {
757
785
  });
758
786
  }
759
787
 
760
- async function getOctokit() {
761
- if (!process.env.GITHUB_TOKEN) {
762
- throw new Error('missing GITHUB_TOKEN environment variable');
763
- }
788
+ function checkGitClean() {
789
+ logInfo('checkGitClean()');
764
790
 
765
- // NOTE: Load dep on demand since it's a devDependency.
766
- const { Octokit } = await import('@octokit/rest');
767
-
768
- const octokit = new Octokit({
769
- auth: process.env.GITHUB_TOKEN,
791
+ const status = execSync('git status --porcelain', {
792
+ encoding: 'utf-8',
793
+ stdio: ['ignore', 'pipe', 'ignore'],
770
794
  });
771
795
 
772
- return octokit;
796
+ if (status.trim()) {
797
+ logError(
798
+ 'checkGitClean() | Git working tree is not clean, commit or stash your changes first'
799
+ );
800
+
801
+ exitWithError();
802
+ }
773
803
  }
774
804
 
775
805
  async function getVersionChanges() {
@@ -785,9 +815,26 @@ async function getVersionChanges() {
785
815
  const entry = entries[idx];
786
816
 
787
817
  if (entry.type === 'heading' && entry.text === pkg.version) {
788
- const changes = entries[idx + 1].raw;
818
+ // Collect every token after the matching heading until the next heading.
819
+ // NOTE: We cannot just use `entries[idx + 1].raw` because `marked`
820
+ // inserts a `space` token between the heading and its content.
821
+ let changes = '';
822
+
823
+ for (let next = idx + 1; next < entries.length; ++next) {
824
+ if (entries[next].type === 'heading') {
825
+ break;
826
+ }
827
+
828
+ changes += entries[next].raw;
829
+ }
830
+
831
+ changes = changes.trim();
789
832
 
790
- return changes;
833
+ if (changes) {
834
+ return changes;
835
+ }
836
+
837
+ break;
791
838
  }
792
839
  }
793
840
 
@@ -797,6 +844,35 @@ async function getVersionChanges() {
797
844
  );
798
845
  }
799
846
 
847
+ async function updateChangelog(version) {
848
+ logInfo(`updateChangelog() [version:${version}]`);
849
+
850
+ // NOTE: Load dep on demand since it's a devDependency.
851
+ const marked = await import('marked');
852
+
853
+ const changelog = fs.readFileSync('./CHANGELOG.md', { encoding: 'utf-8' });
854
+ const tokens = marked.lexer(changelog);
855
+
856
+ // Locate the top "### NEXT" heading.
857
+ const nextHeading = tokens.find(
858
+ token =>
859
+ token.type === 'heading' && token.depth === 3 && token.text === 'NEXT'
860
+ );
861
+
862
+ if (!nextHeading) {
863
+ throw new Error("no '### NEXT' heading found in CHANGELOG.md");
864
+ }
865
+
866
+ // Insert "### <version>" right below "### NEXT" (keeping the empty "### NEXT"
867
+ // for future unreleased changes), preserving the heading's trailing newlines.
868
+ const updatedChangelog = changelog.replace(
869
+ nextHeading.raw,
870
+ `### NEXT\n\n### ${version}${nextHeading.raw.slice('### NEXT'.length)}`
871
+ );
872
+
873
+ fs.writeFileSync('./CHANGELOG.md', updatedChangelog);
874
+ }
875
+
800
876
  function executeCmd(command) {
801
877
  logInfo(`executeCmd(): ${command}`);
802
878
 
@@ -809,11 +885,12 @@ function executeCmd(command) {
809
885
  }
810
886
  }
811
887
 
812
- function executeInteractiveCmd(command) {
813
- logInfo(`executeInteractiveCmd(): ${command}`);
888
+ // eslint-disable-next-line no-unused-vars
889
+ function executeInteractiveCmd(command, { cwd } = {}) {
890
+ logInfo(`executeInteractiveCmd(): ${command}${cwd ? ` [cwd:${cwd}]` : ''}`);
814
891
 
815
892
  try {
816
- execSync(command, { stdio: 'inherit', env: process.env });
893
+ execSync(command, { cwd, stdio: 'inherit', env: process.env });
817
894
  } catch (error) {
818
895
  logError(`executeInteractiveCmd() failed, exiting: ${error}`);
819
896
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasoup",
3
- "version": "3.20.8",
3
+ "version": "3.20.9",
4
4
  "description": "Cutting Edge WebRTC Video Conferencing",
5
5
  "contributors": [
6
6
  "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
@@ -98,7 +98,9 @@
98
98
  "coverage:node": "node npm-scripts.mjs coverage:node",
99
99
  "publish:dry-run": "node npm-scripts.mjs publish:dry-run",
100
100
  "release:check": "node npm-scripts.mjs release:check",
101
- "release": "node npm-scripts.mjs release"
101
+ "release": "node npm-scripts.mjs release",
102
+ "release:rust:check": "node rust-scripts.mjs release:check",
103
+ "release:rust": "node rust-scripts.mjs release"
102
104
  },
103
105
  "dependencies": {
104
106
  "debug": "^4.4.3",
@@ -114,13 +116,13 @@
114
116
  "@types/debug": "^4.1.13",
115
117
  "@types/ini": "^4.1.1",
116
118
  "@types/jest": "^30.0.0",
117
- "@types/node": "^25.9.3",
119
+ "@types/node": "^26.0.0",
118
120
  "eslint": "^10.5.0",
119
121
  "eslint-config-prettier": "^10.1.8",
120
122
  "eslint-plugin-jest": "^29.15.2",
121
123
  "eslint-plugin-prettier": "^5.5.6",
122
124
  "globals": "^17.6.0",
123
- "ini": "^6.0.0",
125
+ "ini": "^7.0.0",
124
126
  "jest": "^30.4.2",
125
127
  "knip": "^6.17.1",
126
128
  "marked": "^18.0.5",
@@ -12,17 +12,11 @@ table IceUserNameFragment {
12
12
  web_rtc_transport_id: string (required);
13
13
  }
14
14
 
15
- table TupleHash {
16
- tuple_hash: uint64;
17
- web_rtc_transport_id: string (required);
18
- }
19
-
20
15
  table DumpResponse {
21
16
  id: string (required);
22
17
  udp_sockets: [IpPort] (required);
23
18
  tcp_servers: [IpPort] (required);
24
19
  web_rtc_transport_ids: [string] (required);
25
20
  local_ice_username_fragments: [IceUserNameFragment] (required);
26
- tuple_hashes: [TupleHash] (required);
27
21
  }
28
22
 
@@ -23,28 +23,60 @@ namespace RTC
23
23
  TCP = 2
24
24
  };
25
25
 
26
+ class TupleKey
27
+ {
28
+ private:
29
+ friend class TransportTuple;
30
+ friend class TupleKeyHash;
31
+
32
+ public:
33
+ TupleKey() = default;
34
+ TupleKey(Protocol protocol, const void* udpSocketOrTcpConnection, const struct sockaddr* udpRemoteAddr)
35
+ : protocol(protocol),
36
+ udpSocketOrTcpConnection(udpSocketOrTcpConnection),
37
+ udpRemoteAddr(udpRemoteAddr) {};
38
+
39
+ bool operator==(const TupleKey& other) const noexcept;
40
+ bool operator!=(const TupleKey& other) const noexcept
41
+ {
42
+ return !(*this == other);
43
+ };
44
+
45
+ private:
46
+ Protocol protocol{ Protocol::UDP };
47
+ // The local endpoint is identified by object pointer identity: the
48
+ // UdpSocket or the TcpConnection. For TCP the connection alone identifies
49
+ // the whole tuple, so `udpRemoteAddr` is unused.
50
+ const void* udpSocketOrTcpConnection{ nullptr };
51
+ const struct sockaddr* udpRemoteAddr{ nullptr };
52
+ };
53
+
54
+ struct TupleKeyHash
55
+ {
56
+ size_t operator()(const TupleKey& key) const noexcept;
57
+ };
58
+
26
59
  static Protocol ProtocolFromFbs(FBS::Transport::Protocol protocol);
27
60
  static FBS::Transport::Protocol ProtocolToFbs(Protocol protocol);
28
- static uint64_t GenerateFnv1aHash(const uint8_t* data, size_t size);
29
61
 
30
62
  public:
31
63
  TransportTuple(RTC::UdpSocket* udpSocket, const struct sockaddr* udpRemoteAddr)
32
64
  : udpSocket(udpSocket),
33
65
  udpRemoteAddr(const_cast<struct sockaddr*>(udpRemoteAddr)),
34
- protocol(Protocol::UDP)
66
+ protocol(Protocol::UDP),
67
+ tupleKey(Protocol::UDP, udpSocket, udpRemoteAddr)
35
68
  {
36
- GenerateHash();
37
69
  }
38
70
 
39
71
  explicit TransportTuple(RTC::TcpConnection* tcpConnection)
40
- : tcpConnection(tcpConnection), protocol(Protocol::TCP)
72
+ : tcpConnection(tcpConnection),
73
+ protocol(Protocol::TCP),
74
+ tupleKey(Protocol::TCP, tcpConnection, nullptr)
41
75
  {
42
- GenerateHash();
43
76
  }
44
77
 
45
78
  explicit TransportTuple(const TransportTuple* tuple)
46
- : hash(tuple->hash),
47
- udpSocket(tuple->udpSocket),
79
+ : udpSocket(tuple->udpSocket),
48
80
  udpRemoteAddr(tuple->udpRemoteAddr),
49
81
  tcpConnection(tuple->tcpConnection),
50
82
  localAnnouncedAddress(tuple->localAnnouncedAddress),
@@ -54,6 +86,10 @@ namespace RTC
54
86
  {
55
87
  StoreUdpRemoteAddress();
56
88
  }
89
+ else
90
+ {
91
+ this->tupleKey = TupleKey(this->protocol, this->tcpConnection, nullptr);
92
+ }
57
93
  }
58
94
 
59
95
  public:
@@ -70,11 +106,32 @@ namespace RTC
70
106
  this->udpRemoteAddrStorage = Utils::IP::CopyAddress(this->udpRemoteAddr);
71
107
  this->udpRemoteAddr =
72
108
  reinterpret_cast<struct sockaddr*>(std::addressof(this->udpRemoteAddrStorage));
109
+ this->tupleKey = TupleKey(this->protocol, this->udpSocket, this->udpRemoteAddr);
73
110
  }
74
111
 
75
112
  bool Compare(const TransportTuple* tuple) const
76
113
  {
77
- return this->hash == tuple->hash;
114
+ if (this->protocol != tuple->protocol)
115
+ {
116
+ return false;
117
+ }
118
+
119
+ switch (this->protocol)
120
+ {
121
+ case Protocol::UDP:
122
+ {
123
+ return (
124
+ this->udpSocket == tuple->udpSocket &&
125
+ Utils::IP::CompareAddresses(this->udpRemoteAddr, tuple->udpRemoteAddr));
126
+ }
127
+
128
+ case Protocol::TCP:
129
+ {
130
+ return (this->tcpConnection == tuple->tcpConnection);
131
+ }
132
+ }
133
+
134
+ return true;
78
135
  }
79
136
 
80
137
  void SetLocalAnnouncedAddress(std::string& localAnnouncedAddress)
@@ -147,12 +204,10 @@ namespace RTC
147
204
  }
148
205
  }
149
206
 
150
- private:
151
- void SetHash();
152
- void GenerateHash();
153
-
154
- public:
155
- uint64_t hash{ 0u };
207
+ const TupleKey& GetTupleKey() const
208
+ {
209
+ return this->tupleKey;
210
+ }
156
211
 
157
212
  private:
158
213
  // Passed by argument.
@@ -163,6 +218,7 @@ namespace RTC
163
218
  // Others.
164
219
  struct sockaddr_storage udpRemoteAddrStorage{};
165
220
  Protocol protocol;
221
+ TupleKey tupleKey;
166
222
  };
167
223
  } // namespace RTC
168
224
 
@@ -10,6 +10,7 @@
10
10
  #include "RTC/UdpSocket.hpp"
11
11
  #include "RTC/WebRtcTransport.hpp"
12
12
  #include "SharedInterface.hpp"
13
+ #include "TransportTuple.hpp"
13
14
  #include <flatbuffers/flatbuffers.h>
14
15
  #include <ankerl/unordered_dense.h>
15
16
  #include <string>
@@ -118,8 +119,10 @@ namespace RTC
118
119
  ankerl::unordered_dense::set<RTC::WebRtcTransport*> webRtcTransports;
119
120
  // Map of WebRtcTransports indexed by local ICE usernameFragment.
120
121
  ankerl::unordered_dense::map<std::string, RTC::WebRtcTransport*> mapLocalIceUsernameFragmentWebRtcTransport;
121
- // Map of WebRtcTransports indexed by TransportTuple.hash.
122
- ankerl::unordered_dense::map<uint64_t, RTC::WebRtcTransport*> mapTupleWebRtcTransport;
122
+ // Map of WebRtcTransports indexed by TransportTuple::TupleKey.
123
+ ankerl::unordered_dense::
124
+ map<RTC::TransportTuple::TupleKey, RTC::WebRtcTransport*, RTC::TransportTuple::TupleKeyHash>
125
+ mapTupleWebRtcTransport;
123
126
  // Whether the destructor has been called.
124
127
  bool closing{ false };
125
128
  };
@@ -264,6 +264,12 @@ namespace Utils
264
264
 
265
265
  static void WriteRandomBytes(uint8_t* buffer, size_t len);
266
266
 
267
+ static void HashCombine(size_t& seed, size_t value)
268
+ {
269
+ // The implementation is the same as boost::hash_combine().
270
+ seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
271
+ };
272
+
267
273
  private:
268
274
  static thread_local std::mt19937_64 rng;
269
275
  static thread_local EVP_MAC* mac;