@blockcast/mmt-container 2.2.0-main.da2e7cd1dd5d → 2.2.0-main.e75bbd7576a6
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 +472 -36
- package/dist/browser-runtime.js.map +2 -2
- package/dist/delivery-catalog.js +2 -1
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/fec-manager.d.ts +30 -0
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +196 -2
- package/dist/fec-manager.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.d.ts +9 -0
- package/dist/iso-block-locator.d.ts.map +1 -1
- package/dist/iso-block-locator.js +17 -0
- package/dist/iso-block-locator.js.map +2 -2
- package/dist/iso-bmff-timing.d.ts +10 -1
- package/dist/iso-bmff-timing.d.ts.map +1 -1
- package/dist/iso-bmff-timing.js +36 -2
- package/dist/iso-bmff-timing.js.map +2 -2
- package/dist/mmtp-container-parser.d.ts +35 -2
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +82 -9
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +85 -11
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/mmtp-decode-pipeline.js +81 -9
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-router.d.ts +18 -1
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +276 -34
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.js +472 -36
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.js +2 -1
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.js +81 -9
- package/dist/no-modules.js.map +2 -2
- package/dist/wasm-mmtp-container-parser.js +81 -9
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +5 -5
- package/src/fec-manager.ts +251 -2
- package/src/index.ts +1 -0
- package/src/iso-block-locator.ts +19 -0
- package/src/iso-bmff-timing.ts +40 -1
- package/src/mmtp-container-parser.ts +84 -8
- package/src/mmtp-router.ts +346 -30
package/dist/no-modules.js
CHANGED
|
@@ -5568,9 +5568,21 @@ var IsoBmffTimingResolver = class {
|
|
|
5568
5568
|
}
|
|
5569
5569
|
#track = null;
|
|
5570
5570
|
#fragments = /* @__PURE__ */ new Map();
|
|
5571
|
+
/** First sample of each pending fragment, kept in `#delivered` with its sample count. */
|
|
5572
|
+
#firstSamples = /* @__PURE__ */ new Map();
|
|
5573
|
+
/**
|
|
5574
|
+
* First sample and sample count of the newest fragments whose every slot was
|
|
5575
|
+
* cleared. Abandonment counts: `releaseSample` clears a slot too, which
|
|
5576
|
+
* matches the combiner, since it retires an abandoned sample as well.
|
|
5577
|
+
*/
|
|
5578
|
+
#delivered = /* @__PURE__ */ new Map();
|
|
5571
5579
|
#pendingSamples = 0;
|
|
5572
5580
|
#evictedSamples = 0;
|
|
5573
5581
|
#lastMovieFragmentRejection = null;
|
|
5582
|
+
/** True once an FT=0 has bound the track that FT=1 timing is read against. */
|
|
5583
|
+
get initialized() {
|
|
5584
|
+
return this.#track !== null;
|
|
5585
|
+
}
|
|
5574
5586
|
get pendingSampleCount() {
|
|
5575
5587
|
return this.#pendingSamples;
|
|
5576
5588
|
}
|
|
@@ -5594,7 +5606,14 @@ var IsoBmffTimingResolver = class {
|
|
|
5594
5606
|
this.#track = timing;
|
|
5595
5607
|
return true;
|
|
5596
5608
|
}
|
|
5597
|
-
|
|
5609
|
+
/**
|
|
5610
|
+
* `recovered` marks an FEC-recovered copy. A recovered block hands back
|
|
5611
|
+
* every FT=1 it holds, the received ones included, so a recovered copy of a
|
|
5612
|
+
* fragment whose samples are all done is a duplicate: admitting it again
|
|
5613
|
+
* would charge stale timing and could displace the live MPU's. A source
|
|
5614
|
+
* copy is still admitted again (a rendition switch re-arms timing that way).
|
|
5615
|
+
*/
|
|
5616
|
+
addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
|
|
5598
5617
|
this.#lastMovieFragmentRejection = null;
|
|
5599
5618
|
if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
|
|
5600
5619
|
this.#lastMovieFragmentRejection = "invalid";
|
|
@@ -5620,6 +5639,8 @@ var IsoBmffTimingResolver = class {
|
|
|
5620
5639
|
(sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
|
|
5621
5640
|
);
|
|
5622
5641
|
}
|
|
5642
|
+
const delivered = recovered ? this.#delivered.get(key) : void 0;
|
|
5643
|
+
if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
|
|
5623
5644
|
let freed = 0;
|
|
5624
5645
|
while (this.#fragments.size > 0 && (this.#pendingSamples + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
|
|
5625
5646
|
freed += this.#evictOldestFragment();
|
|
@@ -5633,6 +5654,7 @@ var IsoBmffTimingResolver = class {
|
|
|
5633
5654
|
return false;
|
|
5634
5655
|
}
|
|
5635
5656
|
this.#fragments.set(key, [...parsed.samples]);
|
|
5657
|
+
this.#firstSamples.set(key, parsed.samples[0]);
|
|
5636
5658
|
this.#pendingSamples += parsed.samples.length;
|
|
5637
5659
|
return true;
|
|
5638
5660
|
}
|
|
@@ -5690,6 +5712,7 @@ var IsoBmffTimingResolver = class {
|
|
|
5690
5712
|
let live = 0;
|
|
5691
5713
|
for (const sample of samples) if (sample !== null) live++;
|
|
5692
5714
|
this.#fragments.delete(oldest);
|
|
5715
|
+
this.#firstSamples.delete(oldest);
|
|
5693
5716
|
this.#pendingSamples -= live;
|
|
5694
5717
|
this.#evictedSamples += live;
|
|
5695
5718
|
return live;
|
|
@@ -5705,12 +5728,23 @@ var IsoBmffTimingResolver = class {
|
|
|
5705
5728
|
if (!samples || !samples[index]) return false;
|
|
5706
5729
|
samples[index] = null;
|
|
5707
5730
|
this.#pendingSamples--;
|
|
5708
|
-
if (samples.every((value) => value === null))
|
|
5731
|
+
if (samples.every((value) => value === null)) {
|
|
5732
|
+
this.#fragments.delete(key);
|
|
5733
|
+
const first = this.#firstSamples.get(key);
|
|
5734
|
+
this.#firstSamples.delete(key);
|
|
5735
|
+
this.#delivered.delete(key);
|
|
5736
|
+
this.#delivered.set(key, { first, sampleCount: samples.length });
|
|
5737
|
+
if (this.#delivered.size > this.maxPendingSamples) {
|
|
5738
|
+
this.#delivered.delete(this.#delivered.keys().next().value);
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5709
5741
|
return true;
|
|
5710
5742
|
}
|
|
5711
5743
|
reset() {
|
|
5712
5744
|
this.#track = null;
|
|
5713
5745
|
this.#fragments.clear();
|
|
5746
|
+
this.#firstSamples.clear();
|
|
5747
|
+
this.#delivered.clear();
|
|
5714
5748
|
this.#pendingSamples = 0;
|
|
5715
5749
|
}
|
|
5716
5750
|
};
|
|
@@ -6192,6 +6226,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6192
6226
|
this.#reclaimedTimingSamples = 0;
|
|
6193
6227
|
this.#evictedTimingSamples = 0;
|
|
6194
6228
|
this.#rejectedMovieFragments = 0;
|
|
6229
|
+
this.#recoveredHeadsAwaitingTiming = {
|
|
6230
|
+
held: 0,
|
|
6231
|
+
expired: 0,
|
|
6232
|
+
discarded: 0,
|
|
6233
|
+
superseded: 0
|
|
6234
|
+
};
|
|
6195
6235
|
this.#sequencers = /* @__PURE__ */ new Map();
|
|
6196
6236
|
this.#fecGate = null;
|
|
6197
6237
|
this.#fecSequencer = null;
|
|
@@ -6300,6 +6340,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6300
6340
|
#reclaimedTimingSamples;
|
|
6301
6341
|
#evictedTimingSamples;
|
|
6302
6342
|
#rejectedMovieFragments;
|
|
6343
|
+
#recoveredHeadsAwaitingTiming;
|
|
6303
6344
|
#sequencers;
|
|
6304
6345
|
#fecGate;
|
|
6305
6346
|
#fecSequencer;
|
|
@@ -6420,6 +6461,14 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6420
6461
|
rejectedMovieFragments: this.#rejectedMovieFragments
|
|
6421
6462
|
};
|
|
6422
6463
|
}
|
|
6464
|
+
/** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
|
|
6465
|
+
get recoveredHeadsAwaitingTiming() {
|
|
6466
|
+
let pending = 0;
|
|
6467
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
6468
|
+
for (const head of awaitingTiming) if (head.options?.recovered) pending++;
|
|
6469
|
+
}
|
|
6470
|
+
return { ...this.#recoveredHeadsAwaitingTiming, pending };
|
|
6471
|
+
}
|
|
6423
6472
|
get reorderStats() {
|
|
6424
6473
|
let bufferedFrames = this.#fecSequencer?.buffered ?? 0;
|
|
6425
6474
|
for (const { sequencer } of this.#sequencers.values()) bufferedFrames += sequencer.buffered;
|
|
@@ -6593,7 +6642,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6593
6642
|
const now = Date.now();
|
|
6594
6643
|
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
6595
6644
|
while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
|
|
6596
|
-
awaitingTiming.shift();
|
|
6645
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
6597
6646
|
}
|
|
6598
6647
|
}
|
|
6599
6648
|
}
|
|
@@ -6711,11 +6760,16 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6711
6760
|
if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
|
|
6712
6761
|
return this.#drop("fragment-unexpected-fragmentation");
|
|
6713
6762
|
const resolver2 = this.#timingResolver(streamKey, info.packetId, false);
|
|
6714
|
-
if (!resolver2) return this.#drop("fragment-no-resolver");
|
|
6763
|
+
if (!resolver2?.initialized) return this.#drop("fragment-no-resolver");
|
|
6715
6764
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
6716
6765
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
6717
6766
|
const remaining = this.#maxPendingTimingSamples - this.#pendingTimingSamples;
|
|
6718
|
-
const admitted = resolver2.addMovieFragment(
|
|
6767
|
+
const admitted = resolver2.addMovieFragment(
|
|
6768
|
+
mpu.mpuSequenceNumber,
|
|
6769
|
+
data,
|
|
6770
|
+
remaining,
|
|
6771
|
+
options?.recovered === true
|
|
6772
|
+
);
|
|
6719
6773
|
this.#pendingTimingSamples += resolver2.pendingSampleCount - pendingBefore;
|
|
6720
6774
|
this.#evictedTimingSamples += resolver2.evictedSampleCount - evictedBefore;
|
|
6721
6775
|
if (!admitted) {
|
|
@@ -6735,7 +6789,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6735
6789
|
};
|
|
6736
6790
|
}
|
|
6737
6791
|
if (fragmentType !== 2) return this.#drop("unknown-fragment-type");
|
|
6738
|
-
const resolver = this.#timingResolver(streamKey, info.packetId,
|
|
6792
|
+
const resolver = this.#timingResolver(streamKey, info.packetId, true);
|
|
6739
6793
|
if (!resolver) return this.#drop("mfu-no-resolver");
|
|
6740
6794
|
if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
|
|
6741
6795
|
return this.#drop("mfu-empty-payload");
|
|
@@ -6756,6 +6810,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6756
6810
|
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
6757
6811
|
movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
|
|
6758
6812
|
sampleNumber: mfuDuHeader.sampleNumber,
|
|
6813
|
+
sequenceNumber: info.sequenceNumber,
|
|
6759
6814
|
arrivedAt: Date.now()
|
|
6760
6815
|
});
|
|
6761
6816
|
return this.#drop("mfu-sample-timing-missing");
|
|
@@ -7144,6 +7199,9 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
7144
7199
|
for (const { sequencer } of this.#sequencers.values()) sequencer.discard();
|
|
7145
7200
|
this.#sequencers.clear();
|
|
7146
7201
|
this.#fecSequencer?.discard();
|
|
7202
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
7203
|
+
for (const head of awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
7204
|
+
}
|
|
7147
7205
|
this.#timingStreams.clear();
|
|
7148
7206
|
this.#pendingTimingSamples = 0;
|
|
7149
7207
|
this.#reliableMoqStartupGate.clear();
|
|
@@ -7162,10 +7220,23 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
7162
7220
|
return resolver;
|
|
7163
7221
|
}
|
|
7164
7222
|
#awaitTiming(streamKey, head) {
|
|
7165
|
-
const
|
|
7166
|
-
if (!
|
|
7223
|
+
const state = this.#timingStreams.get(streamKey);
|
|
7224
|
+
if (!state) return;
|
|
7225
|
+
if (!head.options?.recovered) {
|
|
7226
|
+
const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
|
|
7227
|
+
this.#recoveredHeadsAwaitingTiming.superseded += state.awaitingTiming.length - kept.length;
|
|
7228
|
+
state.awaitingTiming = kept;
|
|
7229
|
+
}
|
|
7230
|
+
const awaitingTiming = state.awaitingTiming;
|
|
7167
7231
|
awaitingTiming.push(head);
|
|
7168
|
-
if (
|
|
7232
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming.held++;
|
|
7233
|
+
if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
|
|
7234
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
7235
|
+
}
|
|
7236
|
+
}
|
|
7237
|
+
/** Count a recovered head leaving the queue other than by replay or its source copy. */
|
|
7238
|
+
#leaveAwaitingTiming(head, outcome) {
|
|
7239
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming[outcome]++;
|
|
7169
7240
|
}
|
|
7170
7241
|
/**
|
|
7171
7242
|
* Feed again the heads whose timing the FT=1 just admitted declares. That
|
|
@@ -7193,6 +7264,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
7193
7264
|
const state = this.#timingStreams.get(streamKey);
|
|
7194
7265
|
if (!state) return;
|
|
7195
7266
|
this.#timingStreams.delete(streamKey);
|
|
7267
|
+
for (const head of state.awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
7196
7268
|
this.#pendingTimingSamples -= state.resolver.pendingSampleCount;
|
|
7197
7269
|
if (this.#pendingTimingSamples < 0) {
|
|
7198
7270
|
throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");
|