@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
|
@@ -4851,9 +4851,21 @@ var IsoBmffTimingResolver = class {
|
|
|
4851
4851
|
}
|
|
4852
4852
|
#track = null;
|
|
4853
4853
|
#fragments = /* @__PURE__ */ new Map();
|
|
4854
|
+
/** First sample of each pending fragment, kept in `#delivered` with its sample count. */
|
|
4855
|
+
#firstSamples = /* @__PURE__ */ new Map();
|
|
4856
|
+
/**
|
|
4857
|
+
* First sample and sample count of the newest fragments whose every slot was
|
|
4858
|
+
* cleared. Abandonment counts: `releaseSample` clears a slot too, which
|
|
4859
|
+
* matches the combiner, since it retires an abandoned sample as well.
|
|
4860
|
+
*/
|
|
4861
|
+
#delivered = /* @__PURE__ */ new Map();
|
|
4854
4862
|
#pendingSamples = 0;
|
|
4855
4863
|
#evictedSamples = 0;
|
|
4856
4864
|
#lastMovieFragmentRejection = null;
|
|
4865
|
+
/** True once an FT=0 has bound the track that FT=1 timing is read against. */
|
|
4866
|
+
get initialized() {
|
|
4867
|
+
return this.#track !== null;
|
|
4868
|
+
}
|
|
4857
4869
|
get pendingSampleCount() {
|
|
4858
4870
|
return this.#pendingSamples;
|
|
4859
4871
|
}
|
|
@@ -4877,7 +4889,14 @@ var IsoBmffTimingResolver = class {
|
|
|
4877
4889
|
this.#track = timing;
|
|
4878
4890
|
return true;
|
|
4879
4891
|
}
|
|
4880
|
-
|
|
4892
|
+
/**
|
|
4893
|
+
* `recovered` marks an FEC-recovered copy. A recovered block hands back
|
|
4894
|
+
* every FT=1 it holds, the received ones included, so a recovered copy of a
|
|
4895
|
+
* fragment whose samples are all done is a duplicate: admitting it again
|
|
4896
|
+
* would charge stale timing and could displace the live MPU's. A source
|
|
4897
|
+
* copy is still admitted again (a rendition switch re-arms timing that way).
|
|
4898
|
+
*/
|
|
4899
|
+
addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
|
|
4881
4900
|
this.#lastMovieFragmentRejection = null;
|
|
4882
4901
|
if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
|
|
4883
4902
|
this.#lastMovieFragmentRejection = "invalid";
|
|
@@ -4903,6 +4922,8 @@ var IsoBmffTimingResolver = class {
|
|
|
4903
4922
|
(sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
|
|
4904
4923
|
);
|
|
4905
4924
|
}
|
|
4925
|
+
const delivered = recovered ? this.#delivered.get(key) : void 0;
|
|
4926
|
+
if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
|
|
4906
4927
|
let freed = 0;
|
|
4907
4928
|
while (this.#fragments.size > 0 && (this.#pendingSamples + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
|
|
4908
4929
|
freed += this.#evictOldestFragment();
|
|
@@ -4916,6 +4937,7 @@ var IsoBmffTimingResolver = class {
|
|
|
4916
4937
|
return false;
|
|
4917
4938
|
}
|
|
4918
4939
|
this.#fragments.set(key, [...parsed.samples]);
|
|
4940
|
+
this.#firstSamples.set(key, parsed.samples[0]);
|
|
4919
4941
|
this.#pendingSamples += parsed.samples.length;
|
|
4920
4942
|
return true;
|
|
4921
4943
|
}
|
|
@@ -4973,6 +4995,7 @@ var IsoBmffTimingResolver = class {
|
|
|
4973
4995
|
let live = 0;
|
|
4974
4996
|
for (const sample of samples) if (sample !== null) live++;
|
|
4975
4997
|
this.#fragments.delete(oldest);
|
|
4998
|
+
this.#firstSamples.delete(oldest);
|
|
4976
4999
|
this.#pendingSamples -= live;
|
|
4977
5000
|
this.#evictedSamples += live;
|
|
4978
5001
|
return live;
|
|
@@ -4988,12 +5011,23 @@ var IsoBmffTimingResolver = class {
|
|
|
4988
5011
|
if (!samples || !samples[index]) return false;
|
|
4989
5012
|
samples[index] = null;
|
|
4990
5013
|
this.#pendingSamples--;
|
|
4991
|
-
if (samples.every((value) => value === null))
|
|
5014
|
+
if (samples.every((value) => value === null)) {
|
|
5015
|
+
this.#fragments.delete(key);
|
|
5016
|
+
const first = this.#firstSamples.get(key);
|
|
5017
|
+
this.#firstSamples.delete(key);
|
|
5018
|
+
this.#delivered.delete(key);
|
|
5019
|
+
this.#delivered.set(key, { first, sampleCount: samples.length });
|
|
5020
|
+
if (this.#delivered.size > this.maxPendingSamples) {
|
|
5021
|
+
this.#delivered.delete(this.#delivered.keys().next().value);
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
4992
5024
|
return true;
|
|
4993
5025
|
}
|
|
4994
5026
|
reset() {
|
|
4995
5027
|
this.#track = null;
|
|
4996
5028
|
this.#fragments.clear();
|
|
5029
|
+
this.#firstSamples.clear();
|
|
5030
|
+
this.#delivered.clear();
|
|
4997
5031
|
this.#pendingSamples = 0;
|
|
4998
5032
|
}
|
|
4999
5033
|
};
|
|
@@ -5475,6 +5509,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
5475
5509
|
this.#reclaimedTimingSamples = 0;
|
|
5476
5510
|
this.#evictedTimingSamples = 0;
|
|
5477
5511
|
this.#rejectedMovieFragments = 0;
|
|
5512
|
+
this.#recoveredHeadsAwaitingTiming = {
|
|
5513
|
+
held: 0,
|
|
5514
|
+
expired: 0,
|
|
5515
|
+
discarded: 0,
|
|
5516
|
+
superseded: 0
|
|
5517
|
+
};
|
|
5478
5518
|
this.#sequencers = /* @__PURE__ */ new Map();
|
|
5479
5519
|
this.#fecGate = null;
|
|
5480
5520
|
this.#fecSequencer = null;
|
|
@@ -5583,6 +5623,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
5583
5623
|
#reclaimedTimingSamples;
|
|
5584
5624
|
#evictedTimingSamples;
|
|
5585
5625
|
#rejectedMovieFragments;
|
|
5626
|
+
#recoveredHeadsAwaitingTiming;
|
|
5586
5627
|
#sequencers;
|
|
5587
5628
|
#fecGate;
|
|
5588
5629
|
#fecSequencer;
|
|
@@ -5703,6 +5744,14 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
5703
5744
|
rejectedMovieFragments: this.#rejectedMovieFragments
|
|
5704
5745
|
};
|
|
5705
5746
|
}
|
|
5747
|
+
/** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
|
|
5748
|
+
get recoveredHeadsAwaitingTiming() {
|
|
5749
|
+
let pending = 0;
|
|
5750
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
5751
|
+
for (const head of awaitingTiming) if (head.options?.recovered) pending++;
|
|
5752
|
+
}
|
|
5753
|
+
return { ...this.#recoveredHeadsAwaitingTiming, pending };
|
|
5754
|
+
}
|
|
5706
5755
|
get reorderStats() {
|
|
5707
5756
|
let bufferedFrames = this.#fecSequencer?.buffered ?? 0;
|
|
5708
5757
|
for (const { sequencer } of this.#sequencers.values()) bufferedFrames += sequencer.buffered;
|
|
@@ -5876,7 +5925,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
5876
5925
|
const now = Date.now();
|
|
5877
5926
|
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
5878
5927
|
while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
|
|
5879
|
-
awaitingTiming.shift();
|
|
5928
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
5880
5929
|
}
|
|
5881
5930
|
}
|
|
5882
5931
|
}
|
|
@@ -5994,11 +6043,16 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
5994
6043
|
if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
|
|
5995
6044
|
return this.#drop("fragment-unexpected-fragmentation");
|
|
5996
6045
|
const resolver2 = this.#timingResolver(streamKey, info.packetId, false);
|
|
5997
|
-
if (!resolver2) return this.#drop("fragment-no-resolver");
|
|
6046
|
+
if (!resolver2?.initialized) return this.#drop("fragment-no-resolver");
|
|
5998
6047
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
5999
6048
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
6000
6049
|
const remaining = this.#maxPendingTimingSamples - this.#pendingTimingSamples;
|
|
6001
|
-
const admitted = resolver2.addMovieFragment(
|
|
6050
|
+
const admitted = resolver2.addMovieFragment(
|
|
6051
|
+
mpu.mpuSequenceNumber,
|
|
6052
|
+
data,
|
|
6053
|
+
remaining,
|
|
6054
|
+
options?.recovered === true
|
|
6055
|
+
);
|
|
6002
6056
|
this.#pendingTimingSamples += resolver2.pendingSampleCount - pendingBefore;
|
|
6003
6057
|
this.#evictedTimingSamples += resolver2.evictedSampleCount - evictedBefore;
|
|
6004
6058
|
if (!admitted) {
|
|
@@ -6018,7 +6072,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6018
6072
|
};
|
|
6019
6073
|
}
|
|
6020
6074
|
if (fragmentType !== 2) return this.#drop("unknown-fragment-type");
|
|
6021
|
-
const resolver = this.#timingResolver(streamKey, info.packetId,
|
|
6075
|
+
const resolver = this.#timingResolver(streamKey, info.packetId, true);
|
|
6022
6076
|
if (!resolver) return this.#drop("mfu-no-resolver");
|
|
6023
6077
|
if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
|
|
6024
6078
|
return this.#drop("mfu-empty-payload");
|
|
@@ -6039,6 +6093,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6039
6093
|
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
6040
6094
|
movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
|
|
6041
6095
|
sampleNumber: mfuDuHeader.sampleNumber,
|
|
6096
|
+
sequenceNumber: info.sequenceNumber,
|
|
6042
6097
|
arrivedAt: Date.now()
|
|
6043
6098
|
});
|
|
6044
6099
|
return this.#drop("mfu-sample-timing-missing");
|
|
@@ -6427,6 +6482,9 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6427
6482
|
for (const { sequencer } of this.#sequencers.values()) sequencer.discard();
|
|
6428
6483
|
this.#sequencers.clear();
|
|
6429
6484
|
this.#fecSequencer?.discard();
|
|
6485
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
6486
|
+
for (const head of awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
6487
|
+
}
|
|
6430
6488
|
this.#timingStreams.clear();
|
|
6431
6489
|
this.#pendingTimingSamples = 0;
|
|
6432
6490
|
this.#reliableMoqStartupGate.clear();
|
|
@@ -6445,10 +6503,23 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6445
6503
|
return resolver;
|
|
6446
6504
|
}
|
|
6447
6505
|
#awaitTiming(streamKey, head) {
|
|
6448
|
-
const
|
|
6449
|
-
if (!
|
|
6506
|
+
const state = this.#timingStreams.get(streamKey);
|
|
6507
|
+
if (!state) return;
|
|
6508
|
+
if (!head.options?.recovered) {
|
|
6509
|
+
const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
|
|
6510
|
+
this.#recoveredHeadsAwaitingTiming.superseded += state.awaitingTiming.length - kept.length;
|
|
6511
|
+
state.awaitingTiming = kept;
|
|
6512
|
+
}
|
|
6513
|
+
const awaitingTiming = state.awaitingTiming;
|
|
6450
6514
|
awaitingTiming.push(head);
|
|
6451
|
-
if (
|
|
6515
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming.held++;
|
|
6516
|
+
if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
|
|
6517
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
/** Count a recovered head leaving the queue other than by replay or its source copy. */
|
|
6521
|
+
#leaveAwaitingTiming(head, outcome) {
|
|
6522
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming[outcome]++;
|
|
6452
6523
|
}
|
|
6453
6524
|
/**
|
|
6454
6525
|
* Feed again the heads whose timing the FT=1 just admitted declares. That
|
|
@@ -6476,6 +6547,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
6476
6547
|
const state = this.#timingStreams.get(streamKey);
|
|
6477
6548
|
if (!state) return;
|
|
6478
6549
|
this.#timingStreams.delete(streamKey);
|
|
6550
|
+
for (const head of state.awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
6479
6551
|
this.#pendingTimingSamples -= state.resolver.pendingSampleCount;
|
|
6480
6552
|
if (this.#pendingTimingSamples < 0) {
|
|
6481
6553
|
throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");
|