@blockcast/mmt-container 2.2.0-main.da2e7cd1dd5d → 2.2.0-main.f480a7ede8b1
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 +215 -23
- 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.map +1 -1
- package/dist/fec-manager.js +95 -1
- 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 +6 -0
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +120 -22
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.js +215 -23
- 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 +104 -1
- 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 +64 -18
package/dist/browser-runtime.js
CHANGED
|
@@ -8746,6 +8746,7 @@ var MOQT_PUBLISHER_PRIORITY = Object.freeze({
|
|
|
8746
8746
|
});
|
|
8747
8747
|
|
|
8748
8748
|
// node_modules/@blockcast/mmt-transport/dist/amt-gateway.js
|
|
8749
|
+
var MAX_PENDING_PACKETS = 256;
|
|
8749
8750
|
var AMTGatewayManager = class {
|
|
8750
8751
|
subscriptions = /* @__PURE__ */ new Map();
|
|
8751
8752
|
messageHandler;
|
|
@@ -8869,7 +8870,7 @@ var AMTGatewayManager = class {
|
|
|
8869
8870
|
if (!subscription.handshakeComplete) {
|
|
8870
8871
|
const queue = this.pendingPackets.get(subscriptionId);
|
|
8871
8872
|
const packetData2 = data;
|
|
8872
|
-
if (queue) {
|
|
8873
|
+
if (queue && queue.length < MAX_PENDING_PACKETS) {
|
|
8873
8874
|
queue.push({
|
|
8874
8875
|
data: packetData2.data,
|
|
8875
8876
|
remoteAddress: packetData2.remoteAddress,
|
|
@@ -12948,6 +12949,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
12948
12949
|
#retentionBlocks;
|
|
12949
12950
|
#heldEvicted;
|
|
12950
12951
|
#newest;
|
|
12952
|
+
#newestSource;
|
|
12951
12953
|
#holes;
|
|
12952
12954
|
static #checkedRetention(retentionBlocks) {
|
|
12953
12955
|
if (!Number.isSafeInteger(retentionBlocks) || retentionBlocks <= 0) {
|
|
@@ -12985,6 +12987,21 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
12985
12987
|
observe(position) {
|
|
12986
12988
|
if (this.#newest === void 0 || serial32Precedes(this.#newest, position)) this.#newest = position;
|
|
12987
12989
|
}
|
|
12990
|
+
/**
|
|
12991
|
+
* The newest source SS_ID noted with `noteSource` (RFC 1982 order). Unlike
|
|
12992
|
+
* `newest`, a repair never moves it: a repair can run ahead of its sources,
|
|
12993
|
+
* a source cannot run ahead of the sources sent before it except by
|
|
12994
|
+
* reordering.
|
|
12995
|
+
*/
|
|
12996
|
+
get newestSource() {
|
|
12997
|
+
return this.#newestSource;
|
|
12998
|
+
}
|
|
12999
|
+
/** Note a received source's SS_ID; true when it is the newest one yet. */
|
|
13000
|
+
noteSource(ssId) {
|
|
13001
|
+
if (this.#newestSource !== void 0 && !serial32Precedes(this.#newestSource, ssId)) return false;
|
|
13002
|
+
this.#newestSource = ssId;
|
|
13003
|
+
return true;
|
|
13004
|
+
}
|
|
12988
13005
|
/** Held sources evicted by `heldCapacity` (never by the horizon itself). */
|
|
12989
13006
|
get heldEvicted() {
|
|
12990
13007
|
return this.#heldEvicted;
|
|
@@ -13217,6 +13234,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
13217
13234
|
this.#held.clear();
|
|
13218
13235
|
this.#coverageEnd = void 0;
|
|
13219
13236
|
this.#newest = void 0;
|
|
13237
|
+
this.#newestSource = void 0;
|
|
13220
13238
|
}
|
|
13221
13239
|
#claim(block) {
|
|
13222
13240
|
const placed = [];
|
|
@@ -15830,6 +15848,9 @@ var FECManager = class _FECManager {
|
|
|
15830
15848
|
this.#unindexBlockKey(key);
|
|
15831
15849
|
}
|
|
15832
15850
|
}
|
|
15851
|
+
if (isSourceSymbol && source === "multicast") {
|
|
15852
|
+
this.#noteMulticastSource(this.#isoLocatorFor(packetId, catalogTrackName), sbn + esi >>> 0, packetId, catalogTrackName);
|
|
15853
|
+
}
|
|
15833
15854
|
return true;
|
|
15834
15855
|
}
|
|
15835
15856
|
#observeBlockCatalogTrackName(blockKey, sbn, packetId, catalogTrackName) {
|
|
@@ -16147,6 +16168,7 @@ var FECManager = class _FECManager {
|
|
|
16147
16168
|
const locator = this.#growingIsoLocatorFor(packetId, catalogTrackName);
|
|
16148
16169
|
const evictedBefore = locator.heldEvicted;
|
|
16149
16170
|
const held = locator.hold(ssId, source);
|
|
16171
|
+
if (held && source.source === "multicast") this.#noteMulticastSource(locator, ssId, packetId, catalogTrackName);
|
|
16150
16172
|
const evicted = locator.heldEvicted - evictedBefore;
|
|
16151
16173
|
if (evicted > 0) {
|
|
16152
16174
|
const before = this.#isoHeldEvictedCount;
|
|
@@ -16973,8 +16995,72 @@ var FECManager = class _FECManager {
|
|
|
16973
16995
|
this.#completeCleanBlock(blockKey, sbn, packetId, catalogTrackName);
|
|
16974
16996
|
return null;
|
|
16975
16997
|
}
|
|
16998
|
+
if (recovered === null) this.#shortenUnrecoverableWait(blockKey, sbn, packetId, catalogTrackName);
|
|
16976
16999
|
return recovered;
|
|
16977
17000
|
}
|
|
17001
|
+
/**
|
|
17002
|
+
* A first-arrival multicast source, held or placed. A datagram flow keeps
|
|
17003
|
+
* its sources in send order, so one that moves the stream's newest source
|
|
17004
|
+
* position past a block leaves the sources that block still misses
|
|
17005
|
+
* reordered behind it at best: it shortens the wait of every block it
|
|
17006
|
+
* passed that only a late source can complete. MoQ is left out: its streams
|
|
17007
|
+
* can overtake one another, so a MoQ source shows nothing about the sources
|
|
17008
|
+
* sent before it.
|
|
17009
|
+
*/
|
|
17010
|
+
#noteMulticastSource(locator, ssId, packetId, catalogTrackName) {
|
|
17011
|
+
const previous = locator.newestSource;
|
|
17012
|
+
if (!locator.noteSource(ssId) || previous === void 0) return;
|
|
17013
|
+
for (const block of locator.blocksBetween(previous - 1 >>> 0, ssId)) {
|
|
17014
|
+
if (block.span || !serial32Precedes(serial32Add(block.start, block.length - 1), ssId)) continue;
|
|
17015
|
+
this.#shortenUnrecoverableWait(
|
|
17016
|
+
this.#blockKey(block.start, packetId, catalogTrackName),
|
|
17017
|
+
block.start,
|
|
17018
|
+
packetId,
|
|
17019
|
+
catalogTrackName
|
|
17020
|
+
);
|
|
17021
|
+
}
|
|
17022
|
+
}
|
|
17023
|
+
/**
|
|
17024
|
+
* The wait of a block that only a late source can complete (BLO-52085): a
|
|
17025
|
+
* repair stated it, its received sources plus every symbol its repair group
|
|
17026
|
+
* carries (RSB_length) are short of K' (the repair scheduler's
|
|
17027
|
+
* group-cannot-complete), and a later source of the stream has arrived, so
|
|
17028
|
+
* a source it misses is lost or reordered behind that one. The span, which
|
|
17029
|
+
* covers repair symbols paced after its sources, buys it nothing: it waits
|
|
17030
|
+
* the late-symbol allowance alone, the multicast wait less the span (the
|
|
17031
|
+
* catalog tolerance or the measured delivery gap). Undefined for any other
|
|
17032
|
+
* block.
|
|
17033
|
+
*/
|
|
17034
|
+
#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config) {
|
|
17035
|
+
const authoredLength = this.#blockAuthoredLengths.get(key);
|
|
17036
|
+
const rsbLength = this.#blockRsbLengths.get(key);
|
|
17037
|
+
if (authoredLength === void 0 || rsbLength === void 0) return void 0;
|
|
17038
|
+
if ((this.#blockSourceCounts.get(key) ?? 0) + rsbLength >= authoredLength) return void 0;
|
|
17039
|
+
const newestSource = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newestSource;
|
|
17040
|
+
if (newestSource === void 0 || !serial32Precedes(serial32Add(sbn, authoredLength - 1), newestSource)) {
|
|
17041
|
+
return void 0;
|
|
17042
|
+
}
|
|
17043
|
+
return this.#multicastWaitMs(config) - config.interleaveDepthMs;
|
|
17044
|
+
}
|
|
17045
|
+
/**
|
|
17046
|
+
* Bring an open block's wait in to its late-source wait, when it has one.
|
|
17047
|
+
* Never later than it is armed: only a source can complete such a block,
|
|
17048
|
+
* so a repair of its group (paced across the span) must not keep it open.
|
|
17049
|
+
*/
|
|
17050
|
+
#shortenUnrecoverableWait(key, sbn, packetId, catalogTrackName) {
|
|
17051
|
+
if (!this.#activeBlocks.has(key)) return;
|
|
17052
|
+
const config = this.#trackConfigs.get(packetId);
|
|
17053
|
+
if (config === void 0) return;
|
|
17054
|
+
const waitMs = this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config);
|
|
17055
|
+
if (waitMs === void 0) return;
|
|
17056
|
+
const armedAtMs = this.#blockWaitDeadlinesMs.get(key);
|
|
17057
|
+
this.#armBlockTimeout(
|
|
17058
|
+
sbn,
|
|
17059
|
+
packetId,
|
|
17060
|
+
catalogTrackName,
|
|
17061
|
+
armedAtMs === void 0 ? waitMs : Math.min(waitMs, armedAtMs - this.#receiptNow())
|
|
17062
|
+
);
|
|
17063
|
+
}
|
|
16978
17064
|
/**
|
|
16979
17065
|
* Record a block's authored SSB_length from a repair symbol. Returns false,
|
|
16980
17066
|
* leaving the block unchanged, when the symbol contradicts the block: a
|
|
@@ -17203,7 +17289,16 @@ var FECManager = class _FECManager {
|
|
|
17203
17289
|
this.#repairDeadlineMs(trackConfig)
|
|
17204
17290
|
);
|
|
17205
17291
|
}
|
|
17206
|
-
this.#
|
|
17292
|
+
if (!sourceProgress && this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, trackConfig) !== void 0) {
|
|
17293
|
+
this.#shortenUnrecoverableWait(key, sbn, packetId, catalogTrackName);
|
|
17294
|
+
return;
|
|
17295
|
+
}
|
|
17296
|
+
this.#armBlockTimeout(
|
|
17297
|
+
sbn,
|
|
17298
|
+
packetId,
|
|
17299
|
+
catalogTrackName,
|
|
17300
|
+
this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, trackConfig) ?? timeoutMs
|
|
17301
|
+
);
|
|
17207
17302
|
}
|
|
17208
17303
|
#failBlock(sbn, packetId, catalogTrackName) {
|
|
17209
17304
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
@@ -17901,9 +17996,21 @@ var IsoBmffTimingResolver = class {
|
|
|
17901
17996
|
}
|
|
17902
17997
|
#track = null;
|
|
17903
17998
|
#fragments = /* @__PURE__ */ new Map();
|
|
17999
|
+
/** First sample of each pending fragment, kept in `#delivered` with its sample count. */
|
|
18000
|
+
#firstSamples = /* @__PURE__ */ new Map();
|
|
18001
|
+
/**
|
|
18002
|
+
* First sample and sample count of the newest fragments whose every slot was
|
|
18003
|
+
* cleared. Abandonment counts: `releaseSample` clears a slot too, which
|
|
18004
|
+
* matches the combiner, since it retires an abandoned sample as well.
|
|
18005
|
+
*/
|
|
18006
|
+
#delivered = /* @__PURE__ */ new Map();
|
|
17904
18007
|
#pendingSamples = 0;
|
|
17905
18008
|
#evictedSamples = 0;
|
|
17906
18009
|
#lastMovieFragmentRejection = null;
|
|
18010
|
+
/** True once an FT=0 has bound the track that FT=1 timing is read against. */
|
|
18011
|
+
get initialized() {
|
|
18012
|
+
return this.#track !== null;
|
|
18013
|
+
}
|
|
17907
18014
|
get pendingSampleCount() {
|
|
17908
18015
|
return this.#pendingSamples;
|
|
17909
18016
|
}
|
|
@@ -17927,7 +18034,14 @@ var IsoBmffTimingResolver = class {
|
|
|
17927
18034
|
this.#track = timing;
|
|
17928
18035
|
return true;
|
|
17929
18036
|
}
|
|
17930
|
-
|
|
18037
|
+
/**
|
|
18038
|
+
* `recovered` marks an FEC-recovered copy. A recovered block hands back
|
|
18039
|
+
* every FT=1 it holds, the received ones included, so a recovered copy of a
|
|
18040
|
+
* fragment whose samples are all done is a duplicate: admitting it again
|
|
18041
|
+
* would charge stale timing and could displace the live MPU's. A source
|
|
18042
|
+
* copy is still admitted again (a rendition switch re-arms timing that way).
|
|
18043
|
+
*/
|
|
18044
|
+
addMovieFragment(mpuSequenceNumber2, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
|
|
17931
18045
|
this.#lastMovieFragmentRejection = null;
|
|
17932
18046
|
if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
|
|
17933
18047
|
this.#lastMovieFragmentRejection = "invalid";
|
|
@@ -17953,6 +18067,8 @@ var IsoBmffTimingResolver = class {
|
|
|
17953
18067
|
(sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
|
|
17954
18068
|
);
|
|
17955
18069
|
}
|
|
18070
|
+
const delivered = recovered ? this.#delivered.get(key) : void 0;
|
|
18071
|
+
if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
|
|
17956
18072
|
let freed = 0;
|
|
17957
18073
|
while (this.#fragments.size > 0 && (this.#pendingSamples + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
|
|
17958
18074
|
freed += this.#evictOldestFragment();
|
|
@@ -17966,6 +18082,7 @@ var IsoBmffTimingResolver = class {
|
|
|
17966
18082
|
return false;
|
|
17967
18083
|
}
|
|
17968
18084
|
this.#fragments.set(key, [...parsed.samples]);
|
|
18085
|
+
this.#firstSamples.set(key, parsed.samples[0]);
|
|
17969
18086
|
this.#pendingSamples += parsed.samples.length;
|
|
17970
18087
|
return true;
|
|
17971
18088
|
}
|
|
@@ -18023,6 +18140,7 @@ var IsoBmffTimingResolver = class {
|
|
|
18023
18140
|
let live = 0;
|
|
18024
18141
|
for (const sample of samples) if (sample !== null) live++;
|
|
18025
18142
|
this.#fragments.delete(oldest);
|
|
18143
|
+
this.#firstSamples.delete(oldest);
|
|
18026
18144
|
this.#pendingSamples -= live;
|
|
18027
18145
|
this.#evictedSamples += live;
|
|
18028
18146
|
return live;
|
|
@@ -18038,12 +18156,23 @@ var IsoBmffTimingResolver = class {
|
|
|
18038
18156
|
if (!samples || !samples[index]) return false;
|
|
18039
18157
|
samples[index] = null;
|
|
18040
18158
|
this.#pendingSamples--;
|
|
18041
|
-
if (samples.every((value) => value === null))
|
|
18159
|
+
if (samples.every((value) => value === null)) {
|
|
18160
|
+
this.#fragments.delete(key);
|
|
18161
|
+
const first = this.#firstSamples.get(key);
|
|
18162
|
+
this.#firstSamples.delete(key);
|
|
18163
|
+
this.#delivered.delete(key);
|
|
18164
|
+
this.#delivered.set(key, { first, sampleCount: samples.length });
|
|
18165
|
+
if (this.#delivered.size > this.maxPendingSamples) {
|
|
18166
|
+
this.#delivered.delete(this.#delivered.keys().next().value);
|
|
18167
|
+
}
|
|
18168
|
+
}
|
|
18042
18169
|
return true;
|
|
18043
18170
|
}
|
|
18044
18171
|
reset() {
|
|
18045
18172
|
this.#track = null;
|
|
18046
18173
|
this.#fragments.clear();
|
|
18174
|
+
this.#firstSamples.clear();
|
|
18175
|
+
this.#delivered.clear();
|
|
18047
18176
|
this.#pendingSamples = 0;
|
|
18048
18177
|
}
|
|
18049
18178
|
};
|
|
@@ -18525,6 +18654,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
18525
18654
|
this.#reclaimedTimingSamples = 0;
|
|
18526
18655
|
this.#evictedTimingSamples = 0;
|
|
18527
18656
|
this.#rejectedMovieFragments = 0;
|
|
18657
|
+
this.#recoveredHeadsAwaitingTiming = {
|
|
18658
|
+
held: 0,
|
|
18659
|
+
expired: 0,
|
|
18660
|
+
discarded: 0,
|
|
18661
|
+
superseded: 0
|
|
18662
|
+
};
|
|
18528
18663
|
this.#sequencers = /* @__PURE__ */ new Map();
|
|
18529
18664
|
this.#fecGate = null;
|
|
18530
18665
|
this.#fecSequencer = null;
|
|
@@ -18633,6 +18768,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
18633
18768
|
#reclaimedTimingSamples;
|
|
18634
18769
|
#evictedTimingSamples;
|
|
18635
18770
|
#rejectedMovieFragments;
|
|
18771
|
+
#recoveredHeadsAwaitingTiming;
|
|
18636
18772
|
#sequencers;
|
|
18637
18773
|
#fecGate;
|
|
18638
18774
|
#fecSequencer;
|
|
@@ -18753,6 +18889,14 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
18753
18889
|
rejectedMovieFragments: this.#rejectedMovieFragments
|
|
18754
18890
|
};
|
|
18755
18891
|
}
|
|
18892
|
+
/** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
|
|
18893
|
+
get recoveredHeadsAwaitingTiming() {
|
|
18894
|
+
let pending = 0;
|
|
18895
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
18896
|
+
for (const head of awaitingTiming) if (head.options?.recovered) pending++;
|
|
18897
|
+
}
|
|
18898
|
+
return { ...this.#recoveredHeadsAwaitingTiming, pending };
|
|
18899
|
+
}
|
|
18756
18900
|
get reorderStats() {
|
|
18757
18901
|
let bufferedFrames = this.#fecSequencer?.buffered ?? 0;
|
|
18758
18902
|
for (const { sequencer } of this.#sequencers.values()) bufferedFrames += sequencer.buffered;
|
|
@@ -18926,7 +19070,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
18926
19070
|
const now = Date.now();
|
|
18927
19071
|
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
18928
19072
|
while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
|
|
18929
|
-
awaitingTiming.shift();
|
|
19073
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
18930
19074
|
}
|
|
18931
19075
|
}
|
|
18932
19076
|
}
|
|
@@ -19044,11 +19188,16 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19044
19188
|
if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
|
|
19045
19189
|
return this.#drop("fragment-unexpected-fragmentation");
|
|
19046
19190
|
const resolver2 = this.#timingResolver(streamKey, info.packetId, false);
|
|
19047
|
-
if (!resolver2) return this.#drop("fragment-no-resolver");
|
|
19191
|
+
if (!resolver2?.initialized) return this.#drop("fragment-no-resolver");
|
|
19048
19192
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
19049
19193
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
19050
19194
|
const remaining = this.#maxPendingTimingSamples - this.#pendingTimingSamples;
|
|
19051
|
-
const admitted = resolver2.addMovieFragment(
|
|
19195
|
+
const admitted = resolver2.addMovieFragment(
|
|
19196
|
+
mpu.mpuSequenceNumber,
|
|
19197
|
+
data,
|
|
19198
|
+
remaining,
|
|
19199
|
+
options?.recovered === true
|
|
19200
|
+
);
|
|
19052
19201
|
this.#pendingTimingSamples += resolver2.pendingSampleCount - pendingBefore;
|
|
19053
19202
|
this.#evictedTimingSamples += resolver2.evictedSampleCount - evictedBefore;
|
|
19054
19203
|
if (!admitted) {
|
|
@@ -19068,7 +19217,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19068
19217
|
};
|
|
19069
19218
|
}
|
|
19070
19219
|
if (fragmentType !== 2) return this.#drop("unknown-fragment-type");
|
|
19071
|
-
const resolver = this.#timingResolver(streamKey, info.packetId,
|
|
19220
|
+
const resolver = this.#timingResolver(streamKey, info.packetId, true);
|
|
19072
19221
|
if (!resolver) return this.#drop("mfu-no-resolver");
|
|
19073
19222
|
if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
|
|
19074
19223
|
return this.#drop("mfu-empty-payload");
|
|
@@ -19089,6 +19238,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19089
19238
|
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
19090
19239
|
movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
|
|
19091
19240
|
sampleNumber: mfuDuHeader.sampleNumber,
|
|
19241
|
+
sequenceNumber: info.sequenceNumber,
|
|
19092
19242
|
arrivedAt: Date.now()
|
|
19093
19243
|
});
|
|
19094
19244
|
return this.#drop("mfu-sample-timing-missing");
|
|
@@ -19477,6 +19627,9 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19477
19627
|
for (const { sequencer } of this.#sequencers.values()) sequencer.discard();
|
|
19478
19628
|
this.#sequencers.clear();
|
|
19479
19629
|
this.#fecSequencer?.discard();
|
|
19630
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
19631
|
+
for (const head of awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
19632
|
+
}
|
|
19480
19633
|
this.#timingStreams.clear();
|
|
19481
19634
|
this.#pendingTimingSamples = 0;
|
|
19482
19635
|
this.#reliableMoqStartupGate.clear();
|
|
@@ -19495,10 +19648,23 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19495
19648
|
return resolver;
|
|
19496
19649
|
}
|
|
19497
19650
|
#awaitTiming(streamKey, head) {
|
|
19498
|
-
const
|
|
19499
|
-
if (!
|
|
19651
|
+
const state = this.#timingStreams.get(streamKey);
|
|
19652
|
+
if (!state) return;
|
|
19653
|
+
if (!head.options?.recovered) {
|
|
19654
|
+
const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
|
|
19655
|
+
this.#recoveredHeadsAwaitingTiming.superseded += state.awaitingTiming.length - kept.length;
|
|
19656
|
+
state.awaitingTiming = kept;
|
|
19657
|
+
}
|
|
19658
|
+
const awaitingTiming = state.awaitingTiming;
|
|
19500
19659
|
awaitingTiming.push(head);
|
|
19501
|
-
if (
|
|
19660
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming.held++;
|
|
19661
|
+
if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
|
|
19662
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
19663
|
+
}
|
|
19664
|
+
}
|
|
19665
|
+
/** Count a recovered head leaving the queue other than by replay or its source copy. */
|
|
19666
|
+
#leaveAwaitingTiming(head, outcome) {
|
|
19667
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming[outcome]++;
|
|
19502
19668
|
}
|
|
19503
19669
|
/**
|
|
19504
19670
|
* Feed again the heads whose timing the FT=1 just admitted declares. That
|
|
@@ -19526,6 +19692,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
19526
19692
|
const state = this.#timingStreams.get(streamKey);
|
|
19527
19693
|
if (!state) return;
|
|
19528
19694
|
this.#timingStreams.delete(streamKey);
|
|
19695
|
+
for (const head of state.awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
19529
19696
|
this.#pendingTimingSamples -= state.resolver.pendingSampleCount;
|
|
19530
19697
|
if (this.#pendingTimingSamples < 0) {
|
|
19531
19698
|
throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");
|
|
@@ -23141,7 +23308,7 @@ var MmtpRouter = class {
|
|
|
23141
23308
|
const cb = this.#getFrameCallback(packetId);
|
|
23142
23309
|
if (isVideo && !assembler) {
|
|
23143
23310
|
if (frameMeta.recovered) {
|
|
23144
|
-
this.#routeDirectRecoveredVideo(packetId, data, frameMeta, cb);
|
|
23311
|
+
this.#routeDirectRecoveredVideo(packetId, data, frameMeta, cb, meta.fecSbn);
|
|
23145
23312
|
} else {
|
|
23146
23313
|
this.#routeDirectSourceVideo(packetId, data, frameMeta, cb);
|
|
23147
23314
|
}
|
|
@@ -23531,6 +23698,10 @@ var MmtpRouter = class {
|
|
|
23531
23698
|
get keyframeOverlayStats() {
|
|
23532
23699
|
return { ...this.#overlayStats };
|
|
23533
23700
|
}
|
|
23701
|
+
/** Recovered FT=2 heads held for their FT=1, and how each left the wait (BLO-53369). */
|
|
23702
|
+
get recoveredHeadsAwaitingTiming() {
|
|
23703
|
+
return this.#parser.recoveredHeadsAwaitingTiming;
|
|
23704
|
+
}
|
|
23534
23705
|
#isOverlayRepair(repairPacketId, repairTrackName) {
|
|
23535
23706
|
return this.#overlayLegs.has(repairPacketId) || repairTrackName !== void 0 && this.#overlayRepairNames.has(repairTrackName);
|
|
23536
23707
|
}
|
|
@@ -25685,8 +25856,16 @@ var MmtpRouter = class {
|
|
|
25685
25856
|
this.#drainDirectRecoveredVideo(streamKey, packetId, (frame) => frame.timestamp <= meta.timestamp, cb);
|
|
25686
25857
|
this.#emitDirectVideo(packetId, data, meta, cb);
|
|
25687
25858
|
}
|
|
25688
|
-
#routeDirectRecoveredVideo(packetId, data, meta, cb) {
|
|
25859
|
+
#routeDirectRecoveredVideo(packetId, data, meta, cb, fecSbn) {
|
|
25689
25860
|
const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);
|
|
25861
|
+
if (this.#isFecTrack(packetId)) {
|
|
25862
|
+
const high = this.#deliveredVideoHighWaterByStream.get(streamKey);
|
|
25863
|
+
if (fecSbn !== void 0 || high !== void 0 && authoredAdjacent(high, meta)) {
|
|
25864
|
+
this.#drainDirectRecoveredVideo(streamKey, packetId, (frame) => frame.timestamp <= meta.timestamp, cb);
|
|
25865
|
+
this.#emitDirectVideo(packetId, data, meta, cb);
|
|
25866
|
+
return;
|
|
25867
|
+
}
|
|
25868
|
+
}
|
|
25690
25869
|
this.#enqueueDirectRecoveredVideo(packetId, data, meta);
|
|
25691
25870
|
const lastSourceTs = this.#lastDirectSourceVideoTsByStream.get(streamKey);
|
|
25692
25871
|
this.#drainDirectRecoveredVideo(
|
|
@@ -25940,16 +26119,29 @@ var MmtpRouter = class {
|
|
|
25940
26119
|
}
|
|
25941
26120
|
);
|
|
25942
26121
|
}
|
|
25943
|
-
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
))
|
|
25952
|
-
|
|
26122
|
+
const untimedHead = (sym) => sym.fragmentType === 2 && sym.fragment.fragmentationIndicator === 1 && sym.fragment.movieFragmentSequenceNumber !== void 0 && sym.fragment.sampleNumber !== void 0 && !this.#parser.hasAuthoredTimingFor(
|
|
26123
|
+
ownerPacketId,
|
|
26124
|
+
sym.fragment.mpuSequenceNumber,
|
|
26125
|
+
sym.fragment.movieFragmentSequenceNumber,
|
|
26126
|
+
sym.fragment.sampleNumber,
|
|
26127
|
+
catalogTrackName
|
|
26128
|
+
);
|
|
26129
|
+
for (const sym of result.symbols) {
|
|
26130
|
+
if (!untimedHead(sym) || this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) continue;
|
|
26131
|
+
this.#feedViaParser(
|
|
26132
|
+
sym.rawSymbol,
|
|
26133
|
+
{ packetId: ownerPacketId, sequenceNumber: sym.packetSequenceNumber },
|
|
26134
|
+
void 0,
|
|
26135
|
+
{
|
|
26136
|
+
skipFecPayloadId: true,
|
|
26137
|
+
recovered: true,
|
|
26138
|
+
deliveryProvenance,
|
|
26139
|
+
...fecSbnOf(sym.index),
|
|
26140
|
+
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
26141
|
+
}
|
|
26142
|
+
);
|
|
26143
|
+
}
|
|
26144
|
+
result = { ...result, symbols: result.symbols.filter((sym) => !untimedHead(sym)) };
|
|
25953
26145
|
const gated = this.#isFecTrack(ownerPacketId);
|
|
25954
26146
|
const assembler = this.#mseAssemblerFor(ownerPacketId, catalogTrackName);
|
|
25955
26147
|
if (assembler) {
|