@blockcast/mmt-container 2.2.0-main.079cd7664c0b → 2.2.0-main.0a7228501631
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-runtime.js +301 -50
- package/dist/browser-runtime.js.map +3 -3
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +11 -2
- package/dist/fec-manager.js.map +2 -2
- package/dist/mmtp-container-parser.d.ts +42 -8
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +121 -17
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +120 -18
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/mmtp-decode-pipeline.js +121 -17
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-router.d.ts +14 -3
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +288 -46
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.js +301 -50
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.js +2 -2
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.js +121 -17
- package/dist/no-modules.js.map +2 -2
- package/dist/wasm-mmtp-container-parser.js +121 -17
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +5 -5
- package/src/fec-manager.ts +23 -2
- package/src/mmtp-container-parser.ts +210 -21
- package/src/mmtp-router.ts +289 -43
package/dist/browser-runtime.js
CHANGED
|
@@ -8625,9 +8625,9 @@ async function openSubscription(rx, openedPromise, options, packetIds) {
|
|
|
8625
8625
|
} catch (error) {
|
|
8626
8626
|
throw new MulticastError("subscription-failed", "MulticastReceiver did not open", error);
|
|
8627
8627
|
}
|
|
8628
|
-
if (
|
|
8628
|
+
if (opened.integrityScheme !== INTEGRITY_SCHEME) {
|
|
8629
8629
|
await rx.close();
|
|
8630
|
-
throw unsupported(`integrity scheme ${String(
|
|
8630
|
+
throw unsupported(`integrity scheme ${String(opened.integrityScheme)} is not ${INTEGRITY_SCHEME}`);
|
|
8631
8631
|
}
|
|
8632
8632
|
const events = new EventTarget();
|
|
8633
8633
|
let state = "connected";
|
|
@@ -16844,9 +16844,15 @@ var FECManager = class _FECManager {
|
|
|
16844
16844
|
rebuilt = /* @__PURE__ */ new Set();
|
|
16845
16845
|
this.#blockRebuiltEsis.set(key, rebuilt);
|
|
16846
16846
|
}
|
|
16847
|
+
const config = this.#trackConfigs.get(packetId);
|
|
16848
|
+
const wasShort = config !== void 0 && this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config) !== void 0;
|
|
16847
16849
|
rebuilt.add(located.esi);
|
|
16848
16850
|
const recovered = this.#feedDecoderSource(packetId, ssId, symbolData, catalogTrackName, sbn);
|
|
16849
|
-
if (recovered === null
|
|
16851
|
+
if (recovered === null && !this.#completeWhenSourcesPresent(key, sbn, located.block.length, packetId, catalogTrackName)) {
|
|
16852
|
+
if (wasShort && this.#activeBlocks.has(key) && this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config) === void 0) {
|
|
16853
|
+
this.#armBlockTimeout(sbn, packetId, catalogTrackName, this.#multicastWaitMs(config));
|
|
16854
|
+
}
|
|
16855
|
+
}
|
|
16850
16856
|
return { taken: true, recovered };
|
|
16851
16857
|
}
|
|
16852
16858
|
/**
|
|
@@ -17113,7 +17119,10 @@ var FECManager = class _FECManager {
|
|
|
17113
17119
|
const authoredLength = this.#blockAuthoredLengths.get(key);
|
|
17114
17120
|
const rsbLength = this.#blockRsbLengths.get(key);
|
|
17115
17121
|
if (authoredLength === void 0 || rsbLength === void 0) return void 0;
|
|
17116
|
-
|
|
17122
|
+
let present = this.#blockSourceCounts.get(key) ?? 0;
|
|
17123
|
+
const received = this.#blockSourceEsis.get(key);
|
|
17124
|
+
for (const esi of this.#blockRebuiltEsis.get(key) ?? []) if (!received?.has(esi)) present++;
|
|
17125
|
+
if (present + rsbLength >= authoredLength) return void 0;
|
|
17117
17126
|
const newestSource = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newestSource;
|
|
17118
17127
|
if (newestSource === void 0 || !serial32Precedes(serial32Add(sbn, authoredLength - 1), newestSource)) {
|
|
17119
17128
|
return void 0;
|
|
@@ -18433,6 +18442,9 @@ function authoredGap(before, after) {
|
|
|
18433
18442
|
function ssIdAfter(a, b) {
|
|
18434
18443
|
return (a - b | 0) > 0;
|
|
18435
18444
|
}
|
|
18445
|
+
function holdsRebuilt(stream) {
|
|
18446
|
+
return stream.buf.some(({ meta }) => meta.fecSbnFirst !== void 0);
|
|
18447
|
+
}
|
|
18436
18448
|
var FecReleaseSequencer = class {
|
|
18437
18449
|
#streams = /* @__PURE__ */ new Map();
|
|
18438
18450
|
#gate;
|
|
@@ -18495,6 +18507,7 @@ var FecReleaseSequencer = class {
|
|
|
18495
18507
|
stream.lastSbn = void 0;
|
|
18496
18508
|
stream.discontinuity = false;
|
|
18497
18509
|
stream.rapSsId = void 0;
|
|
18510
|
+
stream.sourced = false;
|
|
18498
18511
|
}
|
|
18499
18512
|
if (stream.last !== null && compareFrameOrder(frame, stream.last) <= 0) {
|
|
18500
18513
|
this.#recordStat("droppedStale");
|
|
@@ -18521,6 +18534,7 @@ var FecReleaseSequencer = class {
|
|
|
18521
18534
|
if (compareFrameOrder(stream.buf[mid], frame) < 0) lo = mid + 1;
|
|
18522
18535
|
else hi = mid;
|
|
18523
18536
|
}
|
|
18537
|
+
if (stream.buf[lo]?.passed === true) frame.passed = true;
|
|
18524
18538
|
stream.buf.splice(lo, 0, frame);
|
|
18525
18539
|
this.#drainUnlessDeferred(stream);
|
|
18526
18540
|
}
|
|
@@ -18556,6 +18570,41 @@ var FecReleaseSequencer = class {
|
|
|
18556
18570
|
}
|
|
18557
18571
|
return false;
|
|
18558
18572
|
}
|
|
18573
|
+
/**
|
|
18574
|
+
* These streams' sink is being replaced by one that has seen no source
|
|
18575
|
+
* frame yet. First, from a stream that holds a frame the overlay rebuilt,
|
|
18576
|
+
* release to the sink being replaced the frames the unanchored path had
|
|
18577
|
+
* already released to it (#cursorPassed): there they waited in its queue,
|
|
18578
|
+
* which its replacement flushes. A stream that holds none is not drained:
|
|
18579
|
+
* it holds nothing the anchor kept back, and a drain would still force out
|
|
18580
|
+
* a frame held past the hold limit, which before the anchor waited for the
|
|
18581
|
+
* next push. Then treat the streams as unsourced until a source frame is
|
|
18582
|
+
* released.
|
|
18583
|
+
*/
|
|
18584
|
+
unsource(packetId, catalogTrackName) {
|
|
18585
|
+
for (const stream of this.#streams.values()) {
|
|
18586
|
+
if (stream.packetId === packetId && (catalogTrackName === void 0 || stream.catalogTrackName === catalogTrackName)) {
|
|
18587
|
+
if (stream.draining === 0 && holdsRebuilt(stream)) {
|
|
18588
|
+
if (this.#deferDepth > 0) this.#deferredStreams.add(stream);
|
|
18589
|
+
else this.#drain(stream, true);
|
|
18590
|
+
}
|
|
18591
|
+
stream.sourced = false;
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
}
|
|
18595
|
+
/**
|
|
18596
|
+
* Whether a stream of `packetId` holds a frame the keyframe overlay rebuilt
|
|
18597
|
+
* (FrameMeta.fecSbnFirst). Without `catalogTrackName`, every stream of the
|
|
18598
|
+
* packetId.
|
|
18599
|
+
*/
|
|
18600
|
+
holdsRebuilt(packetId, catalogTrackName) {
|
|
18601
|
+
for (const stream of this.#streams.values()) {
|
|
18602
|
+
if (stream.packetId !== packetId) continue;
|
|
18603
|
+
if (catalogTrackName !== void 0 && stream.catalogTrackName !== catalogTrackName) continue;
|
|
18604
|
+
if (holdsRebuilt(stream)) return true;
|
|
18605
|
+
}
|
|
18606
|
+
return false;
|
|
18607
|
+
}
|
|
18559
18608
|
/** Re-evaluate held gaps after FEC state changed. */
|
|
18560
18609
|
poke(streamKey) {
|
|
18561
18610
|
if (streamKey !== void 0) {
|
|
@@ -18647,16 +18696,32 @@ var FecReleaseSequencer = class {
|
|
|
18647
18696
|
last: null,
|
|
18648
18697
|
lastSbn: void 0,
|
|
18649
18698
|
discontinuity: false,
|
|
18650
|
-
rapSsId: void 0
|
|
18699
|
+
rapSsId: void 0,
|
|
18700
|
+
sourced: false,
|
|
18701
|
+
draining: 0
|
|
18651
18702
|
};
|
|
18652
18703
|
this.#streams.set(streamKey, stream);
|
|
18653
18704
|
}
|
|
18654
18705
|
return stream;
|
|
18655
18706
|
}
|
|
18656
|
-
#
|
|
18707
|
+
/** `asUnanchored`: release overlay-rebuilt frames when unanchored did (#cursorPassed). */
|
|
18708
|
+
#drain(stream, asUnanchored = false) {
|
|
18709
|
+
stream.draining++;
|
|
18710
|
+
try {
|
|
18711
|
+
this.#drainLoop(stream, asUnanchored);
|
|
18712
|
+
} finally {
|
|
18713
|
+
stream.draining--;
|
|
18714
|
+
}
|
|
18715
|
+
}
|
|
18716
|
+
#drainLoop(stream, asUnanchored) {
|
|
18657
18717
|
while (stream.buf.length > 0 && stream.last !== null) {
|
|
18658
18718
|
const head = stream.buf[0];
|
|
18659
18719
|
const last = stream.last;
|
|
18720
|
+
if (compareFrameOrder(head, last) <= 0) {
|
|
18721
|
+
stream.buf.shift();
|
|
18722
|
+
this.#release(stream, head, void 0);
|
|
18723
|
+
continue;
|
|
18724
|
+
}
|
|
18660
18725
|
if (stream.discontinuity) {
|
|
18661
18726
|
stream.discontinuity = false;
|
|
18662
18727
|
stream.buf.shift();
|
|
@@ -18670,9 +18735,10 @@ var FecReleaseSequencer = class {
|
|
|
18670
18735
|
}
|
|
18671
18736
|
const headSbn = head.meta.fecSbn;
|
|
18672
18737
|
const from = stream.lastSbn ?? headSbn;
|
|
18673
|
-
const to = headSbn ?? stream.lastSbn;
|
|
18738
|
+
const to = head.meta.fecSbnFirst ?? headSbn ?? stream.lastSbn;
|
|
18674
18739
|
let lost = [];
|
|
18675
18740
|
const unanchored = from === void 0 && to === void 0;
|
|
18741
|
+
let unresolved = false;
|
|
18676
18742
|
if (from !== void 0 && to !== void 0) {
|
|
18677
18743
|
const resolved = this.#gate.resolvedLoss(
|
|
18678
18744
|
stream.packetId,
|
|
@@ -18681,7 +18747,8 @@ var FecReleaseSequencer = class {
|
|
|
18681
18747
|
Math.max(from, to),
|
|
18682
18748
|
from
|
|
18683
18749
|
);
|
|
18684
|
-
|
|
18750
|
+
unresolved = resolved === null;
|
|
18751
|
+
if (unresolved && !this.#cursorPassed(stream, asUnanchored)) {
|
|
18685
18752
|
const heldMs = performance.now() - head.arrivedAtMs;
|
|
18686
18753
|
if (heldMs <= this.#gate.holdLimitMs(stream.packetId, stream.catalogTrackName)) return;
|
|
18687
18754
|
this.#recordStat("forcedWindowFlushes");
|
|
@@ -18689,22 +18756,56 @@ var FecReleaseSequencer = class {
|
|
|
18689
18756
|
this.#release(stream, head, this.#gate.decide(last, head, [], true));
|
|
18690
18757
|
continue;
|
|
18691
18758
|
}
|
|
18692
|
-
lost = resolved;
|
|
18759
|
+
lost = resolved ?? [];
|
|
18693
18760
|
}
|
|
18694
18761
|
stream.buf.shift();
|
|
18695
18762
|
this.#release(
|
|
18696
18763
|
stream,
|
|
18697
18764
|
head,
|
|
18698
|
-
lost.length > 0 || unanchored || authoredGap(last.meta, head.meta) ? this.#gate.decide(last, head, lost, false) : void 0
|
|
18765
|
+
lost.length > 0 || unanchored || unresolved || authoredGap(last.meta, head.meta) ? this.#gate.decide(last, head, lost, false) : void 0
|
|
18699
18766
|
);
|
|
18700
18767
|
}
|
|
18701
18768
|
}
|
|
18769
|
+
/**
|
|
18770
|
+
* An overlay-rebuilt frame (fecSbnFirst) waits no longer than the BLO-53342
|
|
18771
|
+
* source cursor holds it unanchored, and neither does a later frame behind
|
|
18772
|
+
* it. Unanchored, a frame that waits on no block (fecSbnFirst set, or no
|
|
18773
|
+
* fecSbn) left the gate once every frame ahead of it had: mark it `passed`,
|
|
18774
|
+
* like each frame that arrived in front of it since (stale there). The head
|
|
18775
|
+
* then goes, decided as it would be unanchored, once a frame the base path
|
|
18776
|
+
* delivered (a source frame, or one base FEC recovered) is buffered after a
|
|
18777
|
+
* marked one: the base path has passed that one, so a frame still missing
|
|
18778
|
+
* before it can only come from a repair. It also goes while no source frame
|
|
18779
|
+
* has been released, when the direct path shows a recovered frame at once.
|
|
18780
|
+
* And it goes at once while a staged switch decides on when frames arrive
|
|
18781
|
+
* (FecReleaseGate.releaseUnanchored), and when unsource releases into a
|
|
18782
|
+
* sink being replaced: unanchored, it had already reached the sink.
|
|
18783
|
+
* The scan also writes: the `passed` marks it sets stay on the frames,
|
|
18784
|
+
* and push marks a frame it inserts in front of a marked one from them.
|
|
18785
|
+
*/
|
|
18786
|
+
#cursorPassed(stream, asUnanchored) {
|
|
18787
|
+
let at2 = -1;
|
|
18788
|
+
for (let i = 0; i < stream.buf.length; i++) {
|
|
18789
|
+
const frame = stream.buf[i];
|
|
18790
|
+
if (frame.meta.fecSbnFirst !== void 0 || frame.meta.fecSbn === void 0) {
|
|
18791
|
+
frame.passed = true;
|
|
18792
|
+
if (at2 < 0) at2 = i;
|
|
18793
|
+
} else if (frame.passed !== true) break;
|
|
18794
|
+
}
|
|
18795
|
+
if (at2 < 0) return false;
|
|
18796
|
+
return asUnanchored || !stream.sourced || this.#gate.releaseUnanchored?.(stream.packetId, stream.catalogTrackName) === true || stream.buf.some(({ meta }, i) => i > at2 && meta.fecSbnFirst === void 0 && (meta.recovered !== true || meta.fecSbn !== void 0));
|
|
18797
|
+
}
|
|
18702
18798
|
#release(stream, frame, decision) {
|
|
18799
|
+
const stale = stream.last !== null && compareFrameOrder(frame, stream.last) <= 0;
|
|
18800
|
+
if (stale) this.#recordStat("droppedStale");
|
|
18703
18801
|
if (decision) frame.meta.gapBefore = decision;
|
|
18704
18802
|
stream.last = frame;
|
|
18705
|
-
if (frame.meta.fecSbn !== void 0
|
|
18803
|
+
if (frame.meta.fecSbn !== void 0 && (frame.meta.fecSbnFirst === void 0 || stream.lastSbn !== void 0)) {
|
|
18804
|
+
stream.lastSbn = frame.meta.fecSbn;
|
|
18805
|
+
}
|
|
18806
|
+
if (frame.meta.recovered !== true) stream.sourced = true;
|
|
18706
18807
|
this.#gate.onReleased?.(frame.data, frame.meta);
|
|
18707
|
-
this.#emit(frame.data, frame.meta);
|
|
18808
|
+
if (!stale) this.#emit(frame.data, frame.meta);
|
|
18708
18809
|
}
|
|
18709
18810
|
};
|
|
18710
18811
|
var FMPU_BOX_TYPES = /* @__PURE__ */ new Set(["ftyp", "moov", "moof"]);
|
|
@@ -18965,11 +19066,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
18965
19066
|
/**
|
|
18966
19067
|
* Typed view of the ISO-BMFF timing-sample budget.
|
|
18967
19068
|
*
|
|
18968
|
-
*
|
|
18969
|
-
*
|
|
18970
|
-
*
|
|
18971
|
-
*
|
|
18972
|
-
*
|
|
19069
|
+
* Each stream admits timing against its own window of `capacity` slots
|
|
19070
|
+
* (BLO-52085). A stream at capacity displaces its own oldest timing to
|
|
19071
|
+
* admit the moof in hand, and `evicted` counts the slots so displaced:
|
|
19072
|
+
* timing a stream declared and never delivered shows up there. `pending`
|
|
19073
|
+
* sums every stream, so it can exceed `capacity`. Per repo policy this is
|
|
19074
|
+
* a producer-owned counter -- never infer budget state by parsing logs.
|
|
18973
19075
|
*/
|
|
18974
19076
|
get timingBudgetStats() {
|
|
18975
19077
|
return {
|
|
@@ -19029,6 +19131,14 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19029
19131
|
fecReleaseRapAbove(packetId, catalogTrackName, ssId) {
|
|
19030
19132
|
return this.#fecSequencer?.rapAbove(packetId, catalogTrackName, ssId) ?? false;
|
|
19031
19133
|
}
|
|
19134
|
+
/** See FecReleaseSequencer.unsource. */
|
|
19135
|
+
unsourceFecRelease(packetId, catalogTrackName) {
|
|
19136
|
+
this.#fecSequencer?.unsource(packetId, catalogTrackName);
|
|
19137
|
+
}
|
|
19138
|
+
/** See FecReleaseSequencer.holdsRebuilt. */
|
|
19139
|
+
fecReleaseHoldsRebuilt(packetId, catalogTrackName) {
|
|
19140
|
+
return this.#fecSequencer?.holdsRebuilt(packetId, catalogTrackName) ?? false;
|
|
19141
|
+
}
|
|
19032
19142
|
/** Re-evaluate held gaps after FEC block state changed. */
|
|
19033
19143
|
pokeFecRelease(packetId, catalogTrackName) {
|
|
19034
19144
|
if (!this.#fecSequencer) return;
|
|
@@ -19282,7 +19392,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19282
19392
|
if (!resolver2?.initialized) return this.#drop("fragment-no-resolver");
|
|
19283
19393
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
19284
19394
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
19285
|
-
const remaining = this.#maxPendingTimingSamples -
|
|
19395
|
+
const remaining = this.#maxPendingTimingSamples - resolver2.pendingSampleCount;
|
|
19286
19396
|
const admitted = resolver2.addMovieFragment(
|
|
19287
19397
|
mpu.mpuSequenceNumber,
|
|
19288
19398
|
data,
|
|
@@ -19355,7 +19465,8 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19355
19465
|
info,
|
|
19356
19466
|
resolver,
|
|
19357
19467
|
options?.recovered === true,
|
|
19358
|
-
options?.fecSbn
|
|
19468
|
+
options?.fecSbn,
|
|
19469
|
+
options?.fecSbnFirst
|
|
19359
19470
|
);
|
|
19360
19471
|
if (deferred.record === void 0) this.#countDeferredOutcome(deferred.reason);
|
|
19361
19472
|
}
|
|
@@ -19448,7 +19559,8 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19448
19559
|
info,
|
|
19449
19560
|
resolver,
|
|
19450
19561
|
options?.recovered === true,
|
|
19451
|
-
options?.fecSbn
|
|
19562
|
+
options?.fecSbn,
|
|
19563
|
+
options?.fecSbnFirst
|
|
19452
19564
|
);
|
|
19453
19565
|
if (outcome.record !== void 0) return outcome.record;
|
|
19454
19566
|
return this.#drop(outcome.reason);
|
|
@@ -19468,7 +19580,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19468
19580
|
* attribution exists to keep readable. Leaving the choice at the call site
|
|
19469
19581
|
* is what keeps both true.
|
|
19470
19582
|
*/
|
|
19471
|
-
#emitCombinedSample(combined, info, resolver, recovered, fecSbn) {
|
|
19583
|
+
#emitCombinedSample(combined, info, resolver, recovered, fecSbn, fecSbnFirst) {
|
|
19472
19584
|
const timing = resolver.sampleTiming(
|
|
19473
19585
|
combined.mpuSequenceNumber,
|
|
19474
19586
|
combined.movieFragmentSequenceNumber,
|
|
@@ -19485,6 +19597,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19485
19597
|
);
|
|
19486
19598
|
if (!meta) return { reason: "combined-meta-build-failed" };
|
|
19487
19599
|
if (fecSbn !== void 0) meta.fecSbn = fecSbn;
|
|
19600
|
+
if (fecSbnFirst !== void 0) meta.fecSbnFirst = fecSbnFirst;
|
|
19488
19601
|
if (!resolver.consumeSample(
|
|
19489
19602
|
combined.mpuSequenceNumber,
|
|
19490
19603
|
combined.movieFragmentSequenceNumber,
|
|
@@ -22994,9 +23107,14 @@ function overlayBaseSsId(anchor, overlaySsId, restoredPsn, headerPsn) {
|
|
|
22994
23107
|
if (anchor.baseSsId === void 0 || headerPsn !== restoredPsn) return void 0;
|
|
22995
23108
|
return serial32Add(anchor.baseSsId, overlaySsId - anchor.overlaySsId | 0);
|
|
22996
23109
|
}
|
|
23110
|
+
function unstampedBaseSsId(offset, unstampedOffset, headerPsn) {
|
|
23111
|
+
if (offset === void 0 || offset !== unstampedOffset) return void 0;
|
|
23112
|
+
return headerPsn + offset >>> 0;
|
|
23113
|
+
}
|
|
22997
23114
|
var OVERLAY_ROUTED_LIMIT = 1024;
|
|
22998
23115
|
var OVERLAY_PENDING_LIMIT = 256;
|
|
22999
23116
|
var OVERLAY_OFFSET_MPUS = 16;
|
|
23117
|
+
var OVERLAY_REBUILT_LIMIT = 256;
|
|
23000
23118
|
var OVERLAY_MOQ_HORIZON_BLOCKS = 64;
|
|
23001
23119
|
function assertStreamIdentity(role, stream) {
|
|
23002
23120
|
if (stream === void 0 || !Number.isInteger(stream.packetId) || stream.packetId <= MMTP_SIGNALING_PACKET_ID || typeof stream.catalogTrackName !== "string" || stream.catalogTrackName.length === 0) {
|
|
@@ -23107,6 +23225,7 @@ var MmtpRouter = class {
|
|
|
23107
23225
|
#provenance = /* @__PURE__ */ new Map();
|
|
23108
23226
|
#onFlowProtected = null;
|
|
23109
23227
|
#unadmittedListedDrops = 0;
|
|
23228
|
+
#admittedSourceDatagrams = 0;
|
|
23110
23229
|
#unlistedRepairSetAside = 0;
|
|
23111
23230
|
#assistBlocks = /* @__PURE__ */ new Map();
|
|
23112
23231
|
#assistFailures = /* @__PURE__ */ new Map();
|
|
@@ -23163,6 +23282,26 @@ var MmtpRouter = class {
|
|
|
23163
23282
|
* sequence is its SS_ID minus this; see #routeProcessedFecSymbols.
|
|
23164
23283
|
*/
|
|
23165
23284
|
#ssIdSeqOffsetByStream = /* @__PURE__ */ new Map();
|
|
23285
|
+
/**
|
|
23286
|
+
* Per stream, the SS_ID offset at which a base recovery last showed the
|
|
23287
|
+
* sender's own packet_sequence_number: a recovered packet's header psn
|
|
23288
|
+
* equal to its SS_ID minus that offset. While it equals the stream's
|
|
23289
|
+
* current offset, no hop re-stamps the stream, and a packet's protected psn
|
|
23290
|
+
* names its base SS_ID within unstampedBaseSsId's limits (joint decoding of
|
|
23291
|
+
* an MPU no member of which arrived).
|
|
23292
|
+
*/
|
|
23293
|
+
#unstampedSsIdSeqOffsetByStream = /* @__PURE__ */ new Map();
|
|
23294
|
+
/**
|
|
23295
|
+
* The same, shown by the overlay alone, before any base recovery did: a
|
|
23296
|
+
* packet it rebuilt whose base copy carries its protected psn
|
|
23297
|
+
* (#feedOverlaySource), or whose protected psn passes overlayBaseSsId's
|
|
23298
|
+
* test at that offset (#routeOverlayRecovered). A base SS_ID placed by it
|
|
23299
|
+
* anchors the release gate only (BLO-52085 D3): fed to the base, it would
|
|
23300
|
+
* make the base drop a late base copy of a keyframe the overlay never
|
|
23301
|
+
* finished, and lift a block's late-source cut, which nothing before
|
|
23302
|
+
* this offset did.
|
|
23303
|
+
*/
|
|
23304
|
+
#overlayUnstampedSsIdSeqOffsetByStream = /* @__PURE__ */ new Map();
|
|
23166
23305
|
/**
|
|
23167
23306
|
* Streams whose multicast leg delivered a RAP-flagged source packet that was
|
|
23168
23307
|
* not parsed, typically because its overlap copy was parsed first
|
|
@@ -23491,8 +23630,9 @@ var MmtpRouter = class {
|
|
|
23491
23630
|
}
|
|
23492
23631
|
/**
|
|
23493
23632
|
* FEC-recovered source symbols whose reconstructed header named a
|
|
23494
|
-
* packet_sequence_number other than the wire sequence their SS_ID maps to
|
|
23495
|
-
*
|
|
23633
|
+
* packet_sequence_number other than the wire sequence their SS_ID maps to,
|
|
23634
|
+
* or, rebuilt by the overlay, than their base copy carries: a hop
|
|
23635
|
+
* re-stamped the sequence after the sender protected the packet.
|
|
23496
23636
|
*/
|
|
23497
23637
|
get restampedRecoveredSequenceCount() {
|
|
23498
23638
|
return this.#restampedRecoveredSeqs;
|
|
@@ -23671,6 +23811,7 @@ var MmtpRouter = class {
|
|
|
23671
23811
|
enableMseAssembler(packetId, config, catalogTrackName) {
|
|
23672
23812
|
const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);
|
|
23673
23813
|
const previous = this.#mseAssemblers.get(assemblerKey);
|
|
23814
|
+
this.#parser.unsourceFecRelease(packetId, catalogTrackName);
|
|
23674
23815
|
if (previous) {
|
|
23675
23816
|
previous.flush();
|
|
23676
23817
|
previous.dispose();
|
|
@@ -23821,7 +23962,8 @@ var MmtpRouter = class {
|
|
|
23821
23962
|
fec,
|
|
23822
23963
|
k,
|
|
23823
23964
|
offsets: /* @__PURE__ */ new Map(),
|
|
23824
|
-
moqBlocks: /* @__PURE__ */ new Set()
|
|
23965
|
+
moqBlocks: /* @__PURE__ */ new Set(),
|
|
23966
|
+
rebuilt: /* @__PURE__ */ new Map()
|
|
23825
23967
|
};
|
|
23826
23968
|
this.#overlays.set(repairPacketId, inst);
|
|
23827
23969
|
return () => {
|
|
@@ -23862,6 +24004,7 @@ var MmtpRouter = class {
|
|
|
23862
24004
|
inst.last = void 0;
|
|
23863
24005
|
inst.offsets.clear();
|
|
23864
24006
|
inst.moqBlocks.clear();
|
|
24007
|
+
inst.rebuilt.clear();
|
|
23865
24008
|
}
|
|
23866
24009
|
#overlaysForSource(sourcePacketId, catalogTrackName) {
|
|
23867
24010
|
return [...this.#overlays.values()].filter(({ leg }) => leg.sourcePacketId === sourcePacketId && (catalogTrackName === void 0 || leg.sourceCatalogTrackName === void 0 || leg.sourceCatalogTrackName === catalogTrackName));
|
|
@@ -23914,13 +24057,23 @@ var MmtpRouter = class {
|
|
|
23914
24057
|
* base protects: the packet minus its 4-byte SS_ID trailer, extension
|
|
23915
24058
|
* included (7.1, 7.4.3). `baseSsId` is that trailer's SS_ID, when the base
|
|
23916
24059
|
* FEC reads it.
|
|
24060
|
+
*
|
|
24061
|
+
* The base copy of a packet an overlay rebuilt with no base SS_ID shows
|
|
24062
|
+
* what a base recovery of it would (#routeProcessedFecSymbols, BLO-52085):
|
|
24063
|
+
* its wire packet_sequence_number equal to the protected one rebuilt shows
|
|
24064
|
+
* the stream unstamped at the SS_ID offset it states, for the release gate
|
|
24065
|
+
* (#overlayUnstampedSsIdSeqOffsetByStream); any other shows a hop that
|
|
24066
|
+
* re-stamps it, which is counted (restampedRecoveredSequenceCount).
|
|
23917
24067
|
*/
|
|
23918
24068
|
#feedOverlaySource(packetId, catalogTrackName, packet, header, sequenceNumber, rapFlag, source, baseSsId) {
|
|
23919
24069
|
if (this.#overlays.size === 0) return;
|
|
23920
24070
|
const ssId = keyframeOverlaySsId(header);
|
|
23921
24071
|
if (ssId === void 0) return;
|
|
24072
|
+
let rebuiltPsn;
|
|
23922
24073
|
for (const inst of this.#overlaysForSource(packetId, catalogTrackName)) {
|
|
23923
24074
|
this.#noteOverlaySsId(inst, ssId, sequenceNumber);
|
|
24075
|
+
rebuiltPsn ??= inst.rebuilt.get(ssId);
|
|
24076
|
+
inst.rebuilt.delete(ssId);
|
|
23924
24077
|
const identity = MmtpContainerParser.sourcePacketIdentity(packet, header);
|
|
23925
24078
|
if (identity !== null) {
|
|
23926
24079
|
inst.offsets.delete(identity.mpuSequenceNumber);
|
|
@@ -23950,6 +24103,14 @@ var MmtpRouter = class {
|
|
|
23950
24103
|
const recovered = fec.feedSourceSymbol(pid, ssId, symbolData, name);
|
|
23951
24104
|
if (recovered && !alreadyCompleted) this.#routeOverlayRecovered(inst, coords.sbn, recovered);
|
|
23952
24105
|
}
|
|
24106
|
+
if (rebuiltPsn === void 0 || this.#classifyPacketId(packetId) === null) return;
|
|
24107
|
+
const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);
|
|
24108
|
+
if (rebuiltPsn !== sequenceNumber) {
|
|
24109
|
+
this.#restampedRecoveredSeqs++;
|
|
24110
|
+
} else if (baseSsId !== void 0) {
|
|
24111
|
+
const offset = this.#ssIdSeqOffsetByStream.get(streamKey);
|
|
24112
|
+
if (offset === baseSsId - sequenceNumber >>> 0) this.#overlayUnstampedSsIdSeqOffsetByStream.set(streamKey, offset);
|
|
24113
|
+
}
|
|
23953
24114
|
}
|
|
23954
24115
|
#noteOverlayMoqBlock(inst, blockStart) {
|
|
23955
24116
|
inst.moqBlocks.add(blockStart);
|
|
@@ -23983,11 +24144,16 @@ var MmtpRouter = class {
|
|
|
23983
24144
|
}
|
|
23984
24145
|
/**
|
|
23985
24146
|
* Route an overlay-recovered block's packets as source media of the
|
|
23986
|
-
* overlay's source (7.2: an overlay coordinate yields no base SS_ID).
|
|
23987
|
-
*
|
|
23988
|
-
*
|
|
23989
|
-
*
|
|
23990
|
-
*
|
|
24147
|
+
* overlay's source (7.2: an overlay coordinate yields no base SS_ID). A
|
|
24148
|
+
* packet whose base SS_ID is determined independently (overlayBaseSsId,
|
|
24149
|
+
* unstampedBaseSsId) enters the release gate at that SS_ID, as a packet
|
|
24150
|
+
* base FEC recovered does, so it waits behind base frames still in flight,
|
|
24151
|
+
* though not for its own MPU's packets (FrameMeta.fecSbnFirst) and no
|
|
24152
|
+
* longer than unanchored; one whose base SS_ID is undetermined has no
|
|
24153
|
+
* release-gate anchor. No parse ledger, no base re-stamp, no block
|
|
24154
|
+
* advance. Each packet's wire sequence is restored from its keyframe's own
|
|
24155
|
+
* offset; without one, a stream the base never re-stamped keeps the
|
|
24156
|
+
* header's sequence and any other drops the packet.
|
|
23991
24157
|
*/
|
|
23992
24158
|
#routeOverlayRecovered(inst, blockStart, recovered) {
|
|
23993
24159
|
const { fec, repairPacketId: pid, streamName: name } = inst;
|
|
@@ -24008,31 +24174,65 @@ var MmtpRouter = class {
|
|
|
24008
24174
|
const restamped = this.#restampedStreams.has(streamKey);
|
|
24009
24175
|
const restored = /* @__PURE__ */ new Set();
|
|
24010
24176
|
const baseSsIds = /* @__PURE__ */ new Map();
|
|
24177
|
+
const gateOnly = /* @__PURE__ */ new Set();
|
|
24011
24178
|
const symbols = result.symbols.flatMap((sym) => {
|
|
24179
|
+
const overlaySsId = serial32Add(blockStart, sym.index);
|
|
24012
24180
|
const anchor = inst.offsets.get(sym.fragment.mpuSequenceNumber);
|
|
24013
24181
|
if (anchor !== void 0) {
|
|
24014
|
-
const overlaySsId = serial32Add(blockStart, sym.index);
|
|
24015
24182
|
const packetSequenceNumber = overlaySsId - anchor.psnOffset >>> 0;
|
|
24016
24183
|
restored.add(packetSequenceNumber);
|
|
24017
24184
|
const out = { ...sym, packetSequenceNumber };
|
|
24018
24185
|
const baseSsId = overlayBaseSsId(anchor, overlaySsId, packetSequenceNumber, sym.packetSequenceNumber);
|
|
24019
|
-
if (baseSsId !== void 0)
|
|
24186
|
+
if (baseSsId !== void 0) {
|
|
24187
|
+
baseSsIds.set(sym.index, baseSsId);
|
|
24188
|
+
const offset = this.#ssIdSeqOffsetByStream.get(streamKey);
|
|
24189
|
+
if (!this.#sourceSeqReceived(sourcePacketId, packetSequenceNumber, sourceName) && offset === baseSsId - packetSequenceNumber >>> 0) {
|
|
24190
|
+
this.#overlayUnstampedSsIdSeqOffsetByStream.set(streamKey, offset);
|
|
24191
|
+
}
|
|
24192
|
+
}
|
|
24020
24193
|
return [out];
|
|
24021
24194
|
}
|
|
24022
|
-
if (!restamped)
|
|
24195
|
+
if (!restamped) {
|
|
24196
|
+
const offset = this.#ssIdSeqOffsetByStream.get(streamKey);
|
|
24197
|
+
const fedSsId = unstampedBaseSsId(offset, this.#unstampedSsIdSeqOffsetByStream.get(streamKey), sym.packetSequenceNumber);
|
|
24198
|
+
const baseSsId = fedSsId ?? unstampedBaseSsId(offset, this.#overlayUnstampedSsIdSeqOffsetByStream.get(streamKey), sym.packetSequenceNumber);
|
|
24199
|
+
if (baseSsId !== void 0) {
|
|
24200
|
+
baseSsIds.set(sym.index, baseSsId);
|
|
24201
|
+
if (fedSsId === void 0) gateOnly.add(sym.index);
|
|
24202
|
+
} else {
|
|
24203
|
+
inst.rebuilt.set(overlaySsId, sym.packetSequenceNumber);
|
|
24204
|
+
if (inst.rebuilt.size > OVERLAY_REBUILT_LIMIT) inst.rebuilt.delete(inst.rebuilt.keys().next().value);
|
|
24205
|
+
}
|
|
24206
|
+
return [sym];
|
|
24207
|
+
}
|
|
24023
24208
|
this.#overlayStats.unsequencedDropped++;
|
|
24024
24209
|
return [];
|
|
24025
24210
|
});
|
|
24026
24211
|
this.#overlayStats.recovered++;
|
|
24212
|
+
const mpuFirst = /* @__PURE__ */ new Map();
|
|
24213
|
+
for (const sym of symbols) {
|
|
24214
|
+
const ssId = baseSsIds.get(sym.index);
|
|
24215
|
+
const mpu = sym.fragment.mpuSequenceNumber;
|
|
24216
|
+
const first = mpuFirst.get(mpu);
|
|
24217
|
+
if (ssId !== void 0 && (first === void 0 || serial32Precedes(ssId, first))) mpuFirst.set(mpu, ssId);
|
|
24218
|
+
}
|
|
24219
|
+
const firstSsIds = /* @__PURE__ */ new Map();
|
|
24220
|
+
for (const sym of symbols) {
|
|
24221
|
+
if (baseSsIds.has(sym.index)) firstSsIds.set(sym.index, mpuFirst.get(sym.fragment.mpuSequenceNumber));
|
|
24222
|
+
}
|
|
24027
24223
|
this.#routeProcessedFecSymbols(blockStart, { ...result, symbols }, sourcePacketId, sourceName, {
|
|
24028
|
-
deliveryProvenance: inst.moqBlocks.has(blockStart) ? "mixed" : "multicast"
|
|
24224
|
+
deliveryProvenance: inst.moqBlocks.has(blockStart) ? "mixed" : "multicast",
|
|
24225
|
+
baseSsIds,
|
|
24226
|
+
firstSsIds
|
|
24029
24227
|
});
|
|
24030
24228
|
this.#noteOverlayRecoveredRaps(sourcePacketId, sourceName, streamKey, symbols.filter((sym) => restored.has(sym.packetSequenceNumber)));
|
|
24031
|
-
this.#feedOverlayRecoveredToBase(inst, blockStart, symbols, baseSsIds);
|
|
24229
|
+
this.#feedOverlayRecoveredToBase(inst, blockStart, symbols, gateOnly.size === 0 ? baseSsIds : new Map([...baseSsIds].filter(([index]) => !gateOnly.has(index))));
|
|
24032
24230
|
}
|
|
24033
24231
|
/**
|
|
24034
24232
|
* Joint decoding (BLO-52085). A packet the overlay rebuilt is a source of
|
|
24035
|
-
* its base block too, once its base SS_ID is determined (overlayBaseSsId
|
|
24233
|
+
* its base block too, once its base SS_ID is determined (overlayBaseSsId,
|
|
24234
|
+
* from a received member of its MPU, else unstampedBaseSsId at an offset a
|
|
24235
|
+
* base recovery showed; `baseSsIds` holds no other),
|
|
24036
24236
|
* so it is fed to the base decoder as a rebuilt source
|
|
24037
24237
|
* (FECManager.feedRebuiltSourceSymbol): the base block has one erasure
|
|
24038
24238
|
* fewer to solve, and its repair can recover the block's other packets.
|
|
@@ -24058,7 +24258,7 @@ var MmtpRouter = class {
|
|
|
24058
24258
|
const feeds = [];
|
|
24059
24259
|
for (const sym of symbols) {
|
|
24060
24260
|
if (duplicates.sourceSeqs.has(sym.packetSequenceNumber)) continue;
|
|
24061
|
-
const ssId = baseSsIds.get(sym);
|
|
24261
|
+
const ssId = baseSsIds.get(sym.index);
|
|
24062
24262
|
if (ssId === void 0) {
|
|
24063
24263
|
this.#overlayStats.baseUndetermined++;
|
|
24064
24264
|
continue;
|
|
@@ -24068,7 +24268,10 @@ var MmtpRouter = class {
|
|
|
24068
24268
|
if (routed.size > OVERLAY_ROUTED_LIMIT) routed.delete(routed.values().next().value);
|
|
24069
24269
|
feeds.push([ssId, sym.rawSymbol]);
|
|
24070
24270
|
}
|
|
24071
|
-
for (const [ssId, symbolData] of feeds)
|
|
24271
|
+
for (const [ssId, symbolData] of feeds) {
|
|
24272
|
+
if (this.#classifyPacketId(owner) === null) return;
|
|
24273
|
+
this.#feedRebuiltToBase(owner, name, ssId, symbolData);
|
|
24274
|
+
}
|
|
24072
24275
|
}
|
|
24073
24276
|
/** Feed one rebuilt source to its base block, or keep it until a repair states that block. */
|
|
24074
24277
|
#feedRebuiltToBase(owner, name, ssId, symbolData) {
|
|
@@ -24078,7 +24281,7 @@ var MmtpRouter = class {
|
|
|
24078
24281
|
if (coords == null) {
|
|
24079
24282
|
const pending = this.#stream(owner, name).overlayToBase.pending;
|
|
24080
24283
|
pending.delete(ssId);
|
|
24081
|
-
pending.set(ssId, symbolData);
|
|
24284
|
+
pending.set(ssId, symbolData.slice());
|
|
24082
24285
|
if (pending.size > OVERLAY_PENDING_LIMIT) pending.delete(pending.keys().next().value);
|
|
24083
24286
|
return;
|
|
24084
24287
|
}
|
|
@@ -24098,6 +24301,7 @@ var MmtpRouter = class {
|
|
|
24098
24301
|
const pending = this.#streams.get(owner)?.get(name)?.overlayToBase.pending;
|
|
24099
24302
|
if (pending === void 0 || pending.size === 0) return;
|
|
24100
24303
|
for (const [ssId, symbolData] of [...pending]) {
|
|
24304
|
+
if (this.#classifyPacketId(owner) === null) return;
|
|
24101
24305
|
if (serial32Distance(block.start, ssId) >= block.length) continue;
|
|
24102
24306
|
pending.delete(ssId);
|
|
24103
24307
|
this.#feedRebuiltToBase(owner, name, ssId, symbolData);
|
|
@@ -24241,6 +24445,10 @@ var MmtpRouter = class {
|
|
|
24241
24445
|
get unadmittedListedDropCount() {
|
|
24242
24446
|
return this.#unadmittedListedDrops;
|
|
24243
24447
|
}
|
|
24448
|
+
/** Source datagrams (multicast or MoQ) a protected flow's bc-provenance v2 receiver released: admitted at once or held, then admitted by a record. Repair symbols and user-agent-admitted datagrams are not counted. Counted at release, before this router's own track checks: a released datagram those drop, which only a track change between hold and release can cause, still counts, so this is not a count of routed media. */
|
|
24449
|
+
get admittedSourceDatagramCount() {
|
|
24450
|
+
return this.#admittedSourceDatagrams;
|
|
24451
|
+
}
|
|
24244
24452
|
/** MoQ repair symbols of an unlisted track kept out of a governed FEC instance (7.2.7 step 2). */
|
|
24245
24453
|
get unlistedRepairSetAsideCount() {
|
|
24246
24454
|
return this.#unlistedRepairSetAside;
|
|
@@ -24717,8 +24925,6 @@ var MmtpRouter = class {
|
|
|
24717
24925
|
}
|
|
24718
24926
|
this.#replayIsoSources(ownerPacketId, catalogTrackName, blockStart, placement.placed);
|
|
24719
24927
|
if (this.#classifyPacketId(ownerPacketId) === null) return;
|
|
24720
|
-
this.#feedPendingRebuilt(ownerPacketId, catalogTrackName, placement.block);
|
|
24721
|
-
if (this.#classifyPacketId(ownerPacketId) === null) return;
|
|
24722
24928
|
if (!this.#feedFecTransportSymbol(
|
|
24723
24929
|
ownerPacketId,
|
|
24724
24930
|
blockStart,
|
|
@@ -24729,6 +24935,8 @@ var MmtpRouter = class {
|
|
|
24729
24935
|
)) return;
|
|
24730
24936
|
fec.noteBlockActivity?.(blockStart, ownerPacketId, catalogTrackName);
|
|
24731
24937
|
fec.noteRepairGroupLength?.(blockStart, ownerPacketId, repair.rsbLength, catalogTrackName);
|
|
24938
|
+
this.#feedPendingRebuilt(ownerPacketId, catalogTrackName, placement.block);
|
|
24939
|
+
if (this.#classifyPacketId(ownerPacketId) === null) return;
|
|
24732
24940
|
const alreadyCompleted = fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
24733
24941
|
const recovered = fec.feedRepairSymbol(
|
|
24734
24942
|
ownerPacketId,
|
|
@@ -24866,6 +25074,8 @@ var MmtpRouter = class {
|
|
|
24866
25074
|
this.#droppedPreSyncVideo.delete(streamKey);
|
|
24867
25075
|
this.#latestVideoSeqByStream.delete(streamKey);
|
|
24868
25076
|
this.#ssIdSeqOffsetByStream.delete(streamKey);
|
|
25077
|
+
this.#unstampedSsIdSeqOffsetByStream.delete(streamKey);
|
|
25078
|
+
this.#overlayUnstampedSsIdSeqOffsetByStream.delete(streamKey);
|
|
24869
25079
|
this.#multicastRapTwinByStream.delete(streamKey);
|
|
24870
25080
|
this.#lastDirectSourceVideoTsByStream.delete(streamKey);
|
|
24871
25081
|
this.#lastDirectEmittedVideoTsByStream.delete(streamKey);
|
|
@@ -24992,6 +25202,12 @@ var MmtpRouter = class {
|
|
|
24992
25202
|
* the outgoing stream stops. The hold is bounded by the incoming stream's
|
|
24993
25203
|
* own RAP cadence, so no timer is involved.
|
|
24994
25204
|
*
|
|
25205
|
+
* While the switch is staged, the FEC release gate lets a keyframe the
|
|
25206
|
+
* overlay rebuilt reach it, on either side, when the keyframe did before
|
|
25207
|
+
* the gate anchored it (FecReleaseGate.releaseUnanchored): the switch
|
|
25208
|
+
* decides on what each side delivered, and the commit discards the gate's
|
|
25209
|
+
* frames of the outgoing stream.
|
|
25210
|
+
*
|
|
24995
25211
|
* The commit is one synchronous transition inside the frame path: the
|
|
24996
25212
|
* outgoing stream's already-admitted frames are released (its queued FEC
|
|
24997
25213
|
* gap fills only up to the RAP, since recovery can queue a frame ahead of
|
|
@@ -25042,6 +25258,9 @@ var MmtpRouter = class {
|
|
|
25042
25258
|
droppedIncomingFrames: 0
|
|
25043
25259
|
};
|
|
25044
25260
|
this.#staged = state;
|
|
25261
|
+
if (this.#parser.fecReleaseHoldsRebuilt(from.packetId, from.catalogTrackName)) {
|
|
25262
|
+
this.#requestFecPoke(from.packetId, from.catalogTrackName);
|
|
25263
|
+
}
|
|
25045
25264
|
return {
|
|
25046
25265
|
cancel: () => {
|
|
25047
25266
|
if (this.#staged !== state) return false;
|
|
@@ -25124,7 +25343,17 @@ var MmtpRouter = class {
|
|
|
25124
25343
|
*/
|
|
25125
25344
|
#routeParsedFrame(data, meta) {
|
|
25126
25345
|
if (this.#classifyPacketId(meta.packetId) === null) return;
|
|
25127
|
-
if (this.#staged !== null
|
|
25346
|
+
if (this.#staged !== null) {
|
|
25347
|
+
let consumed = false;
|
|
25348
|
+
this.#fecPokeDepth++;
|
|
25349
|
+
try {
|
|
25350
|
+
consumed = this.#interceptSwitchFrame(data, meta);
|
|
25351
|
+
} finally {
|
|
25352
|
+
this.#fecPokeDepth--;
|
|
25353
|
+
this.#flushPendingFecPokes();
|
|
25354
|
+
}
|
|
25355
|
+
if (consumed) return;
|
|
25356
|
+
}
|
|
25128
25357
|
this.#deliverParsedFrame(data, meta);
|
|
25129
25358
|
}
|
|
25130
25359
|
#commitStreamSwitch(state, rap, reason) {
|
|
@@ -25276,12 +25505,16 @@ var MmtpRouter = class {
|
|
|
25276
25505
|
this.#parser.deferFecRelease(work);
|
|
25277
25506
|
} finally {
|
|
25278
25507
|
this.#fecPokeDepth--;
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25508
|
+
this.#flushPendingFecPokes();
|
|
25509
|
+
}
|
|
25510
|
+
}
|
|
25511
|
+
/** Run the pokes requested while #fecPokeDepth was raised, once it is 0. */
|
|
25512
|
+
#flushPendingFecPokes() {
|
|
25513
|
+
if (this.#fecPokeDepth === 0 && this.#pendingFecPokes.size > 0 && !this.#disposed) {
|
|
25514
|
+
const pokes = [...this.#pendingFecPokes.values()];
|
|
25515
|
+
this.#pendingFecPokes.clear();
|
|
25516
|
+
for (const poke of pokes) {
|
|
25517
|
+
this.#parser.pokeFecRelease(poke.packetId, poke.catalogTrackName);
|
|
25285
25518
|
}
|
|
25286
25519
|
}
|
|
25287
25520
|
}
|
|
@@ -25359,6 +25592,14 @@ var MmtpRouter = class {
|
|
|
25359
25592
|
onRap: (packetId, catalogTrackName, ssId) => {
|
|
25360
25593
|
for (const observer of this.#fecRapObservers) observer(packetId, catalogTrackName, ssId);
|
|
25361
25594
|
},
|
|
25595
|
+
// A staged switch commits on when each side's frames reach it
|
|
25596
|
+
// (#interceptSwitchFrame): both sides' overlay keyframes reach it when
|
|
25597
|
+
// they did unanchored.
|
|
25598
|
+
releaseUnanchored: (packetId, catalogTrackName) => {
|
|
25599
|
+
const staged = this.#staged;
|
|
25600
|
+
const stream = { packetId, ...catalogTrackName === void 0 ? {} : { catalogTrackName } };
|
|
25601
|
+
return staged !== null && (isStream(stream, staged.from) || isStream(stream, staged.to));
|
|
25602
|
+
},
|
|
25362
25603
|
onReleased: (data, meta) => {
|
|
25363
25604
|
if (meta.trackType !== "video" || meta.mpuSequenceNumber === void 0) return;
|
|
25364
25605
|
const timing = meta.authoredTiming;
|
|
@@ -25500,6 +25741,7 @@ var MmtpRouter = class {
|
|
|
25500
25741
|
const parsed = this.#parser.parsePacket(packet);
|
|
25501
25742
|
if (!parsed) return;
|
|
25502
25743
|
const info = parsed.info;
|
|
25744
|
+
if (manifestAdmitted && !info.isRepair && ingress?.admittedBy !== USER_AGENT_VERIFIED) this.#admittedSourceDatagrams++;
|
|
25503
25745
|
if (source === "multicast" && !manifestAdmitted) {
|
|
25504
25746
|
this.#fecManager?.noteMulticastArrival?.(info.packetId, info.sequenceNumber);
|
|
25505
25747
|
}
|
|
@@ -25808,7 +26050,8 @@ var MmtpRouter = class {
|
|
|
25808
26050
|
source: parserOptions?.source,
|
|
25809
26051
|
deliveryProvenance: parserOptions?.deliveryProvenance,
|
|
25810
26052
|
catalogTrackName: parserOptions?.catalogTrackName,
|
|
25811
|
-
...parserOptions?.fecSbn === void 0 ? {} : { fecSbn: parserOptions.fecSbn }
|
|
26053
|
+
...parserOptions?.fecSbn === void 0 ? {} : { fecSbn: parserOptions.fecSbn },
|
|
26054
|
+
...parserOptions?.fecSbnFirst === void 0 ? {} : { fecSbnFirst: parserOptions.fecSbnFirst }
|
|
25812
26055
|
};
|
|
25813
26056
|
if (parsed) this.#parser.feedParsed(packet, parsed, feedOptions);
|
|
25814
26057
|
else this.#parser.feed(packet, feedOptions);
|
|
@@ -26274,7 +26517,12 @@ var MmtpRouter = class {
|
|
|
26274
26517
|
#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, overlay) {
|
|
26275
26518
|
if (ownerPacketId == null) return;
|
|
26276
26519
|
if (this.#classifyPacketId(ownerPacketId) === null) return;
|
|
26277
|
-
const fecSbnOf = (index) =>
|
|
26520
|
+
const fecSbnOf = (index) => {
|
|
26521
|
+
if (!overlay) return { fecSbn: this.#fecAnchorForRecovered(blockId, index) };
|
|
26522
|
+
const fecSbn = overlay.baseSsIds.get(index);
|
|
26523
|
+
const fecSbnFirst = overlay.firstSsIds.get(index);
|
|
26524
|
+
return fecSbn === void 0 || fecSbnFirst === void 0 ? {} : { fecSbn, fecSbnFirst };
|
|
26525
|
+
};
|
|
26278
26526
|
const streamKey = this.#mediaStreamKey(ownerPacketId, catalogTrackName);
|
|
26279
26527
|
const ssIdSeqOffset = overlay ? void 0 : this.#ssIdSeqOffsetByStream.get(streamKey);
|
|
26280
26528
|
if (ssIdSeqOffset !== void 0) {
|
|
@@ -26282,7 +26530,10 @@ var MmtpRouter = class {
|
|
|
26282
26530
|
...result,
|
|
26283
26531
|
symbols: result.symbols.map((sym) => {
|
|
26284
26532
|
const wire = this.#fecAnchorForRecovered(blockId, sym.index) - ssIdSeqOffset >>> 0;
|
|
26285
|
-
if (wire === sym.packetSequenceNumber)
|
|
26533
|
+
if (wire === sym.packetSequenceNumber) {
|
|
26534
|
+
this.#unstampedSsIdSeqOffsetByStream.set(streamKey, ssIdSeqOffset);
|
|
26535
|
+
return sym;
|
|
26536
|
+
}
|
|
26286
26537
|
this.#restampedRecoveredSeqs++;
|
|
26287
26538
|
this.#restampedStreams.add(streamKey);
|
|
26288
26539
|
return { ...sym, packetSequenceNumber: wire };
|