@blockcast/mmt-transport 0.2.0 → 0.2.2

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 (98) hide show
  1. package/README.md +264 -0
  2. package/dist/.build-stamp +0 -0
  3. package/dist/abr-controller.d.ts +3 -0
  4. package/dist/abr-controller.d.ts.map +1 -1
  5. package/dist/abr-controller.js +5 -0
  6. package/dist/abr-controller.js.map +1 -1
  7. package/dist/amt-gateway.d.ts.map +1 -1
  8. package/dist/amt-gateway.js +2 -1
  9. package/dist/amt-gateway.js.map +1 -1
  10. package/dist/endpoint-config.d.ts +20 -0
  11. package/dist/endpoint-config.d.ts.map +1 -0
  12. package/dist/endpoint-config.js +103 -0
  13. package/dist/endpoint-config.js.map +1 -0
  14. package/dist/endpoint-config.test.d.ts +2 -0
  15. package/dist/endpoint-config.test.d.ts.map +1 -0
  16. package/dist/endpoint-config.test.js +39 -0
  17. package/dist/endpoint-config.test.js.map +1 -0
  18. package/dist/fec-client.d.ts +2 -2
  19. package/dist/fec-client.d.ts.map +1 -1
  20. package/dist/fec-client.js +10 -5
  21. package/dist/fec-client.js.map +1 -1
  22. package/dist/index.d.ts +8 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/mmt-transport.no-modules.js +1154 -0
  27. package/dist/mmt-transport.no-modules.js.map +7 -0
  28. package/dist/multicast-orchestrator.d.ts +56 -0
  29. package/dist/multicast-orchestrator.d.ts.map +1 -0
  30. package/dist/multicast-orchestrator.js +123 -0
  31. package/dist/multicast-orchestrator.js.map +1 -0
  32. package/dist/multicast-subscription-pool.d.ts +18 -0
  33. package/dist/multicast-subscription-pool.d.ts.map +1 -0
  34. package/dist/multicast-subscription-pool.js +130 -0
  35. package/dist/multicast-subscription-pool.js.map +1 -0
  36. package/dist/multicast-subscription-pool.test.d.ts +2 -0
  37. package/dist/multicast-subscription-pool.test.d.ts.map +1 -0
  38. package/dist/multicast-subscription-pool.test.js +108 -0
  39. package/dist/multicast-subscription-pool.test.js.map +1 -0
  40. package/dist/multicast-tier.d.ts +14 -0
  41. package/dist/multicast-tier.d.ts.map +1 -0
  42. package/dist/multicast-tier.js +22 -0
  43. package/dist/multicast-tier.js.map +1 -0
  44. package/dist/multicast-tier.test.d.ts +2 -0
  45. package/dist/multicast-tier.test.d.ts.map +1 -0
  46. package/dist/multicast-tier.test.js +75 -0
  47. package/dist/multicast-tier.test.js.map +1 -0
  48. package/dist/no-modules.d.ts +29 -0
  49. package/dist/no-modules.d.ts.map +1 -0
  50. package/dist/no-modules.js +35 -0
  51. package/dist/no-modules.js.map +1 -0
  52. package/dist/stall-detector.d.ts +22 -0
  53. package/dist/stall-detector.d.ts.map +1 -0
  54. package/dist/stall-detector.js +46 -0
  55. package/dist/stall-detector.js.map +1 -0
  56. package/dist/stall-detector.test.d.ts +2 -0
  57. package/dist/stall-detector.test.d.ts.map +1 -0
  58. package/dist/stall-detector.test.js +33 -0
  59. package/dist/stall-detector.test.js.map +1 -0
  60. package/dist/takeover.d.ts +49 -0
  61. package/dist/takeover.d.ts.map +1 -0
  62. package/dist/takeover.js +43 -0
  63. package/dist/takeover.js.map +1 -0
  64. package/dist/transport-manager.d.ts +28 -18
  65. package/dist/transport-manager.d.ts.map +1 -1
  66. package/dist/transport-manager.js +192 -138
  67. package/dist/transport-manager.js.map +1 -1
  68. package/dist/transport-manager.test.d.ts +2 -0
  69. package/dist/transport-manager.test.d.ts.map +1 -0
  70. package/dist/transport-manager.test.js +155 -0
  71. package/dist/transport-manager.test.js.map +1 -0
  72. package/dist/types.d.ts +36 -36
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js.map +1 -1
  75. package/dist/unicast-fallback.d.ts +36 -0
  76. package/dist/unicast-fallback.d.ts.map +1 -0
  77. package/dist/unicast-fallback.js +53 -0
  78. package/dist/unicast-fallback.js.map +1 -0
  79. package/package.json +6 -6
  80. package/src/abr-controller.ts +9 -0
  81. package/src/amt-gateway.ts +2 -1
  82. package/src/endpoint-config.test.ts +54 -0
  83. package/src/endpoint-config.ts +121 -0
  84. package/src/fec-client.ts +11 -5
  85. package/src/index.ts +72 -2
  86. package/src/multicast-orchestrator.ts +150 -0
  87. package/src/multicast-subscription-pool.test.ts +115 -0
  88. package/src/multicast-subscription-pool.ts +164 -0
  89. package/src/multicast-tier.test.ts +80 -0
  90. package/src/multicast-tier.ts +32 -0
  91. package/src/no-modules.ts +44 -0
  92. package/src/stall-detector.test.ts +34 -0
  93. package/src/stall-detector.ts +63 -0
  94. package/src/takeover.ts +71 -0
  95. package/src/transport-manager.test.ts +192 -0
  96. package/src/transport-manager.ts +203 -153
  97. package/src/types.ts +50 -31
  98. package/src/unicast-fallback.ts +57 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/multicast-tier.ts", "../src/abr-controller.ts", "../src/endpoint-config.ts", "../src/multicast-orchestrator.ts", "../src/multicast-subscription-pool.ts", "../src/stall-detector.ts", "../src/takeover.ts", "../src/driad-discovery.ts", "../src/transport-manager.ts", "../src/unicast-fallback.ts", "../src/no-modules.ts"],
4
+ "sourcesContent": ["export interface MulticastTierEndpoint {\n\tsourceAddress?: string;\n\tgroupAddress: string;\n\tport: number;\n\ttracks: { name: string; packetId?: number }[];\n\tbandwidth?: number;\n\tprotocol?: string;\n}\n\nexport function endpointKey(endpoint: MulticastTierEndpoint): string {\n\treturn `${endpoint.sourceAddress ?? \"\"}|${endpoint.groupAddress}|${endpoint.port}`;\n}\n\nexport function selectMulticastTier(\n\tendpoints: readonly MulticastTierEndpoint[],\n\testimate: number | undefined,\n): MulticastTierEndpoint | undefined {\n\tif (endpoints.length <= 1) return endpoints[0];\n\n\tconst ranked = endpoints\n\t\t.filter((endpoint) => typeof endpoint.bandwidth === \"number\" && endpoint.bandwidth > 0)\n\t\t.sort((a, b) => a.bandwidth! - b.bandwidth!);\n\tif (ranked.length < 2) return endpoints[0];\n\tif (estimate === undefined) return ranked[0];\n\n\tconst budget = estimate * 0.8;\n\tlet selected: MulticastTierEndpoint | undefined;\n\tfor (const endpoint of ranked) {\n\t\tif (endpoint.bandwidth! <= budget) selected = endpoint;\n\t}\n\treturn selected ?? ranked[0];\n}\n", "/**\n * @blockcast/transport - Adaptive Bitrate Controller\n *\n * Progressive adaptive bitrate increase based on network conditions.\n * Monitors received bandwidth and packet loss to adjust quality levels.\n *\n * Per draft-ramadan-moq-fec-00, ABR decisions should use the **effective loss rate**\n * after FEC recovery, not the raw transport-level loss rate. This allows FEC to\n * mask burst losses without unnecessarily downgrading quality.\n */\n\nimport type { QualityLevel } from \"./types.js\";\nimport { selectMulticastTier, type MulticastTierEndpoint } from \"./multicast-tier.js\";\n\n/**\n * FEC statistics for ABR decisions\n */\nexport interface FecStatsForAbr {\n\t/** Raw loss rate before FEC recovery (0.0-1.0) */\n\trawLossRate: number;\n\t/** Effective loss rate after FEC recovery (0.0-1.0) */\n\teffectiveLossRate: number;\n\t/** FEC recovery rate (percentage of lost packets recovered) */\n\trecoveryRate: number;\n\t/** Whether FEC is enabled */\n\tfecEnabled: boolean;\n}\n\n/**\n * Standard quality levels for progressive ABR\n */\nexport const QUALITY_LEVELS: QualityLevel[] = [\n\t{ name: \"240p\", bitrate: 300_000, width: 426, height: 240, fps: 15 },\n\t{ name: \"360p\", bitrate: 500_000, width: 640, height: 360, fps: 24 },\n\t{ name: \"480p\", bitrate: 1_000_000, width: 854, height: 480, fps: 30 },\n\t{ name: \"720p\", bitrate: 2_500_000, width: 1280, height: 720, fps: 30 },\n\t{ name: \"1080p\", bitrate: 5_000_000, width: 1920, height: 1080, fps: 30 },\n\t{ name: \"1440p\", bitrate: 8_000_000, width: 2560, height: 1440, fps: 30 },\n\t{ name: \"4K\", bitrate: 15_000_000, width: 3840, height: 2160, fps: 30 },\n];\n\n/**\n * Progressive Adaptive Bitrate Controller\n *\n * Features:\n * - Monitors bandwidth via received bytes over time window\n * - Tracks packet loss rate (raw and effective after FEC)\n * - Uses effective loss rate for ABR decisions per draft-ramadan-moq-fec-00\n * - Progressive quality upgrades (waits for stability)\n * - Fast quality downgrades on congestion\n */\nexport class AdaptiveBitrateController {\n\tprivate currentQualityIndex: number;\n\tprivate history: { timestamp: number; bytesReceived: number; packetsLost: number }[] = [];\n\tprivate readonly historyWindow = 10_000; // 10 second window\n\tprivate readonly upgradeThreshold = 0.8; // 80% of target bitrate sustained\n\tprivate readonly downgradeThreshold = 0.5; // 50% of target bitrate triggers downgrade\n\tprivate readonly stabilityPeriod = 5_000; // Wait 5s before upgrading\n\n\tprivate lastQualityChange = 0;\n\tprivate onQualityChange?: (quality: QualityLevel) => void;\n\n\t// FEC stats for effective loss rate calculation\n\tprivate fecStats: FecStatsForAbr = {\n\t\trawLossRate: 0,\n\t\teffectiveLossRate: 0,\n\t\trecoveryRate: 1,\n\t\tfecEnabled: false,\n\t};\n\n\tconstructor(initialQuality?: QualityLevel) {\n\t\tthis.currentQualityIndex = initialQuality ? QUALITY_LEVELS.findIndex((q) => q.name === initialQuality.name) : 0;\n\n\t\tif (this.currentQualityIndex < 0) this.currentQualityIndex = 0;\n\t}\n\n\tsetCallback(callback: (quality: QualityLevel) => void) {\n\t\tthis.onQualityChange = callback;\n\t}\n\n\tgetCurrentQuality(): QualityLevel {\n\t\treturn QUALITY_LEVELS[this.currentQualityIndex];\n\t}\n\n\t/** Selects the multicast (S,G) tier without replacing a player's unicast ABR manager. */\n\tselectMulticastTier(\n\t\tendpoints: readonly MulticastTierEndpoint[],\n\t\testimate: number | undefined = this.getMetrics().measuredBitrate || undefined,\n\t): MulticastTierEndpoint | undefined {\n\t\treturn selectMulticastTier(endpoints, estimate);\n\t}\n\n\t/**\n\t * Record network sample for bitrate adaptation\n\t *\n\t * @param bytesReceived - Bytes received in this sample\n\t * @param packetsLost - Packets lost (raw, before FEC recovery)\n\t */\n\trecordSample(bytesReceived: number, packetsLost: number) {\n\t\tconst now = Date.now();\n\n\t\tthis.history.push({ timestamp: now, bytesReceived, packetsLost });\n\n\t\t// Prune old samples\n\t\tthis.history = this.history.filter((s) => now - s.timestamp < this.historyWindow);\n\n\t\t// Calculate metrics\n\t\tconst { measuredBitrate, lossRate: rawLossRate } = this.calculateMetrics();\n\t\tconst currentQuality = this.getCurrentQuality();\n\n\t\t// Per draft-ramadan-moq-fec-00: Use effective loss rate after FEC for ABR decisions\n\t\t// This allows FEC to mask burst losses without triggering unnecessary downgrades\n\t\tconst effectiveLossRate = this.fecStats.fecEnabled\n\t\t\t? this.fecStats.effectiveLossRate\n\t\t\t: rawLossRate;\n\n\t\t// Decision logic\n\t\tif (now - this.lastQualityChange < this.stabilityPeriod) {\n\t\t\treturn; // Wait for stability\n\t\t}\n\n\t\t// Check for downgrade - use effective loss rate (post-FEC)\n\t\t// Only downgrade if FEC cannot recover the losses\n\t\tif (effectiveLossRate > 0.05 || measuredBitrate < currentQuality.bitrate * this.downgradeThreshold) {\n\t\t\tthis.downgrade();\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for upgrade (progressive increase) - use effective loss rate\n\t\tif (\n\t\t\tmeasuredBitrate > currentQuality.bitrate * this.upgradeThreshold &&\n\t\t\teffectiveLossRate < 0.01 &&\n\t\t\tthis.currentQualityIndex < QUALITY_LEVELS.length - 1\n\t\t) {\n\t\t\tthis.upgrade();\n\t\t}\n\t}\n\n\t/**\n\t * Update FEC statistics for ABR decisions\n\t *\n\t * Per draft-ramadan-moq-fec-00, ABR should use the effective loss rate\n\t * (after FEC recovery) rather than raw transport loss rate.\n\t *\n\t * @param stats - FEC statistics from the FEC decoder\n\t */\n\tsetFecStats(stats: FecStatsForAbr) {\n\t\tthis.fecStats = stats;\n\t}\n\n\t/**\n\t * Get current FEC statistics\n\t */\n\tgetFecStats(): FecStatsForAbr {\n\t\treturn { ...this.fecStats };\n\t}\n\n\tprivate calculateMetrics() {\n\t\tif (this.history.length < 2) {\n\t\t\treturn { measuredBitrate: 0, lossRate: 0 };\n\t\t}\n\n\t\tconst totalBytes = this.history.reduce((sum, s) => sum + s.bytesReceived, 0);\n\t\tconst totalLost = this.history.reduce((sum, s) => sum + s.packetsLost, 0);\n\t\tconst duration = (this.history[this.history.length - 1].timestamp - this.history[0].timestamp) / 1000;\n\n\t\tconst measuredBitrate = duration > 0 ? (totalBytes * 8) / duration : 0;\n\t\tconst lossRate = totalLost / (this.history.length + totalLost);\n\n\t\treturn { measuredBitrate, lossRate };\n\t}\n\n\tprivate upgrade() {\n\t\tif (this.currentQualityIndex >= QUALITY_LEVELS.length - 1) return;\n\n\t\tthis.currentQualityIndex++;\n\t\tthis.lastQualityChange = Date.now();\n\n\t\tconst newQuality = this.getCurrentQuality();\n\t\tconsole.log(`[ABR] Upgrading to ${newQuality.name} (${newQuality.bitrate / 1_000_000}Mbps)`);\n\n\t\tthis.onQualityChange?.(newQuality);\n\t}\n\n\tprivate downgrade() {\n\t\tif (this.currentQualityIndex <= 0) return;\n\n\t\tthis.currentQualityIndex--;\n\t\tthis.lastQualityChange = Date.now();\n\n\t\tconst newQuality = this.getCurrentQuality();\n\t\tconsole.log(`[ABR] Downgrading to ${newQuality.name} (${newQuality.bitrate / 1_000_000}Mbps)`);\n\n\t\tthis.onQualityChange?.(newQuality);\n\t}\n\n\t/**\n\t * Force quality level (for testing)\n\t */\n\tsetQuality(name: string) {\n\t\tconst index = QUALITY_LEVELS.findIndex((q) => q.name === name);\n\t\tif (index >= 0) {\n\t\t\tthis.currentQualityIndex = index;\n\t\t\tthis.lastQualityChange = Date.now();\n\t\t\tthis.onQualityChange?.(this.getCurrentQuality());\n\t\t}\n\t}\n\n\t/**\n\t * Get current metrics including both raw and effective loss rates\n\t */\n\tgetMetrics(): {\n\t\tmeasuredBitrate: number;\n\t\trawLossRate: number;\n\t\teffectiveLossRate: number;\n\t\tfecEnabled: boolean;\n\t\tsampleCount: number;\n\t} {\n\t\tconst { measuredBitrate, lossRate: rawLossRate } = this.calculateMetrics();\n\t\treturn {\n\t\t\tmeasuredBitrate,\n\t\t\trawLossRate,\n\t\t\teffectiveLossRate: this.fecStats.fecEnabled ? this.fecStats.effectiveLossRate : rawLossRate,\n\t\t\tfecEnabled: this.fecStats.fecEnabled,\n\t\t\tsampleCount: this.history.length,\n\t\t};\n\t}\n\n\t/**\n\t * Reset history and start fresh\n\t */\n\treset() {\n\t\tthis.history = [];\n\t\tthis.lastQualityChange = 0;\n\t}\n}\n", "export type MulticastTransportPreference = \"auto\" | \"native\" | \"tunnel\";\n\nexport interface MulticastEndpointConfig {\n\tgroupAddress: string;\n\tsourceAddress?: string;\n\tport: number;\n\tmode?: MulticastTransportPreference;\n\tamtRelay?: string;\n\tamtRelayPort?: number;\n}\n\nexport function isValidMulticastGroupAddress(address: string | undefined): boolean {\n\tconst value = address?.trim().toLowerCase();\n\tif (!value) return false;\n\tif (value.startsWith(\"ff\")) return isValidIpv6MulticastAddress(value);\n\n\tconst parts = value.split(\".\");\n\tif (parts.length !== 4) return false;\n\tconst octets = parts.map((part) => {\n\t\tif (!/^\\d+$/.test(part)) return Number.NaN;\n\t\tconst octet = Number(part);\n\t\treturn Number.isInteger(octet) && octet >= 0 && octet <= 255 ? octet : Number.NaN;\n\t});\n\treturn octets.every(Number.isInteger) && octets[0] >= 224 && octets[0] <= 239;\n}\n\nexport function normalizeMulticastEndpoint(\n\tendpoint: MulticastEndpointConfig,\n): MulticastEndpointConfig | undefined {\n\tconst groupAddress = endpoint.groupAddress.trim().toLowerCase();\n\tconst sourceAddress = endpoint.sourceAddress?.trim().toLowerCase() || undefined;\n\tconst amtRelay = endpoint.amtRelay?.trim().toLowerCase() || undefined;\n\tif (!isValidMulticastGroupAddress(groupAddress) || !isPositivePort(endpoint.port)) return undefined;\n\tif (endpoint.mode && ![\"auto\", \"native\", \"tunnel\"].includes(endpoint.mode)) return undefined;\n\tif (endpoint.amtRelayPort !== undefined && !isPositivePort(endpoint.amtRelayPort)) return undefined;\n\n\treturn {\n\t\tgroupAddress,\n\t\t...(sourceAddress ? { sourceAddress } : {}),\n\t\tport: endpoint.port,\n\t\t...(endpoint.mode ? { mode: endpoint.mode } : {}),\n\t\t...(amtRelay ? { amtRelay } : {}),\n\t\t...(endpoint.amtRelayPort ? { amtRelayPort: endpoint.amtRelayPort } : {}),\n\t};\n}\n\n/** Stable ownership key for one IWA multicast/AMT subscription. */\nexport function multicastEndpointKey(endpoint: MulticastEndpointConfig): string {\n\tconst normalized = normalizeMulticastEndpoint(endpoint);\n\tif (!normalized) throw new Error(\"Invalid multicast endpoint\");\n\treturn JSON.stringify([\n\t\tnormalized.sourceAddress ?? \"\",\n\t\tnormalized.groupAddress,\n\t\tnormalized.port,\n\t\tnormalized.mode ?? \"auto\",\n\t\tnormalized.amtRelay ?? \"\",\n\t\tnormalized.amtRelayPort ?? \"\",\n\t]);\n}\n\nexport function isUsableMulticastEndpoint(\n\tendpoint: { groupAddress?: string; port?: unknown } | undefined,\n): boolean {\n\tconst port = Number(endpoint?.port);\n\treturn isValidMulticastGroupAddress(endpoint?.groupAddress) && isPositivePort(port);\n}\n\nexport function readUrlMulticastConfig(\n\tsearch: string | URLSearchParams = typeof location === \"undefined\" ? \"\" : location.search,\n): MulticastEndpointConfig | undefined {\n\tconst params = typeof search === \"string\" ? new URLSearchParams(search) : search;\n\tconst groupAddress = params.get(\"mc_group\") || \"\";\n\tconst port = positivePort(params.get(\"mc_port\"));\n\tif (!groupAddress || !port) return undefined;\n\n\tconst amtRelay = params.get(\"mc_amt_relay\") || params.get(\"amt_relay\") || undefined;\n\tconst amtRelayPort = positivePort(params.get(\"mc_amt_relay_port\") || params.get(\"amt_relay_port\"));\n\tconst requestedMode = params.get(\"mc_mode\");\n\tconst mode = amtRelay ? \"tunnel\" : multicastMode(requestedMode);\n\n\treturn normalizeMulticastEndpoint({\n\t\tgroupAddress,\n\t\tsourceAddress: params.get(\"mc_source\") || undefined,\n\t\tport,\n\t\t...(mode ? { mode } : {}),\n\t\t...(amtRelay ? { amtRelay } : {}),\n\t\t...(amtRelayPort ? { amtRelayPort } : {}),\n\t});\n}\n\nexport function isMulticastExplicitlyDisabled(\n\tsearch: string | URLSearchParams = typeof location === \"undefined\" ? \"\" : location.search,\n): boolean {\n\tconst params = typeof search === \"string\" ? new URLSearchParams(search) : search;\n\tconst multicast = params.get(\"multicast\")?.trim().toLowerCase();\n\tconst transport = params.get(\"transport\")?.trim().toLowerCase();\n\treturn transport === \"moq-only\" || [\"0\", \"false\", \"no\", \"off\"].includes(multicast || \"\");\n}\n\nfunction isValidIpv6MulticastAddress(value: string): boolean {\n\tif (!/^[0-9a-f:]+$/.test(value)) return false;\n\tconst compressed = value.includes(\"::\");\n\tif (value.indexOf(\"::\") !== value.lastIndexOf(\"::\")) return false;\n\tconst groups = value.split(\":\").filter(Boolean);\n\tif (groups.length === 0 || groups.length > 8) return false;\n\tif (!compressed && groups.length !== 8) return false;\n\treturn groups.every((group) => /^[0-9a-f]{1,4}$/.test(group));\n}\n\nfunction multicastMode(value: string | null): MulticastTransportPreference | undefined {\n\treturn value === \"native\" || value === \"tunnel\" || value === \"auto\" ? value : undefined;\n}\n\nfunction positivePort(value: string | null): number | undefined {\n\tconst port = Number(value);\n\treturn isPositivePort(port) ? port : undefined;\n}\n\nfunction isPositivePort(port: number): boolean {\n\treturn Number.isInteger(port) && port > 0 && port <= 65535;\n}\n", "import { endpointKey, selectMulticastTier, type MulticastTierEndpoint } from \"./multicast-tier.js\";\n\nexport type OrchestratorState = \"stable\" | \"joining\";\nexport type OrchestratorCommand =\n\t| { kind: \"join-target\"; current: MulticastTierEndpoint; target: MulticastTierEndpoint }\n\t| { kind: \"commit-swap\"; target: MulticastTierEndpoint; old: MulticastTierEndpoint }\n\t| { kind: \"abandon-join\"; current: MulticastTierEndpoint; target: MulticastTierEndpoint };\n\nexport interface MulticastTierPacket {\n\tendpointKey: string;\n\tkeyframe: boolean;\n\ttimestamp: number;\n\thasValidCodecConfig: boolean;\n}\n\nexport interface OrchestratorOptions {\n\tendpoints: readonly MulticastTierEndpoint[];\n\tinitialEndpoint?: MulticastTierEndpoint;\n\tadaptive?: boolean;\n\tnow: () => number;\n\tdwellMs?: number;\n\tjoinDeadlineMs?: number;\n}\n\nexport interface GoodputSample { bitrate: number | undefined; }\n\nexport const DEFAULT_DWELL_MS = 3_000;\nexport const DEFAULT_JOIN_DEADLINE_MS = 5_000;\n\nexport function goodputFromStats(\n\tprevious: { bytesReceived: number; atMs: number } | undefined,\n\tcurrent: { bytesReceived: number; atMs: number },\n\tminSampleMs = 500,\n): number | undefined {\n\tif (!previous) return undefined;\n\tconst durationMs = current.atMs - previous.atMs;\n\tconst bytes = current.bytesReceived - previous.bytesReceived;\n\tif (durationMs < minSampleMs || bytes <= 0) return undefined;\n\treturn (bytes * 8 * 1000) / durationMs;\n}\n\nexport class MulticastOrchestrator {\n\t#state: OrchestratorState = \"stable\";\n\t#current: MulticastTierEndpoint | undefined;\n\t#target: MulticastTierEndpoint | undefined;\n\t#estimate: number | undefined;\n\t#favored: { key: string; since: number } | undefined;\n\t#joinStartedAt = 0;\n\t#lastCommittedTimestamp = Number.NEGATIVE_INFINITY;\n\treadonly #dwellMs: number;\n\treadonly #joinDeadlineMs: number;\n\n\tconstructor(private readonly options: OrchestratorOptions) {\n\t\tthis.#dwellMs = options.dwellMs ?? DEFAULT_DWELL_MS;\n\t\tthis.#joinDeadlineMs = options.joinDeadlineMs ?? DEFAULT_JOIN_DEADLINE_MS;\n\t\tif (!Number.isFinite(this.#dwellMs) || !Number.isFinite(this.#joinDeadlineMs) ||\n\t\t\tthis.#dwellMs <= 0 || this.#joinDeadlineMs <= 0) {\n\t\t\tthrow new Error(\"Multicast dwell and join deadline must be positive\");\n\t\t}\n\t}\n\n\tget state(): OrchestratorState { return this.#state; }\n\tget current(): MulticastTierEndpoint | undefined { return this.#current; }\n\tget target(): MulticastTierEndpoint | undefined { return this.#target; }\n\tget estimate(): number | undefined { return this.#estimate; }\n\n\tstart(): MulticastTierEndpoint | undefined {\n\t\tthis.#current ??= this.options.initialEndpoint ?? selectMulticastTier(this.options.endpoints, undefined);\n\t\treturn this.#current;\n\t}\n\n\tonGoodput(sample: GoodputSample): OrchestratorCommand | undefined {\n\t\tconst bitrate = sample.bitrate;\n\t\tthis.#estimate = bitrate;\n\t\tif (!this.#current || this.options.adaptive === false) return undefined;\n\t\tif (this.#state === \"joining\") return this.poll();\n\t\tconst desired = selectMulticastTier(this.options.endpoints, bitrate);\n\t\tif (!desired) return undefined;\n\t\tconst desiredKey = endpointKey(desired);\n\t\tif (desiredKey === endpointKey(this.#current)) {\n\t\t\tthis.#favored = undefined;\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst now = this.#now();\n\t\tif (!this.#favored || this.#favored.key !== desiredKey) {\n\t\t\tthis.#favored = { key: desiredKey, since: now };\n\t\t\treturn undefined;\n\t\t}\n\t\tif (now - this.#favored.since < this.#dwellMs) return undefined;\n\n\t\tthis.#state = \"joining\";\n\t\tthis.#target = desired;\n\t\tthis.#joinStartedAt = now;\n\t\tthis.#favored = undefined;\n\t\treturn { kind: \"join-target\", current: this.#current, target: desired };\n\t}\n\n\tonTierPacket(packet: MulticastTierPacket): OrchestratorCommand | undefined {\n\t\tif (!this.#current) return undefined;\n\t\tif (packet.endpointKey === endpointKey(this.#current) && Number.isFinite(packet.timestamp)) {\n\t\t\tthis.#lastCommittedTimestamp = Math.max(this.#lastCommittedTimestamp, packet.timestamp);\n\t\t}\n\t\tif (this.#state !== \"joining\" || !this.#target) return undefined;\n\t\tconst expired = this.poll();\n\t\tif (expired) return expired;\n\t\tif (\n\t\t\tpacket.endpointKey !== endpointKey(this.#target) ||\n\t\t\t!packet.keyframe ||\n\t\t\t!packet.hasValidCodecConfig ||\n\t\t\t!Number.isFinite(packet.timestamp) ||\n\t\t\tpacket.timestamp < this.#lastCommittedTimestamp\n\t\t) return undefined;\n\n\t\tconst command: OrchestratorCommand = {\n\t\t\tkind: \"commit-swap\",\n\t\t\ttarget: this.#target,\n\t\t\told: this.#current,\n\t\t};\n\t\tthis.#lastCommittedTimestamp = packet.timestamp;\n\t\tthis.#current = this.#target;\n\t\tthis.#target = undefined;\n\t\tthis.#state = \"stable\";\n\t\treturn command;\n\t}\n\n\tpoll(): OrchestratorCommand | undefined {\n\t\tif (this.#state !== \"joining\" || !this.#target || !this.#current) return undefined;\n\t\tif (this.#now() - this.#joinStartedAt < this.#joinDeadlineMs) return undefined;\n\t\treturn this.cancel();\n\t}\n\n\tcancel(): OrchestratorCommand | undefined {\n\t\tif (this.#state !== \"joining\" || !this.#target || !this.#current) return undefined;\n\t\tconst command: OrchestratorCommand = {\n\t\t\tkind: \"abandon-join\",\n\t\t\tcurrent: this.#current,\n\t\t\ttarget: this.#target,\n\t\t};\n\t\tthis.#target = undefined;\n\t\tthis.#state = \"stable\";\n\t\treturn command;\n\t}\n\n\t#now(): number {\n\t\tconst now = this.options.now();\n\t\tif (!Number.isFinite(now)) throw new Error(\"Multicast orchestrator clock must be finite\");\n\t\treturn now;\n\t}\n}\n", "import { multicastEndpointKey, normalizeMulticastEndpoint } from \"./endpoint-config.js\";\nimport type {\n\tTransportConfig,\n\tWindowMulticastGateway,\n\tWindowMulticastSubscription,\n\tWindowMulticastSubscriptionState,\n} from \"./types.js\";\n\nexport interface MulticastSubscriptionConsumer {\n\tonBytes(data: ArrayBuffer): void;\n\tonState?(state: WindowMulticastSubscriptionState): void;\n\tonError?(error: Error): void;\n}\n\ninterface PoolEntry {\n\tsubscription: WindowMulticastSubscription;\n\tconsumers: Map<symbol, MulticastSubscriptionConsumer>;\n\tbytesListener: (event: CustomEvent<ArrayBuffer>) => void;\n\tstateListener: (event: CustomEvent<WindowMulticastSubscriptionState>) => void;\n\terrorListener: (event: CustomEvent<unknown>) => void;\n\tclosed: boolean;\n}\n\ninterface PoolRecord { claims: number; entry: Promise<PoolEntry>; }\n\nexport interface MulticastSubscriptionLease {\n\treadonly key: string;\n\trelease(): Promise<void>;\n}\n\n/** Shares only IWA multicast/AMT subscriptions. Unicast sessions remain per manager. */\nexport class MulticastSubscriptionPool {\n\treadonly #entries = new Map<string, PoolRecord>();\n\treadonly #gatewayIds = new WeakMap<object, number>();\n\t#nextGatewayId = 1;\n\n\tasync acquire(\n\t\tgateway: WindowMulticastGateway,\n\t\tconfig: TransportConfig,\n\t\tconsumer: MulticastSubscriptionConsumer,\n\t): Promise<MulticastSubscriptionLease> {\n\t\tconst endpoint = normalizeMulticastEndpoint({\n\t\t\tgroupAddress: config.group,\n\t\t\tsourceAddress: config.source,\n\t\t\tport: config.port,\n\t\t\tmode: config.mode,\n\t\t\tamtRelay: config.amtRelay,\n\t\t\tamtRelayPort: config.amtRelayPort,\n\t\t});\n\t\tif (!endpoint) throw new Error(\"Invalid multicast endpoint\");\n\t\tif (typeof gateway.subscribe !== \"function\") throw new Error(\"window.multicast.subscribe unavailable\");\n\n\t\tconst key = multicastEndpointKey(endpoint);\n\t\tconst internalKey = `${this.#gatewayId(gateway)}|${key}`;\n\t\tlet record = this.#entries.get(internalKey);\n\t\tif (!record) {\n\t\t\trecord = { claims: 0, entry: Promise.resolve(null as unknown as PoolEntry) };\n\t\t\tconst createdRecord = record;\n\t\t\trecord.entry = this.#createEntry(gateway, endpoint, (entry) => {\n\t\t\t\tif (this.#entries.get(internalKey) === createdRecord) this.#entries.delete(internalKey);\n\t\t\t\tthis.#detach(entry);\n\t\t\t}).catch((error) => {\n\t\t\t\tif (this.#entries.get(internalKey) === createdRecord) this.#entries.delete(internalKey);\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tthis.#entries.set(internalKey, record);\n\t\t}\n\n\t\trecord.claims++;\n\t\tlet entry: PoolEntry;\n\t\ttry {\n\t\t\tentry = await record.entry;\n\t\t} catch (error) {\n\t\t\trecord.claims--;\n\t\t\tthrow error;\n\t\t}\n\t\tif (entry.closed) {\n\t\t\trecord.claims--;\n\t\t\treturn this.acquire(gateway, config, consumer);\n\t\t}\n\t\tconst token = Symbol(key);\n\t\tentry.consumers.set(token, consumer);\n\t\tlet released = false;\n\t\treturn {\n\t\t\tkey,\n\t\t\trelease: async () => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tentry.consumers.delete(token);\n\t\t\t\trecord.claims--;\n\t\t\t\tif (record.claims > 0) return;\n\t\t\t\tif (this.#entries.get(internalKey) === record) this.#entries.delete(internalKey);\n\t\t\t\tthis.#detach(entry);\n\t\t\t\tif (!entry.closed) await entry.subscription.unsubscribe();\n\t\t\t},\n\t\t};\n\t}\n\n\tget size(): number { return this.#entries.size; }\n\n\tasync #createEntry(\n\t\tgateway: WindowMulticastGateway,\n\t\tendpoint: NonNullable<ReturnType<typeof normalizeMulticastEndpoint>>,\n\t\tonClosed: (entry: PoolEntry) => void,\n\t): Promise<PoolEntry> {\n\t\tconst subscription = await gateway.subscribe!({\n\t\t\tgroup: endpoint.groupAddress,\n\t\t\t...(endpoint.sourceAddress ? { source: endpoint.sourceAddress } : {}),\n\t\t\tport: endpoint.port,\n\t\t\tconsume: \"bytes\",\n\t\t\t...(endpoint.mode ? { mode: endpoint.mode } : {}),\n\t\t\t...(endpoint.amtRelay ? { amtRelay: endpoint.amtRelay } : {}),\n\t\t\t...(endpoint.amtRelayPort ? { amtRelayPort: endpoint.amtRelayPort } : {}),\n\t\t});\n\t\tconst consumers = new Map<symbol, MulticastSubscriptionConsumer>();\n\t\tlet entry: PoolEntry;\n\t\tconst bytesListener = (event: CustomEvent<ArrayBuffer>) => {\n\t\t\tthis.#dispatch(consumers, (consumer) => consumer.onBytes(event.detail));\n\t\t};\n\t\tconst stateListener = (event: CustomEvent<WindowMulticastSubscriptionState>) => {\n\t\t\tthis.#dispatch(consumers, (consumer) => consumer.onState?.(event.detail));\n\t\t\tif (event.detail === \"closed\" && !entry.closed) {\n\t\t\t\tentry.closed = true;\n\t\t\t\tonClosed(entry);\n\t\t\t}\n\t\t};\n\t\tconst errorListener = (event: CustomEvent<unknown>) => {\n\t\t\tconst error = event.detail instanceof Error ? event.detail : new Error(String(event.detail));\n\t\t\tthis.#dispatch(consumers, (consumer) => consumer.onError?.(error));\n\t\t};\n\t\tentry = { subscription, consumers, bytesListener, stateListener, errorListener, closed: false };\n\t\tsubscription.addEventListener(\"bytes\", bytesListener);\n\t\tsubscription.addEventListener(\"state\", stateListener);\n\t\tsubscription.addEventListener(\"error\", errorListener);\n\t\treturn entry;\n\t}\n\n\t#gatewayId(gateway: WindowMulticastGateway): number {\n\t\tconst object = gateway as object;\n\t\tlet id = this.#gatewayIds.get(object);\n\t\tif (!id) {\n\t\t\tid = this.#nextGatewayId++;\n\t\t\tthis.#gatewayIds.set(object, id);\n\t\t}\n\t\treturn id;\n\t}\n\n\t#detach(entry: PoolEntry): void {\n\t\tentry.subscription.removeEventListener(\"bytes\", entry.bytesListener);\n\t\tentry.subscription.removeEventListener(\"state\", entry.stateListener);\n\t\tentry.subscription.removeEventListener(\"error\", entry.errorListener);\n\t}\n\n\t#dispatch(\n\t\tconsumers: Map<symbol, MulticastSubscriptionConsumer>,\n\t\tcallback: (consumer: MulticastSubscriptionConsumer) => void,\n\t): void {\n\t\tfor (const consumer of consumers.values()) {\n\t\t\ttry { callback(consumer); } catch (error) { console.error(\"[Transport] multicast consumer callback failed\", error); }\n\t\t}\n\t}\n}\n\nexport const multicastSubscriptionPool = new MulticastSubscriptionPool();\n", "export interface FecProgressSnapshot {\n\tsourceSymbols: number;\n\trepairSymbols: number;\n\tblocksComplete: number;\n\tblocksRecovered: number;\n\tmediaFrames?: number;\n}\n\nexport interface StallDetectorConfig {\n\twindowMs: number;\n\tonStarvation(): void;\n\tonRecovery(): void;\n\tnow?: () => number;\n}\n\nexport class TransportStallDetector {\n\t#last: FecProgressSnapshot | null = null;\n\t#lastSampleAt = 0;\n\t#starvedSince: number | null = null;\n\t#latched = false;\n\treadonly #now: () => number;\n\n\tconstructor(private readonly config: StallDetectorConfig) {\n\t\tif (config.windowMs <= 0) throw new Error(\"Stall window must be positive and catalog-derived\");\n\t\tthis.#now = config.now ?? Date.now;\n\t}\n\n\tget isStarved(): boolean { return this.#latched; }\n\n\tupdate(snapshot: FecProgressSnapshot): void {\n\t\tconst previous = this.#last;\n\t\tconst previousAt = this.#lastSampleAt;\n\t\tconst now = this.#now();\n\t\tthis.#last = { ...snapshot };\n\t\tthis.#lastSampleAt = now;\n\t\tif (!previous) return;\n\n\t\tconst blockDelta = snapshot.blocksComplete - previous.blocksComplete +\n\t\t\tsnapshot.blocksRecovered - previous.blocksRecovered;\n\t\tconst mediaDelta = (snapshot.mediaFrames ?? 0) - (previous.mediaFrames ?? 0);\n\n\t\tif (blockDelta <= 0 && mediaDelta <= 0) {\n\t\t\tthis.#starvedSince ??= previousAt;\n\t\t\tif (!this.#latched && now - this.#starvedSince >= this.config.windowMs) {\n\t\t\t\tthis.#latched = true;\n\t\t\t\tthis.config.onStarvation();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#starvedSince = null;\n\t\tif (this.#latched) {\n\t\t\tthis.#latched = false;\n\t\t\tthis.config.onRecovery();\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.#last = null;\n\t\tthis.#starvedSince = null;\n\t\tthis.#latched = false;\n\t}\n}\n", "export interface ActivatableSessions { activateMulticast(): void; }\n\nexport function makeSourceSwitchHandler(options: {\n\tmulticastBridgeAvailable: boolean;\n\tsessionsReady: Promise<ActivatableSessions | null>;\n\tonMulticastConfirmed?(): void;\n\tonAbortTakeover?(): void;\n\tlog?(message: string): void;\n\twarn?(message: string): void;\n}): (source: string) => Promise<void> {\n\tconst log = options.log ?? console.log;\n\tconst warn = options.warn ?? console.warn;\n\treturn async (source) => {\n\t\tif (source !== \"multicast\") return;\n\t\tif (!options.multicastBridgeAvailable) {\n\t\t\twarn(\"[MoqWatch] MediaRouter requested multicast without bridge; keeping MoQ active\");\n\t\t\treturn;\n\t\t}\n\t\tconst sessions = await options.sessionsReady;\n\t\tif (!sessions) {\n\t\t\twarn(\"[MoqWatch] SessionManager unavailable; aborting multicast takeover, staying on MoQ\");\n\t\t\toptions.onAbortTakeover?.();\n\t\t\treturn;\n\t\t}\n\t\tlog(\"[MoqWatch] MediaRouter -> multicast; activating via SessionManager\");\n\t\tsessions.activateMulticast();\n\t\toptions.onMulticastConfirmed?.();\n\t};\n}\n\nexport interface LossArcSession { lossDetected(): void; lossRecovered(): void; }\nexport interface FecStarvationSnapshot {\n\tsourceSymbols: number;\n\trepairSymbols: number;\n\tblocksComplete: number;\n\tblocksRecovered: number;\n\tmediaFrames?: number;\n}\nexport interface StarvationDetectorLike { update(snapshot: FecStarvationSnapshot): void; reset(): void; }\nexport type StallDetectorLike = StarvationDetectorLike;\n\nexport function wireStallDetector(args: {\n\tsession: LossArcSession | undefined;\n\twindowMs: number;\n\tmakeDetector(config: { windowMs: number; onStarvation(): void; onRecovery(): void }): StallDetectorLike;\n}): StallDetectorLike | null {\n\tif (!args.session || args.windowMs <= 0) return null;\n\treturn args.makeDetector({\n\t\twindowMs: args.windowMs,\n\t\tonStarvation: () => args.session!.lossDetected(),\n\t\tonRecovery: () => args.session!.lossRecovered(),\n\t});\n}\n\nexport async function wireStarvationDetector(args: {\n\tsession: LossArcSession | undefined;\n\ttrackName: string;\n\tfecInterleaveMs: number;\n\tfecK: number;\n\tmakeDetector(config: { windowMs: number; onStarvation(): void; onRecovery(): void }): StarvationDetectorLike;\n\twarn?(message: string): void;\n}): Promise<StarvationDetectorLike | null> {\n\tif (!args.session || args.fecInterleaveMs <= 0 || args.fecK <= 0) return null;\n\tconst detector = wireStallDetector({\n\t\tsession: args.session,\n\t\twindowMs: Math.ceil(args.fecInterleaveMs * 3),\n\t\tmakeDetector: args.makeDetector,\n\t});\n\tif (!detector) args.warn?.(`Unable to create starvation detector for ${args.trackName}`);\n\treturn detector;\n}\n", "/**\n * @blockcast/transport - DRIAD Discovery (RFC 8777)\n *\n * DRIAD (DNS Reverse IP AMT Discovery) discovers AMT relays based on the\n * **source address**, NOT the multicast group. The source network operator\n * configures DNS records for their source IPs to advertise which AMT relay(s)\n * can tunnel their traffic.\n *\n * Example: For source 69.25.95.10 sending to group 232.0.0.1:\n * Query: 10.95.25.69.amt.in-addr.arpa (source-based, NOT group-based)\n */\n\nimport type { DRIADRelay } from \"./types.js\";\n\n/**\n * DRIAD (DNS Reverse IP AMT Discovery) - RFC 8777\n * Discovers AMT relays based on source address via DNS lookups\n */\nexport class DRIADDiscovery {\n\tprivate cache = new Map<string, DRIADRelay[]>();\n\tprivate cacheExpiry = 300_000; // 5 minutes\n\n\t/**\n\t * Discover AMT relays for a multicast source address (RFC 8777)\n\t *\n\t * @param source - The multicast source IP address (NOT the group!)\n\t * @returns Array of discovered AMT relays that can tunnel traffic from this source\n\t */\n\tasync discoverRelays(source: string): Promise<DRIADRelay[]> {\n\t\t// Check cache first\n\t\tconst cached = this.cache.get(source);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\ttry {\n\t\t\t// Convert source to DRIAD format (reversed octets)\n\t\t\tconst reversedSource = source.split(\".\").reverse().join(\".\");\n\t\t\tconst queryDomain = `${reversedSource}.amt.in-addr.arpa`;\n\n\t\t\tconsole.log(`[DRIAD] Discovering relays for source ${source} via ${queryDomain}`);\n\n\t\t\t// In browser, we use a DRIAD proxy service or DNS-over-HTTPS\n\t\t\tconst relays = await this.queryDRIADProxy(source);\n\n\t\t\tif (relays.length > 0) {\n\t\t\t\tthis.cache.set(source, relays);\n\t\t\t\tsetTimeout(() => this.cache.delete(source), this.cacheExpiry);\n\t\t\t}\n\n\t\t\treturn relays;\n\t\t} catch (error) {\n\t\t\tconsole.warn(`[DRIAD] Discovery failed for source ${source}:`, error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Query DRIAD proxy service (browser-compatible)\n\t */\n\tprivate async queryDRIADProxy(source: string): Promise<DRIADRelay[]> {\n\t\t// List of known DRIAD proxy endpoints\n\t\tconst proxyEndpoints = [\n\t\t\t\"https://driad.amt.net/api/v1/relays\",\n\t\t\t\"https://multicast-gateway.cloudflare.com/driad\",\n\t\t];\n\n\t\tfor (const endpoint of proxyEndpoints) {\n\t\t\ttry {\n\t\t\t\t// Query by source address per RFC 8777\n\t\t\t\tconst response = await fetch(`${endpoint}?source=${encodeURIComponent(source)}`, {\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\theaders: { Accept: \"application/json\" },\n\t\t\t\t\tsignal: AbortSignal.timeout(5000),\n\t\t\t\t});\n\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as { relays: DRIADRelay[] };\n\t\t\t\t\treturn data.relays || [];\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Try next proxy endpoint\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: Try DNS-over-HTTPS query\n\t\tconst dohRelay = await this.queryDNSOverHTTPS(source);\n\t\tif (dohRelay) {\n\t\t\treturn [dohRelay];\n\t\t}\n\n\t\t// Final fallback to well-known AMT relays\n\t\treturn [\n\t\t\t{ host: \"amt-relay.m2icast.net\", port: 2268, priority: 10, weight: 100, asn: 0 },\n\t\t\t{ host: \"amt.akamaistream.net\", port: 2268, priority: 20, weight: 50, asn: 0 },\n\t\t];\n\t}\n\n\t/**\n\t * Query DNS-over-HTTPS for DRIAD relay based on source address (RFC 8777)\n\t */\n\tprivate async queryDNSOverHTTPS(source: string): Promise<DRIADRelay | null> {\n\t\ttry {\n\t\t\t// Build DRIAD query name from source address (reversed octets)\n\t\t\tconst reversedSource = source.split(\".\").reverse().join(\".\");\n\t\t\tconst queryName = `${reversedSource}.amt.in-addr.arpa`;\n\n\t\t\tconst dohUrl = `https://cloudflare-dns.com/dns-query?name=${queryName}&type=A`;\n\t\t\tconst response = await fetch(dohUrl, {\n\t\t\t\theaders: { Accept: \"application/dns-json\" },\n\t\t\t\tsignal: AbortSignal.timeout(3000),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst result = (await response.json()) as { Answer?: Array<{ data: string }> };\n\n\t\t\t// Extract first A/AAAA record\n\t\t\tif (result.Answer && result.Answer.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\thost: result.Answer[0].data,\n\t\t\t\t\tport: 2268,\n\t\t\t\t\tpriority: 10,\n\t\t\t\t\tweight: 100,\n\t\t\t\t\tasn: 0,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[DRIAD] DNS-over-HTTPS query failed:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Select best relay based on RTT measurements\n\t */\n\tasync selectBestRelay(relays: DRIADRelay[]): Promise<DRIADRelay | null> {\n\t\tif (relays.length === 0) return null;\n\n\t\t// Measure RTT to each relay\n\t\tconst measured = await Promise.all(\n\t\t\trelays.map(async (relay) => {\n\t\t\t\tconst rtt = await this.measureRTT(relay.host, relay.port);\n\t\t\t\treturn { ...relay, rtt };\n\t\t\t}),\n\t\t);\n\n\t\t// Sort by priority, then by RTT\n\t\tmeasured.sort((a, b) => {\n\t\t\tif (a.priority !== b.priority) return a.priority - b.priority;\n\t\t\treturn (a.rtt || Infinity) - (b.rtt || Infinity);\n\t\t});\n\n\t\tconsole.log(`[DRIAD] Selected relay: ${measured[0].host} (RTT: ${measured[0].rtt}ms)`);\n\t\treturn measured[0];\n\t}\n\n\tprivate async measureRTT(host: string, _port: number): Promise<number> {\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\t// Use HTTP/HTTPS probe since we can't do UDP from browser\n\t\t\tawait fetch(`https://${host}/health`, {\n\t\t\t\tmethod: \"HEAD\",\n\t\t\t\tsignal: AbortSignal.timeout(2000),\n\t\t\t\tmode: \"no-cors\",\n\t\t\t});\n\t\t\treturn performance.now() - start;\n\t\t} catch {\n\t\t\treturn Infinity;\n\t\t}\n\t}\n\n\t/**\n\t * Clear the relay cache\n\t */\n\tclearCache(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/**\n\t * Get cached relays for a source address\n\t */\n\tgetCached(source: string): DRIADRelay[] | undefined {\n\t\treturn this.cache.get(source);\n\t}\n}\n\n// Export singleton instance\nexport const driadDiscovery = new DRIADDiscovery();\n", "/**\n * @blockcast/transport - Transport Manager\n *\n * Unified transport abstraction for MoQ players supporting:\n * - window.multicast gateway subscriptions\n * - AMT proxy tunneling (RFC 7450) with optional DRIAD relay discovery\n * - WebTransport/QUIC (MoQ-lite protocol)\n * - WebSocket fallback\n *\n * Transport promotion hierarchy (WebTransport-first for fast startup):\n * 1. WebTransport - immediate connection for fast channel select\n * 2. Background discovery - check if window.multicast is available\n * 3. Promote to gateway multicast if available\n * 4. WebSocket fallback if WebTransport unavailable\n *\n * Features:\n * - Progressive adaptive bitrate based on network conditions\n * - FEC (Forward Error Correction) for burst loss recovery\n * - Seamless transport switching with session continuity\n * - Unified FEC/ABR pipeline across all transport modes\n */\n\nimport type {\n\tTransportConfig,\n\tTransportSubscription,\n\tTransportCapabilities,\n\tTransportState,\n\tQualityLevel,\n\tWindowMulticastCapabilities,\n\tWindowMulticastGateway,\n\tWindowMulticastSubscriptionState,\n} from \"./types.js\";\nimport { DRIADDiscovery } from \"./driad-discovery.js\";\nimport { AdaptiveBitrateController } from \"./abr-controller.js\";\nimport {\n\tMulticastOrchestrator,\n\ttype MulticastTierPacket,\n\ttype OrchestratorCommand,\n\ttype OrchestratorOptions,\n} from \"./multicast-orchestrator.js\";\nimport type { MulticastTierEndpoint } from \"./multicast-tier.js\";\nimport { multicastSubscriptionPool } from \"./multicast-subscription-pool.js\";\nimport { normalizeMulticastEndpoint } from \"./endpoint-config.js\";\n\n/**\n * Transport Manager\n *\n * Manages transport connections with automatic mode selection:\n * WebTransport -> window.multicast -> AMT proxy -> WebSocket\n *\n * Renamed from SSMTransportManager - the old name is preserved\n * as a deprecated alias for backwards compatibility.\n */\nexport class TransportManager {\n\tprivate state: TransportState = {\n\t\tmode: \"disconnected\",\n\t\tconnected: false,\n\t\tpacketsReceived: 0,\n\t\tbytesReceived: 0,\n\t\tlastPacketTime: 0,\n\t};\n\n\tprivate capabilities: TransportCapabilities | null = null;\n\tprivate subscription: TransportSubscription | null = null;\n\tprivate driad = new DRIADDiscovery();\n\tprivate abr: AdaptiveBitrateController;\n\tprivate multicastOrchestrator: MulticastOrchestrator | null = null;\n\tprivate onOrchestratorCommand?: (command: OrchestratorCommand) => void;\n\tprivate connectionGeneration = 0;\n\n\tprivate onPacket?: (data: ArrayBuffer) => void;\n\tprivate onError?: (error: Error) => void;\n\tprivate onStateChange?: (state: TransportState) => void;\n\n\tconstructor() {\n\t\tthis.abr = new AdaptiveBitrateController();\n\t}\n\n\tprivate getWindow(): Window | undefined {\n\t\treturn typeof window === \"undefined\" ? undefined : window;\n\t}\n\n\tprivate getWindowMulticastGateway(): WindowMulticastGateway | undefined {\n\t\treturn this.getWindow()?.multicast;\n\t}\n\n\tprivate async getGatewayCapabilities(\n\t\tgateway: WindowMulticastGateway,\n\t): Promise<WindowMulticastCapabilities | undefined> {\n\t\tif (!gateway?.capabilities) return undefined;\n\t\ttry {\n\t\t\treturn typeof gateway.capabilities === \"function\" ?\n\t\t\t\tawait gateway.capabilities() :\n\t\t\t\tgateway.capabilities;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[Transport] window.multicast capabilities failed:\", error);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async gatewaySupportsBytes(gateway: WindowMulticastGateway | undefined): Promise<boolean> {\n\t\tif (typeof gateway?.subscribe !== \"function\") return false;\n\t\tif (!gateway.capabilities) return true;\n\n\t\tconst capabilities = await this.getGatewayCapabilities(gateway);\n\t\tif (!capabilities) return false;\n\t\tconst consumeModes = capabilities.consumeModes;\n\t\treturn !consumeModes || consumeModes.includes(\"bytes\");\n\t}\n\n\t/**\n\t * Detect available transport capabilities\n\t */\n\tasync detectCapabilities(): Promise<TransportCapabilities> {\n\t\tif (this.capabilities) return this.capabilities;\n\n\t\tconst gateway = this.getWindowMulticastGateway();\n\t\tconst hasGatewayBytes = await this.gatewaySupportsBytes(gateway);\n\t\tconst win = this.getWindow();\n\n\t\tthis.capabilities = {\n\t\t\tnativeSSM: hasGatewayBytes,\n\t\t\tnativeIGMP: hasGatewayBytes,\n\t\t\tdirectSockets: typeof win?.UDPSocket !== \"undefined\",\n\t\t\tamt: false,\n\t\t\tdriad: true, // DRIAD uses HTTP proxy, always available\n\t\t\tfecDecode: false,\n\t\t};\n\n\t\tconsole.log(\"[Transport] Capabilities:\", this.capabilities);\n\t\treturn this.capabilities;\n\t}\n\n\t/**\n\t * Connect using best available transport\n\t *\n\t * Promotion hierarchy:\n\t * 1. window.multicast gateway bytes\n\t * 2. AMT proxy (specified or discovered via DRIAD)\n\t * 3. MOQ WebTransport (final fallback)\n\t */\n\tasync connect(config: TransportConfig): Promise<boolean> {\n\t\tconst generation = ++this.connectionGeneration;\n\t\tthis.subscription?.close();\n\t\tthis.subscription = null;\n\t\tthis.state = {\n\t\t\tmode: \"disconnected\",\n\t\t\tconnected: false,\n\t\t\tpacketsReceived: 0,\n\t\t\tbytesReceived: 0,\n\t\t\tlastPacketTime: 0,\n\t\t};\n\t\tthis.onStateChange?.(this.state);\n\t\tconst endpoint = normalizeMulticastEndpoint({\n\t\t\tgroupAddress: config.group,\n\t\t\tsourceAddress: config.source,\n\t\t\tport: config.port,\n\t\t\tmode: config.mode,\n\t\t\tamtRelay: config.amtRelay,\n\t\t\tamtRelayPort: config.amtRelayPort,\n\t\t});\n\t\tif (!endpoint) {\n\t\t\tconst error = new Error(\"Invalid multicast endpoint configuration\");\n\t\t\tthis.onError?.(error);\n\t\t\treturn false;\n\t\t}\n\t\tconfig = {\n\t\t\t...config,\n\t\t\tgroup: endpoint.groupAddress,\n\t\t\tsource: endpoint.sourceAddress ?? \"\",\n\t\t\tmode: endpoint.mode,\n\t\t\tamtRelay: endpoint.amtRelay,\n\t\t\tamtRelayPort: endpoint.amtRelayPort,\n\t\t};\n\t\tconst caps = await this.detectCapabilities();\n\t\tif (generation !== this.connectionGeneration) return false;\n\n\t\t// Initialize ABR with requested quality\n\t\tif (config.initialQuality) {\n\t\t\tthis.abr = new AdaptiveBitrateController(config.initialQuality);\n\t\t}\n\n\t\tthis.abr.setCallback((quality) => {\n\t\t\tthis.state.quality = quality;\n\t\t\tthis.subscription?.onqualitychange?.(quality);\n\t\t\tthis.onStateChange?.(this.state);\n\t\t});\n\n\t\t// Try transport hierarchy\n\t\tlet connected = false;\n\n\t\t// 1. Try the canonical browser multicast gateway.\n\t\tif (caps.nativeSSM || caps.nativeIGMP) {\n\t\t\tconsole.log(\"[Transport] Attempting window.multicast...\");\n\t\t\tconnected = await this.connectWindowMulticast(config, generation);\n\t\t\tif (connected) {\n\t\t\t\tif (generation !== this.connectionGeneration) return false;\n\t\t\t\tthis.state.mode = \"multicast\";\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Try AMT proxy (with optional DRIAD discovery)\n\t\tif (!connected) {\n\t\t\tconsole.log(\"[Transport] Attempting AMT tunnel...\");\n\t\t\tconnected = await this.connectAmt(config, generation);\n\t\t\tif (connected) {\n\t\t\t\tif (generation !== this.connectionGeneration) return false;\n\t\t\t\tthis.state.mode = \"amt\";\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// 3. Fallback to MOQ\n\t\tconsole.log(\"[Transport] All multicast transports failed, falling back to MOQ\");\n\t\treturn false;\n\t}\n\n\t/**\n\t * Connect via the lowercase window.multicast bytes gateway.\n\t */\n\tprivate async connectWindowMulticast(config: TransportConfig, generation: number): Promise<boolean> {\n\t\ttry {\n\t\t\tconst gateway = this.getWindowMulticastGateway();\n\t\t\tif (typeof gateway?.subscribe !== \"function\") return false;\n\n\t\t\tconst lease = await multicastSubscriptionPool.acquire(gateway, config, {\n\t\t\t\tonBytes: (data) => {\n\t\t\t\t\tif (generation === this.connectionGeneration) this.handlePacket(data);\n\t\t\t\t},\n\t\t\t\tonState: (state: WindowMulticastSubscriptionState) => {\n\t\t\t\t\tif (generation !== this.connectionGeneration) return;\n\t\t\t\t\tthis.state.connected = state !== \"closed\";\n\t\t\t\t\tthis.onStateChange?.(this.state);\n\t\t\t\t},\n\t\t\t\tonError: (error) => {\n\t\t\t\t\tif (generation !== this.connectionGeneration) return;\n\t\t\t\t\tconsole.error(\"[Transport] window.multicast error:\", error);\n\t\t\t\t\tthis.onError?.(error);\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (generation !== this.connectionGeneration) {\n\t\t\t\tawait lease.release();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.subscription = {\n\t\t\t\tid: lease.key,\n\t\t\t\tconfig,\n\t\t\t\tclose: () => { void lease.release(); },\n\t\t\t};\n\n\t\t\tthis.state = {\n\t\t\t\tmode: \"multicast\",\n\t\t\t\tconnected: true,\n\t\t\t\tgroup: config.group,\n\t\t\t\tsource: config.source,\n\t\t\t\tquality: this.abr.getCurrentQuality(),\n\t\t\t\tpacketsReceived: 0,\n\t\t\t\tbytesReceived: 0,\n\t\t\t\tlastPacketTime: 0,\n\t\t\t};\n\n\t\t\tconsole.log(`[Transport] Connected via window.multicast: (${config.source}, ${config.group})`);\n\t\t\tthis.onStateChange?.(this.state);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[Transport] window.multicast failed:\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Connect via AMT proxy (with optional DRIAD discovery)\n\t */\n\tprivate async connectAmt(config: TransportConfig, generation: number): Promise<boolean> {\n\t\ttry {\n\t\t\tlet amtRelay = config.amtRelay;\n\n\t\t\t// Use DRIAD to discover relay if not specified (RFC 8777)\n\t\t\t// DRIAD discovers relays based on SOURCE address, not group\n\t\t\tif (!amtRelay && config.driadEnabled !== false && config.source) {\n\t\t\t\tconsole.log(`[Transport] Discovering AMT relay via DRIAD for source ${config.source}...`);\n\t\t\t\tconst relays = await this.driad.discoverRelays(config.source);\n\t\t\t\tif (generation !== this.connectionGeneration) return false;\n\t\t\t\tconst bestRelay = await this.driad.selectBestRelay(relays);\n\t\t\t\tif (generation !== this.connectionGeneration) return false;\n\n\t\t\t\tif (bestRelay) {\n\t\t\t\t\tamtRelay = bestRelay.host;\n\t\t\t\t\tthis.state.amtRelay = amtRelay;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!amtRelay) {\n\t\t\t\tconsole.warn(\"[Transport] No AMT relay available (DRIAD requires source address for SSM)\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Use the WebSocket-based AMT proxy when a relay is known.\n\t\t\treturn await this.connectAmtProxy(config, amtRelay, generation);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[Transport] AMT connection failed:\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Connect via WebSocket-based AMT proxy (browser fallback)\n\t */\n\tprivate async connectAmtProxy(config: TransportConfig, amtRelay: string, generation: number): Promise<boolean> {\n\t\ttry {\n\t\t\tconst relayHost = amtRelay.includes(\":\") && !amtRelay.startsWith(\"[\") ? `[${amtRelay}]` : amtRelay;\n\t\t\tconst relayAuthority = config.amtRelayPort ? `${relayHost}:${config.amtRelayPort}` : relayHost;\n\t\t\tconst wsUrl = new URL(`wss://${relayAuthority}/amt-proxy`);\n\t\t\twsUrl.searchParams.set(\"group\", config.group);\n\t\t\twsUrl.searchParams.set(\"source\", config.source || \"\");\n\t\t\twsUrl.searchParams.set(\"port\", String(config.port));\n\n\t\t\tconst ws = new WebSocket(wsUrl);\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tws.binaryType = \"arraybuffer\";\n\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\tws.close();\n\t\t\t\t\treject(new Error(\"AMT proxy connection timeout\"));\n\t\t\t\t}, 10000);\n\n\t\t\t\tws.onopen = () => {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (generation !== this.connectionGeneration) {\n\t\t\t\t\t\tws.close();\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log(`[Transport] Connected to AMT proxy: ${amtRelay}`);\n\n\t\t\t\t\tthis.subscription = {\n\t\t\t\t\t\tid: `amt-proxy-${Date.now()}`,\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tclose: () => ws.close(),\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.state = {\n\t\t\t\t\t\tmode: \"amt\",\n\t\t\t\t\t\tconnected: true,\n\t\t\t\t\t\tgroup: config.group,\n\t\t\t\t\t\tsource: config.source,\n\t\t\t\t\t\tamtRelay,\n\t\t\t\t\t\tquality: this.abr.getCurrentQuality(),\n\t\t\t\t\t\tpacketsReceived: 0,\n\t\t\t\t\t\tbytesReceived: 0,\n\t\t\t\t\t\tlastPacketTime: 0,\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.onStateChange?.(this.state);\n\t\t\t\t\tresolve(true);\n\t\t\t\t};\n\n\t\t\t\tws.onmessage = (event) => {\n\t\t\t\t\tif (generation === this.connectionGeneration && event.data instanceof ArrayBuffer) {\n\t\t\t\t\t\tthis.handlePacket(event.data);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tws.onerror = (error) => {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (generation !== this.connectionGeneration) {\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconsole.error(\"[Transport] AMT proxy error:\", error);\n\t\t\t\t\treject(error);\n\t\t\t\t};\n\n\t\t\t\tws.onclose = () => {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tif (generation !== this.connectionGeneration) {\n\t\t\t\t\t\tresolve(false);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.state.connected = false;\n\t\t\t\t\tthis.onStateChange?.(this.state);\n\t\t\t\t\tresolve(false);\n\t\t\t\t};\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[Transport] AMT proxy failed:\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Handle received packet\n\t */\n\tprivate handlePacket(data: ArrayBuffer) {\n\t\tthis.state.packetsReceived++;\n\t\tthis.state.bytesReceived += data.byteLength;\n\t\tthis.state.lastPacketTime = Date.now();\n\n\t\t// Update ABR with sample\n\t\tthis.abr.recordSample(data.byteLength, 0);\n\n\t\t// Forward to callback\n\t\tthis.onPacket?.(data);\n\t}\n\n\t/**\n\t * Get current transport state\n\t */\n\tgetState(): TransportState {\n\t\treturn { ...this.state };\n\t}\n\n\t/**\n\t * Get current quality level\n\t */\n\tgetQuality(): QualityLevel {\n\t\treturn this.abr.getCurrentQuality();\n\t}\n\n\t/**\n\t * Force quality level\n\t */\n\tsetQuality(name: string) {\n\t\tthis.abr.setQuality(name);\n\t}\n\n\t/** Configures multicast-only ABR while leaving the caller's MoQ ABR untouched. */\n\tconfigureMulticastTiers(\n\t\toptions: OrchestratorOptions,\n\t\tonCommand?: (command: OrchestratorCommand) => void,\n\t): MulticastTierEndpoint | undefined {\n\t\tthis.dispatchOrchestratorCommand(this.multicastOrchestrator?.cancel());\n\t\tthis.multicastOrchestrator = new MulticastOrchestrator(options);\n\t\tthis.onOrchestratorCommand = onCommand;\n\t\treturn this.multicastOrchestrator.start();\n\t}\n\n\trecordMulticastGoodput(bitrate: number | undefined): OrchestratorCommand | undefined {\n\t\treturn this.dispatchOrchestratorCommand(this.multicastOrchestrator?.onGoodput({ bitrate }));\n\t}\n\n\trecordMulticastTierPacket(packet: MulticastTierPacket): OrchestratorCommand | undefined {\n\t\treturn this.dispatchOrchestratorCommand(this.multicastOrchestrator?.onTierPacket(packet));\n\t}\n\n\tpollMulticastTierSwitch(): OrchestratorCommand | undefined {\n\t\treturn this.dispatchOrchestratorCommand(this.multicastOrchestrator?.poll());\n\t}\n\n\t/**\n\t * Set packet callback\n\t */\n\tsetPacketCallback(callback: (data: ArrayBuffer) => void) {\n\t\tthis.onPacket = callback;\n\t}\n\n\t/**\n\t * Set error callback\n\t */\n\tsetErrorCallback(callback: (error: Error) => void) {\n\t\tthis.onError = callback;\n\t}\n\n\t/**\n\t * Set state change callback\n\t */\n\tsetStateCallback(callback: (state: TransportState) => void) {\n\t\tthis.onStateChange = callback;\n\t}\n\n\t/**\n\t * Disconnect\n\t */\n\tdisconnect() {\n\t\tthis.connectionGeneration++;\n\t\tthis.subscription?.close();\n\t\tthis.subscription = null;\n\t\tthis.dispatchOrchestratorCommand(this.multicastOrchestrator?.cancel());\n\t\tthis.multicastOrchestrator = null;\n\t\tthis.onOrchestratorCommand = undefined;\n\t\tthis.state = {\n\t\t\tmode: \"disconnected\",\n\t\t\tconnected: false,\n\t\t\tpacketsReceived: 0,\n\t\t\tbytesReceived: 0,\n\t\t\tlastPacketTime: 0,\n\t\t};\n\t\tthis.onStateChange?.(this.state);\n\t}\n\n\tprivate dispatchOrchestratorCommand(command: OrchestratorCommand | undefined): OrchestratorCommand | undefined {\n\t\tif (command) this.onOrchestratorCommand?.(command);\n\t\treturn command;\n\t}\n}\n\n// Export singleton instance\nexport const transportManager = new TransportManager();\n\n/**\n * @deprecated Use `TransportManager` instead. This alias is preserved for backwards compatibility.\n */\nexport const SSMTransportManager = TransportManager;\n\n/**\n * @deprecated Use `transportManager` instead. This alias is preserved for backwards compatibility.\n */\nexport const ssmTransport = transportManager;\n", "export interface FallbackTrack { name: string; type: \"video\" | \"audio\"; }\nexport interface FallbackGroup { readFrame(): Promise<Uint8Array | undefined>; close(): void; }\nexport interface FallbackSubscription { nextGroup(): Promise<FallbackGroup | undefined>; close(): void; }\nexport interface FallbackBroadcast { subscribe(name: string, priority: number): FallbackSubscription; }\nexport interface FallbackSessions { enterUnicastFallback(): void; }\nexport type FallbackGroupLike = FallbackGroup;\nexport type FallbackSubLike = FallbackSubscription;\nexport type FallbackBroadcastLike = FallbackBroadcast;\nexport interface FallbackSessionManagerLike extends FallbackSessions { hasActiveFallback(): boolean; }\n\nexport function runUnicastFallback(input: {\n\ttracks: FallbackTrack[];\n\tbroadcast: FallbackBroadcast;\n\tsessions: FallbackSessions;\n\tonPacket(packet: Uint8Array): void;\n\tpriority: number;\n\tsignal: AbortSignal;\n\tonError?(trackName: string, error: unknown): void;\n}): { done: Promise<void> } {\n\tinput.sessions.enterUnicastFallback();\n\tconst tracks = Array.from(new Map(input.tracks.map((track) => [track.name, track])).values());\n\tlet resolveAbort: (value: undefined) => void;\n\tconst aborted = new Promise<undefined>((resolve) => {\n\t\tresolveAbort = resolve;\n\t\tif (input.signal.aborted) resolve(undefined);\n\t});\n\tconst onAbort = () => resolveAbort(undefined);\n\tif (!input.signal.aborted) input.signal.addEventListener(\"abort\", onAbort, { once: true });\n\n\tconst drainers = tracks.map(async (track) => {\n\t\tconst subscription = input.broadcast.subscribe(track.name, input.priority);\n\t\ttry {\n\t\t\twhile (!input.signal.aborted) {\n\t\t\t\tconst nextGroup = subscription.nextGroup();\n\t\t\t\tconst group = await Promise.race([nextGroup, aborted]);\n\t\t\t\tif (!group && input.signal.aborted) void nextGroup.then((lateGroup) => lateGroup?.close());\n\t\t\t\tif (!group) break;\n\t\t\t\ttry {\n\t\t\t\t\twhile (!input.signal.aborted) {\n\t\t\t\t\t\tconst frame = await Promise.race([group.readFrame(), aborted]);\n\t\t\t\t\t\tif (!frame) break;\n\t\t\t\t\t\tinput.onPacket(frame);\n\t\t\t\t\t}\n\t\t\t\t} finally { group.close(); }\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!input.signal.aborted) {\n\t\t\t\tif (input.onError) input.onError(track.name, error);\n\t\t\t\telse console.warn(`[unicast-fallback] drainer error on ${track.name}:`, error);\n\t\t\t}\n\t\t} finally { subscription.close(); }\n\t});\n\tconst done = Promise.all(drainers).then(() => undefined).finally(() => {\n\t\tinput.signal.removeEventListener(\"abort\", onAbort);\n\t});\n\treturn { done };\n}\n", "import { AdaptiveBitrateController } from \"./abr-controller.js\";\nimport {\n\tisMulticastExplicitlyDisabled,\n\tisUsableMulticastEndpoint,\n\tisValidMulticastGroupAddress,\n\tmulticastEndpointKey,\n\tnormalizeMulticastEndpoint,\n\treadUrlMulticastConfig,\n} from \"./endpoint-config.js\";\nimport { MulticastOrchestrator } from \"./multicast-orchestrator.js\";\nimport { MulticastSubscriptionPool } from \"./multicast-subscription-pool.js\";\nimport { endpointKey, selectMulticastTier } from \"./multicast-tier.js\";\nimport { TransportStallDetector } from \"./stall-detector.js\";\nimport { makeSourceSwitchHandler, wireStarvationDetector } from \"./takeover.js\";\nimport { TransportManager } from \"./transport-manager.js\";\nimport { runUnicastFallback } from \"./unicast-fallback.js\";\n\nconst api = {\n\tAdaptiveBitrateController,\n\tMulticastOrchestrator,\n\tMulticastSubscriptionPool,\n\tTransportManager,\n\tTransportStallDetector,\n\tendpointKey,\n\tisMulticastExplicitlyDisabled,\n\tisUsableMulticastEndpoint,\n\tisValidMulticastGroupAddress,\n\tmakeSourceSwitchHandler,\n\tmulticastEndpointKey,\n\tnormalizeMulticastEndpoint,\n\treadUrlMulticastConfig,\n\trunUnicastFallback,\n\tselectMulticastTier,\n\twireStarvationDetector,\n};\n\nObject.defineProperty(globalThis, \"__MmtTransport\", {\n\tconfigurable: true,\n\tenumerable: false,\n\twritable: false,\n\tvalue: Object.freeze(api),\n});\n\nexport default api;\n"],
5
+ "mappings": "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",
6
+ "names": ["entry"]
7
+ }
@@ -0,0 +1,56 @@
1
+ import { type MulticastTierEndpoint } from "./multicast-tier.js";
2
+ export type OrchestratorState = "stable" | "joining";
3
+ export type OrchestratorCommand = {
4
+ kind: "join-target";
5
+ current: MulticastTierEndpoint;
6
+ target: MulticastTierEndpoint;
7
+ } | {
8
+ kind: "commit-swap";
9
+ target: MulticastTierEndpoint;
10
+ old: MulticastTierEndpoint;
11
+ } | {
12
+ kind: "abandon-join";
13
+ current: MulticastTierEndpoint;
14
+ target: MulticastTierEndpoint;
15
+ };
16
+ export interface MulticastTierPacket {
17
+ endpointKey: string;
18
+ keyframe: boolean;
19
+ timestamp: number;
20
+ hasValidCodecConfig: boolean;
21
+ }
22
+ export interface OrchestratorOptions {
23
+ endpoints: readonly MulticastTierEndpoint[];
24
+ initialEndpoint?: MulticastTierEndpoint;
25
+ adaptive?: boolean;
26
+ now: () => number;
27
+ dwellMs?: number;
28
+ joinDeadlineMs?: number;
29
+ }
30
+ export interface GoodputSample {
31
+ bitrate: number | undefined;
32
+ }
33
+ export declare const DEFAULT_DWELL_MS = 3000;
34
+ export declare const DEFAULT_JOIN_DEADLINE_MS = 5000;
35
+ export declare function goodputFromStats(previous: {
36
+ bytesReceived: number;
37
+ atMs: number;
38
+ } | undefined, current: {
39
+ bytesReceived: number;
40
+ atMs: number;
41
+ }, minSampleMs?: number): number | undefined;
42
+ export declare class MulticastOrchestrator {
43
+ #private;
44
+ private readonly options;
45
+ constructor(options: OrchestratorOptions);
46
+ get state(): OrchestratorState;
47
+ get current(): MulticastTierEndpoint | undefined;
48
+ get target(): MulticastTierEndpoint | undefined;
49
+ get estimate(): number | undefined;
50
+ start(): MulticastTierEndpoint | undefined;
51
+ onGoodput(sample: GoodputSample): OrchestratorCommand | undefined;
52
+ onTierPacket(packet: MulticastTierPacket): OrchestratorCommand | undefined;
53
+ poll(): OrchestratorCommand | undefined;
54
+ cancel(): OrchestratorCommand | undefined;
55
+ }
56
+ //# sourceMappingURL=multicast-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multicast-orchestrator.d.ts","sourceRoot":"","sources":["../src/multicast-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEnG,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,CAAC;AACrD,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,GAAG,EAAE,qBAAqB,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IACnC,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC5C,eAAe,CAAC,EAAE,qBAAqB,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAAG,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAAE;AAE/D,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EAC7D,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAChD,WAAW,SAAM,GACf,MAAM,GAAG,SAAS,CAMpB;AAED,qBAAa,qBAAqB;;IAWrB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IASzD,IAAI,KAAK,IAAI,iBAAiB,CAAwB;IACtD,IAAI,OAAO,IAAI,qBAAqB,GAAG,SAAS,CAA0B;IAC1E,IAAI,MAAM,IAAI,qBAAqB,GAAG,SAAS,CAAyB;IACxE,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAA2B;IAE7D,KAAK,IAAI,qBAAqB,GAAG,SAAS;IAK1C,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,mBAAmB,GAAG,SAAS;IA2BjE,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,GAAG,SAAS;IA4B1E,IAAI,IAAI,mBAAmB,GAAG,SAAS;IAMvC,MAAM,IAAI,mBAAmB,GAAG,SAAS;CAiBzC"}
@@ -0,0 +1,123 @@
1
+ import { endpointKey, selectMulticastTier } from "./multicast-tier.js";
2
+ export const DEFAULT_DWELL_MS = 3_000;
3
+ export const DEFAULT_JOIN_DEADLINE_MS = 5_000;
4
+ export function goodputFromStats(previous, current, minSampleMs = 500) {
5
+ if (!previous)
6
+ return undefined;
7
+ const durationMs = current.atMs - previous.atMs;
8
+ const bytes = current.bytesReceived - previous.bytesReceived;
9
+ if (durationMs < minSampleMs || bytes <= 0)
10
+ return undefined;
11
+ return (bytes * 8 * 1000) / durationMs;
12
+ }
13
+ export class MulticastOrchestrator {
14
+ options;
15
+ #state = "stable";
16
+ #current;
17
+ #target;
18
+ #estimate;
19
+ #favored;
20
+ #joinStartedAt = 0;
21
+ #lastCommittedTimestamp = Number.NEGATIVE_INFINITY;
22
+ #dwellMs;
23
+ #joinDeadlineMs;
24
+ constructor(options) {
25
+ this.options = options;
26
+ this.#dwellMs = options.dwellMs ?? DEFAULT_DWELL_MS;
27
+ this.#joinDeadlineMs = options.joinDeadlineMs ?? DEFAULT_JOIN_DEADLINE_MS;
28
+ if (!Number.isFinite(this.#dwellMs) || !Number.isFinite(this.#joinDeadlineMs) ||
29
+ this.#dwellMs <= 0 || this.#joinDeadlineMs <= 0) {
30
+ throw new Error("Multicast dwell and join deadline must be positive");
31
+ }
32
+ }
33
+ get state() { return this.#state; }
34
+ get current() { return this.#current; }
35
+ get target() { return this.#target; }
36
+ get estimate() { return this.#estimate; }
37
+ start() {
38
+ this.#current ??= this.options.initialEndpoint ?? selectMulticastTier(this.options.endpoints, undefined);
39
+ return this.#current;
40
+ }
41
+ onGoodput(sample) {
42
+ const bitrate = sample.bitrate;
43
+ this.#estimate = bitrate;
44
+ if (!this.#current || this.options.adaptive === false)
45
+ return undefined;
46
+ if (this.#state === "joining")
47
+ return this.poll();
48
+ const desired = selectMulticastTier(this.options.endpoints, bitrate);
49
+ if (!desired)
50
+ return undefined;
51
+ const desiredKey = endpointKey(desired);
52
+ if (desiredKey === endpointKey(this.#current)) {
53
+ this.#favored = undefined;
54
+ return undefined;
55
+ }
56
+ const now = this.#now();
57
+ if (!this.#favored || this.#favored.key !== desiredKey) {
58
+ this.#favored = { key: desiredKey, since: now };
59
+ return undefined;
60
+ }
61
+ if (now - this.#favored.since < this.#dwellMs)
62
+ return undefined;
63
+ this.#state = "joining";
64
+ this.#target = desired;
65
+ this.#joinStartedAt = now;
66
+ this.#favored = undefined;
67
+ return { kind: "join-target", current: this.#current, target: desired };
68
+ }
69
+ onTierPacket(packet) {
70
+ if (!this.#current)
71
+ return undefined;
72
+ if (packet.endpointKey === endpointKey(this.#current) && Number.isFinite(packet.timestamp)) {
73
+ this.#lastCommittedTimestamp = Math.max(this.#lastCommittedTimestamp, packet.timestamp);
74
+ }
75
+ if (this.#state !== "joining" || !this.#target)
76
+ return undefined;
77
+ const expired = this.poll();
78
+ if (expired)
79
+ return expired;
80
+ if (packet.endpointKey !== endpointKey(this.#target) ||
81
+ !packet.keyframe ||
82
+ !packet.hasValidCodecConfig ||
83
+ !Number.isFinite(packet.timestamp) ||
84
+ packet.timestamp < this.#lastCommittedTimestamp)
85
+ return undefined;
86
+ const command = {
87
+ kind: "commit-swap",
88
+ target: this.#target,
89
+ old: this.#current,
90
+ };
91
+ this.#lastCommittedTimestamp = packet.timestamp;
92
+ this.#current = this.#target;
93
+ this.#target = undefined;
94
+ this.#state = "stable";
95
+ return command;
96
+ }
97
+ poll() {
98
+ if (this.#state !== "joining" || !this.#target || !this.#current)
99
+ return undefined;
100
+ if (this.#now() - this.#joinStartedAt < this.#joinDeadlineMs)
101
+ return undefined;
102
+ return this.cancel();
103
+ }
104
+ cancel() {
105
+ if (this.#state !== "joining" || !this.#target || !this.#current)
106
+ return undefined;
107
+ const command = {
108
+ kind: "abandon-join",
109
+ current: this.#current,
110
+ target: this.#target,
111
+ };
112
+ this.#target = undefined;
113
+ this.#state = "stable";
114
+ return command;
115
+ }
116
+ #now() {
117
+ const now = this.options.now();
118
+ if (!Number.isFinite(now))
119
+ throw new Error("Multicast orchestrator clock must be finite");
120
+ return now;
121
+ }
122
+ }
123
+ //# sourceMappingURL=multicast-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multicast-orchestrator.js","sourceRoot":"","sources":["../src/multicast-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { TransportConfig, WindowMulticastGateway, WindowMulticastSubscriptionState } from "./types.js";
2
+ export interface MulticastSubscriptionConsumer {
3
+ onBytes(data: ArrayBuffer): void;
4
+ onState?(state: WindowMulticastSubscriptionState): void;
5
+ onError?(error: Error): void;
6
+ }
7
+ export interface MulticastSubscriptionLease {
8
+ readonly key: string;
9
+ release(): Promise<void>;
10
+ }
11
+ /** Shares only IWA multicast/AMT subscriptions. Unicast sessions remain per manager. */
12
+ export declare class MulticastSubscriptionPool {
13
+ #private;
14
+ acquire(gateway: WindowMulticastGateway, config: TransportConfig, consumer: MulticastSubscriptionConsumer): Promise<MulticastSubscriptionLease>;
15
+ get size(): number;
16
+ }
17
+ export declare const multicastSubscriptionPool: MulticastSubscriptionPool;
18
+ //# sourceMappingURL=multicast-subscription-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multicast-subscription-pool.d.ts","sourceRoot":"","sources":["../src/multicast-subscription-pool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,eAAe,EACf,sBAAsB,EAEtB,gCAAgC,EAChC,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,6BAA6B;IAC7C,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,CAAC,KAAK,EAAE,gCAAgC,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC7B;AAaD,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,wFAAwF;AACxF,qBAAa,yBAAyB;;IAK/B,OAAO,CACZ,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,6BAA6B,GACrC,OAAO,CAAC,0BAA0B,CAAC;IA0DtC,IAAI,IAAI,IAAI,MAAM,CAA+B;CA+DjD;AAED,eAAO,MAAM,yBAAyB,2BAAkC,CAAC"}
@@ -0,0 +1,130 @@
1
+ import { multicastEndpointKey, normalizeMulticastEndpoint } from "./endpoint-config.js";
2
+ /** Shares only IWA multicast/AMT subscriptions. Unicast sessions remain per manager. */
3
+ export class MulticastSubscriptionPool {
4
+ #entries = new Map();
5
+ #gatewayIds = new WeakMap();
6
+ #nextGatewayId = 1;
7
+ async acquire(gateway, config, consumer) {
8
+ const endpoint = normalizeMulticastEndpoint({
9
+ groupAddress: config.group,
10
+ sourceAddress: config.source,
11
+ port: config.port,
12
+ mode: config.mode,
13
+ amtRelay: config.amtRelay,
14
+ amtRelayPort: config.amtRelayPort,
15
+ });
16
+ if (!endpoint)
17
+ throw new Error("Invalid multicast endpoint");
18
+ if (typeof gateway.subscribe !== "function")
19
+ throw new Error("window.multicast.subscribe unavailable");
20
+ const key = multicastEndpointKey(endpoint);
21
+ const internalKey = `${this.#gatewayId(gateway)}|${key}`;
22
+ let record = this.#entries.get(internalKey);
23
+ if (!record) {
24
+ record = { claims: 0, entry: Promise.resolve(null) };
25
+ const createdRecord = record;
26
+ record.entry = this.#createEntry(gateway, endpoint, (entry) => {
27
+ if (this.#entries.get(internalKey) === createdRecord)
28
+ this.#entries.delete(internalKey);
29
+ this.#detach(entry);
30
+ }).catch((error) => {
31
+ if (this.#entries.get(internalKey) === createdRecord)
32
+ this.#entries.delete(internalKey);
33
+ throw error;
34
+ });
35
+ this.#entries.set(internalKey, record);
36
+ }
37
+ record.claims++;
38
+ let entry;
39
+ try {
40
+ entry = await record.entry;
41
+ }
42
+ catch (error) {
43
+ record.claims--;
44
+ throw error;
45
+ }
46
+ if (entry.closed) {
47
+ record.claims--;
48
+ return this.acquire(gateway, config, consumer);
49
+ }
50
+ const token = Symbol(key);
51
+ entry.consumers.set(token, consumer);
52
+ let released = false;
53
+ return {
54
+ key,
55
+ release: async () => {
56
+ if (released)
57
+ return;
58
+ released = true;
59
+ entry.consumers.delete(token);
60
+ record.claims--;
61
+ if (record.claims > 0)
62
+ return;
63
+ if (this.#entries.get(internalKey) === record)
64
+ this.#entries.delete(internalKey);
65
+ this.#detach(entry);
66
+ if (!entry.closed)
67
+ await entry.subscription.unsubscribe();
68
+ },
69
+ };
70
+ }
71
+ get size() { return this.#entries.size; }
72
+ async #createEntry(gateway, endpoint, onClosed) {
73
+ const subscription = await gateway.subscribe({
74
+ group: endpoint.groupAddress,
75
+ ...(endpoint.sourceAddress ? { source: endpoint.sourceAddress } : {}),
76
+ port: endpoint.port,
77
+ consume: "bytes",
78
+ ...(endpoint.mode ? { mode: endpoint.mode } : {}),
79
+ ...(endpoint.amtRelay ? { amtRelay: endpoint.amtRelay } : {}),
80
+ ...(endpoint.amtRelayPort ? { amtRelayPort: endpoint.amtRelayPort } : {}),
81
+ });
82
+ const consumers = new Map();
83
+ let entry;
84
+ const bytesListener = (event) => {
85
+ this.#dispatch(consumers, (consumer) => consumer.onBytes(event.detail));
86
+ };
87
+ const stateListener = (event) => {
88
+ this.#dispatch(consumers, (consumer) => consumer.onState?.(event.detail));
89
+ if (event.detail === "closed" && !entry.closed) {
90
+ entry.closed = true;
91
+ onClosed(entry);
92
+ }
93
+ };
94
+ const errorListener = (event) => {
95
+ const error = event.detail instanceof Error ? event.detail : new Error(String(event.detail));
96
+ this.#dispatch(consumers, (consumer) => consumer.onError?.(error));
97
+ };
98
+ entry = { subscription, consumers, bytesListener, stateListener, errorListener, closed: false };
99
+ subscription.addEventListener("bytes", bytesListener);
100
+ subscription.addEventListener("state", stateListener);
101
+ subscription.addEventListener("error", errorListener);
102
+ return entry;
103
+ }
104
+ #gatewayId(gateway) {
105
+ const object = gateway;
106
+ let id = this.#gatewayIds.get(object);
107
+ if (!id) {
108
+ id = this.#nextGatewayId++;
109
+ this.#gatewayIds.set(object, id);
110
+ }
111
+ return id;
112
+ }
113
+ #detach(entry) {
114
+ entry.subscription.removeEventListener("bytes", entry.bytesListener);
115
+ entry.subscription.removeEventListener("state", entry.stateListener);
116
+ entry.subscription.removeEventListener("error", entry.errorListener);
117
+ }
118
+ #dispatch(consumers, callback) {
119
+ for (const consumer of consumers.values()) {
120
+ try {
121
+ callback(consumer);
122
+ }
123
+ catch (error) {
124
+ console.error("[Transport] multicast consumer callback failed", error);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ export const multicastSubscriptionPool = new MulticastSubscriptionPool();
130
+ //# sourceMappingURL=multicast-subscription-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multicast-subscription-pool.js","sourceRoot":"","sources":["../src/multicast-subscription-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=multicast-subscription-pool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multicast-subscription-pool.test.d.ts","sourceRoot":"","sources":["../src/multicast-subscription-pool.test.ts"],"names":[],"mappings":""}