@unboundcx/video-sdk-client 2.0.4 → 2.0.5

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.
@@ -366,9 +366,19 @@ export class LocalMediaManager extends EventEmitter {
366
366
  // Get display media
367
367
  // Note: Audio is only available when sharing a Chrome Tab or Window (not Screen)
368
368
  // Chrome will show an audio checkbox for Tab and Window sharing if audio is requested
369
+ //
370
+ // Video constraints: cap at 1920×1080 / 15fps so a huge external
371
+ // monitor or 4K display doesn't push absurd amounts of bandwidth
372
+ // or compute. 15fps is plenty for screen content (text + UI
373
+ // changes infrequently); the encoder profile in MediasoupManager
374
+ // uses degradationPreference="maintain-resolution" so the
375
+ // browser keeps the pixels sharp and drops frames if it has to.
369
376
  const constraints = {
370
377
  video: {
371
378
  cursor: 'always',
379
+ width: { max: 1920 },
380
+ height: { max: 1080 },
381
+ frameRate: { ideal: 15, max: 30 },
372
382
  },
373
383
  // Request audio with processing disabled for system audio
374
384
  // System audio should not have echo cancellation, noise suppression, or auto gain control
@@ -509,6 +509,60 @@ export class MediasoupManager extends EventEmitter {
509
509
  const baseWidth = settings.width || 1920;
510
510
  const baseHeight = settings.height || 1080;
511
511
 
512
+ const isScreenShare = options.appData?.type === "screenShare";
513
+
514
+ // ───────────────────────────────────────────────────────────
515
+ // Screen-share encoder profile
516
+ //
517
+ // Screen content is fundamentally different from camera:
518
+ // - Mostly static (text, UI, diagrams) with occasional motion
519
+ // - Text legibility is binary — readable or not, no in-between
520
+ // - Receivers either need to read it or they're zoomed out
521
+ //
522
+ // So: single high-quality layer (not 3-tier simulcast), much
523
+ // higher max bitrate per pixel, lower framerate target (the
524
+ // encoder handles this via degradationPreference + the actual
525
+ // capture frame rate, which Chrome lowers automatically for
526
+ // static content). Also set contentHint='detail' on the
527
+ // track itself so the encoder favours sharpness over motion.
528
+ // ───────────────────────────────────────────────────────────
529
+ if (isScreenShare) {
530
+ try {
531
+ track.contentHint = "detail";
532
+ } catch (err) {
533
+ this.logger.warn("Could not set contentHint on screenshare track:", err);
534
+ }
535
+ // Bitrate budget tuned for text crispness: ~5 Mbps at 1080p,
536
+ // ~3 Mbps at 720p, scaled linearly for unusual sizes. Cap
537
+ // upper at 8 Mbps so a huge external monitor doesn't blow
538
+ // through user bandwidth.
539
+ const pixels = baseWidth * baseHeight;
540
+ const bitrate = Math.min(
541
+ 8_000_000,
542
+ Math.max(2_000_000, Math.round(pixels * 2.5)),
543
+ );
544
+ produceOptions.encodings = [
545
+ {
546
+ rid: "h",
547
+ maxBitrate: bitrate,
548
+ scaleResolutionDownBy: 1.0,
549
+ },
550
+ ];
551
+ // degradationPreference="maintain-resolution" tells WebRTC
552
+ // to drop framerate before resolution if bandwidth tightens
553
+ // — exactly what we want for text.
554
+ produceOptions.degradationPreference = "maintain-resolution";
555
+ this.logger.info("VIDEO_QUALITY :: screen-share encoder profile", {
556
+ resolution: `${baseWidth}×${baseHeight}`,
557
+ bitrate: `${(bitrate / 1_000_000).toFixed(1)}Mbps`,
558
+ contentHint: track.contentHint,
559
+ degradationPreference: "maintain-resolution",
560
+ });
561
+ // Skip the camera simulcast branches below.
562
+ // (Falls through to the produceOptions logging.)
563
+ // No `return` — we still want to call transport.produce().
564
+ } else {
565
+
512
566
  // Check if user has set a max resolution preference
513
567
  const maxResolution = options.maxResolution || "1080p"; // Default to 1080p
514
568
 
@@ -584,6 +638,7 @@ export class MediasoupManager extends EventEmitter {
584
638
  bitrate: "800kbps",
585
639
  });
586
640
  }
641
+ } // end camera-profile else (matches `if (isScreenShare)`)
587
642
  }
588
643
 
589
644
  this.logger.info("Calling transport.produce with options:", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/video-sdk-client",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Framework-agnostic WebRTC video meeting SDK powered by mediasoup",
5
5
  "type": "module",
6
6
  "main": "index.js",