@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
|
@@ -1210,6 +1210,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
1210
1210
|
#retentionBlocks;
|
|
1211
1211
|
#heldEvicted;
|
|
1212
1212
|
#newest;
|
|
1213
|
+
#newestSource;
|
|
1213
1214
|
#holes;
|
|
1214
1215
|
static #checkedRetention(retentionBlocks) {
|
|
1215
1216
|
if (!Number.isSafeInteger(retentionBlocks) || retentionBlocks <= 0) {
|
|
@@ -1247,6 +1248,21 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
1247
1248
|
observe(position) {
|
|
1248
1249
|
if (this.#newest === void 0 || serial32Precedes(this.#newest, position)) this.#newest = position;
|
|
1249
1250
|
}
|
|
1251
|
+
/**
|
|
1252
|
+
* The newest source SS_ID noted with `noteSource` (RFC 1982 order). Unlike
|
|
1253
|
+
* `newest`, a repair never moves it: a repair can run ahead of its sources,
|
|
1254
|
+
* a source cannot run ahead of the sources sent before it except by
|
|
1255
|
+
* reordering.
|
|
1256
|
+
*/
|
|
1257
|
+
get newestSource() {
|
|
1258
|
+
return this.#newestSource;
|
|
1259
|
+
}
|
|
1260
|
+
/** Note a received source's SS_ID; true when it is the newest one yet. */
|
|
1261
|
+
noteSource(ssId) {
|
|
1262
|
+
if (this.#newestSource !== void 0 && !serial32Precedes(this.#newestSource, ssId)) return false;
|
|
1263
|
+
this.#newestSource = ssId;
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1250
1266
|
/** Held sources evicted by `heldCapacity` (never by the horizon itself). */
|
|
1251
1267
|
get heldEvicted() {
|
|
1252
1268
|
return this.#heldEvicted;
|
|
@@ -1479,6 +1495,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
1479
1495
|
this.#held.clear();
|
|
1480
1496
|
this.#coverageEnd = void 0;
|
|
1481
1497
|
this.#newest = void 0;
|
|
1498
|
+
this.#newestSource = void 0;
|
|
1482
1499
|
}
|
|
1483
1500
|
#claim(block) {
|
|
1484
1501
|
const placed = [];
|
|
@@ -4061,6 +4078,9 @@ var FECManager = class _FECManager {
|
|
|
4061
4078
|
this.#unindexBlockKey(key);
|
|
4062
4079
|
}
|
|
4063
4080
|
}
|
|
4081
|
+
if (isSourceSymbol && source === "multicast") {
|
|
4082
|
+
this.#noteMulticastSource(this.#isoLocatorFor(packetId, catalogTrackName), sbn + esi >>> 0, packetId, catalogTrackName);
|
|
4083
|
+
}
|
|
4064
4084
|
return true;
|
|
4065
4085
|
}
|
|
4066
4086
|
#observeBlockCatalogTrackName(blockKey, sbn, packetId, catalogTrackName) {
|
|
@@ -4378,6 +4398,7 @@ var FECManager = class _FECManager {
|
|
|
4378
4398
|
const locator = this.#growingIsoLocatorFor(packetId, catalogTrackName);
|
|
4379
4399
|
const evictedBefore = locator.heldEvicted;
|
|
4380
4400
|
const held = locator.hold(ssId, source);
|
|
4401
|
+
if (held && source.source === "multicast") this.#noteMulticastSource(locator, ssId, packetId, catalogTrackName);
|
|
4381
4402
|
const evicted = locator.heldEvicted - evictedBefore;
|
|
4382
4403
|
if (evicted > 0) {
|
|
4383
4404
|
const before = this.#isoHeldEvictedCount;
|
|
@@ -5204,8 +5225,72 @@ var FECManager = class _FECManager {
|
|
|
5204
5225
|
this.#completeCleanBlock(blockKey, sbn, packetId, catalogTrackName);
|
|
5205
5226
|
return null;
|
|
5206
5227
|
}
|
|
5228
|
+
if (recovered === null) this.#shortenUnrecoverableWait(blockKey, sbn, packetId, catalogTrackName);
|
|
5207
5229
|
return recovered;
|
|
5208
5230
|
}
|
|
5231
|
+
/**
|
|
5232
|
+
* A first-arrival multicast source, held or placed. A datagram flow keeps
|
|
5233
|
+
* its sources in send order, so one that moves the stream's newest source
|
|
5234
|
+
* position past a block leaves the sources that block still misses
|
|
5235
|
+
* reordered behind it at best: it shortens the wait of every block it
|
|
5236
|
+
* passed that only a late source can complete. MoQ is left out: its streams
|
|
5237
|
+
* can overtake one another, so a MoQ source shows nothing about the sources
|
|
5238
|
+
* sent before it.
|
|
5239
|
+
*/
|
|
5240
|
+
#noteMulticastSource(locator, ssId, packetId, catalogTrackName) {
|
|
5241
|
+
const previous = locator.newestSource;
|
|
5242
|
+
if (!locator.noteSource(ssId) || previous === void 0) return;
|
|
5243
|
+
for (const block of locator.blocksBetween(previous - 1 >>> 0, ssId)) {
|
|
5244
|
+
if (block.span || !serial32Precedes(serial32Add(block.start, block.length - 1), ssId)) continue;
|
|
5245
|
+
this.#shortenUnrecoverableWait(
|
|
5246
|
+
this.#blockKey(block.start, packetId, catalogTrackName),
|
|
5247
|
+
block.start,
|
|
5248
|
+
packetId,
|
|
5249
|
+
catalogTrackName
|
|
5250
|
+
);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
/**
|
|
5254
|
+
* The wait of a block that only a late source can complete (BLO-52085): a
|
|
5255
|
+
* repair stated it, its received sources plus every symbol its repair group
|
|
5256
|
+
* carries (RSB_length) are short of K' (the repair scheduler's
|
|
5257
|
+
* group-cannot-complete), and a later source of the stream has arrived, so
|
|
5258
|
+
* a source it misses is lost or reordered behind that one. The span, which
|
|
5259
|
+
* covers repair symbols paced after its sources, buys it nothing: it waits
|
|
5260
|
+
* the late-symbol allowance alone, the multicast wait less the span (the
|
|
5261
|
+
* catalog tolerance or the measured delivery gap). Undefined for any other
|
|
5262
|
+
* block.
|
|
5263
|
+
*/
|
|
5264
|
+
#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config) {
|
|
5265
|
+
const authoredLength = this.#blockAuthoredLengths.get(key);
|
|
5266
|
+
const rsbLength = this.#blockRsbLengths.get(key);
|
|
5267
|
+
if (authoredLength === void 0 || rsbLength === void 0) return void 0;
|
|
5268
|
+
if ((this.#blockSourceCounts.get(key) ?? 0) + rsbLength >= authoredLength) return void 0;
|
|
5269
|
+
const newestSource = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newestSource;
|
|
5270
|
+
if (newestSource === void 0 || !serial32Precedes(serial32Add(sbn, authoredLength - 1), newestSource)) {
|
|
5271
|
+
return void 0;
|
|
5272
|
+
}
|
|
5273
|
+
return this.#multicastWaitMs(config) - config.interleaveDepthMs;
|
|
5274
|
+
}
|
|
5275
|
+
/**
|
|
5276
|
+
* Bring an open block's wait in to its late-source wait, when it has one.
|
|
5277
|
+
* Never later than it is armed: only a source can complete such a block,
|
|
5278
|
+
* so a repair of its group (paced across the span) must not keep it open.
|
|
5279
|
+
*/
|
|
5280
|
+
#shortenUnrecoverableWait(key, sbn, packetId, catalogTrackName) {
|
|
5281
|
+
if (!this.#activeBlocks.has(key)) return;
|
|
5282
|
+
const config = this.#trackConfigs.get(packetId);
|
|
5283
|
+
if (config === void 0) return;
|
|
5284
|
+
const waitMs = this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, config);
|
|
5285
|
+
if (waitMs === void 0) return;
|
|
5286
|
+
const armedAtMs = this.#blockWaitDeadlinesMs.get(key);
|
|
5287
|
+
this.#armBlockTimeout(
|
|
5288
|
+
sbn,
|
|
5289
|
+
packetId,
|
|
5290
|
+
catalogTrackName,
|
|
5291
|
+
armedAtMs === void 0 ? waitMs : Math.min(waitMs, armedAtMs - this.#receiptNow())
|
|
5292
|
+
);
|
|
5293
|
+
}
|
|
5209
5294
|
/**
|
|
5210
5295
|
* Record a block's authored SSB_length from a repair symbol. Returns false,
|
|
5211
5296
|
* leaving the block unchanged, when the symbol contradicts the block: a
|
|
@@ -5434,7 +5519,16 @@ var FECManager = class _FECManager {
|
|
|
5434
5519
|
this.#repairDeadlineMs(trackConfig)
|
|
5435
5520
|
);
|
|
5436
5521
|
}
|
|
5437
|
-
this.#
|
|
5522
|
+
if (!sourceProgress && this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, trackConfig) !== void 0) {
|
|
5523
|
+
this.#shortenUnrecoverableWait(key, sbn, packetId, catalogTrackName);
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
this.#armBlockTimeout(
|
|
5527
|
+
sbn,
|
|
5528
|
+
packetId,
|
|
5529
|
+
catalogTrackName,
|
|
5530
|
+
this.#lateSourceWaitMs(key, sbn, packetId, catalogTrackName, trackConfig) ?? timeoutMs
|
|
5531
|
+
);
|
|
5438
5532
|
}
|
|
5439
5533
|
#failBlock(sbn, packetId, catalogTrackName) {
|
|
5440
5534
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
@@ -7076,6 +7170,7 @@ var MOQT_PUBLISHER_PRIORITY = Object.freeze({
|
|
|
7076
7170
|
});
|
|
7077
7171
|
|
|
7078
7172
|
// node_modules/@blockcast/mmt-transport/dist/amt-gateway.js
|
|
7173
|
+
var MAX_PENDING_PACKETS = 256;
|
|
7079
7174
|
var AMTGatewayManager = class {
|
|
7080
7175
|
subscriptions = /* @__PURE__ */ new Map();
|
|
7081
7176
|
messageHandler;
|
|
@@ -7199,7 +7294,7 @@ var AMTGatewayManager = class {
|
|
|
7199
7294
|
if (!subscription.handshakeComplete) {
|
|
7200
7295
|
const queue = this.pendingPackets.get(subscriptionId);
|
|
7201
7296
|
const packetData2 = data;
|
|
7202
|
-
if (queue) {
|
|
7297
|
+
if (queue && queue.length < MAX_PENDING_PACKETS) {
|
|
7203
7298
|
queue.push({
|
|
7204
7299
|
data: packetData2.data,
|
|
7205
7300
|
remoteAddress: packetData2.remoteAddress,
|
|
@@ -13142,9 +13237,21 @@ var IsoBmffTimingResolver = class {
|
|
|
13142
13237
|
}
|
|
13143
13238
|
#track = null;
|
|
13144
13239
|
#fragments = /* @__PURE__ */ new Map();
|
|
13240
|
+
/** First sample of each pending fragment, kept in `#delivered` with its sample count. */
|
|
13241
|
+
#firstSamples = /* @__PURE__ */ new Map();
|
|
13242
|
+
/**
|
|
13243
|
+
* First sample and sample count of the newest fragments whose every slot was
|
|
13244
|
+
* cleared. Abandonment counts: `releaseSample` clears a slot too, which
|
|
13245
|
+
* matches the combiner, since it retires an abandoned sample as well.
|
|
13246
|
+
*/
|
|
13247
|
+
#delivered = /* @__PURE__ */ new Map();
|
|
13145
13248
|
#pendingSamples = 0;
|
|
13146
13249
|
#evictedSamples = 0;
|
|
13147
13250
|
#lastMovieFragmentRejection = null;
|
|
13251
|
+
/** True once an FT=0 has bound the track that FT=1 timing is read against. */
|
|
13252
|
+
get initialized() {
|
|
13253
|
+
return this.#track !== null;
|
|
13254
|
+
}
|
|
13148
13255
|
get pendingSampleCount() {
|
|
13149
13256
|
return this.#pendingSamples;
|
|
13150
13257
|
}
|
|
@@ -13168,7 +13275,14 @@ var IsoBmffTimingResolver = class {
|
|
|
13168
13275
|
this.#track = timing;
|
|
13169
13276
|
return true;
|
|
13170
13277
|
}
|
|
13171
|
-
|
|
13278
|
+
/**
|
|
13279
|
+
* `recovered` marks an FEC-recovered copy. A recovered block hands back
|
|
13280
|
+
* every FT=1 it holds, the received ones included, so a recovered copy of a
|
|
13281
|
+
* fragment whose samples are all done is a duplicate: admitting it again
|
|
13282
|
+
* would charge stale timing and could displace the live MPU's. A source
|
|
13283
|
+
* copy is still admitted again (a rendition switch re-arms timing that way).
|
|
13284
|
+
*/
|
|
13285
|
+
addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
|
|
13172
13286
|
this.#lastMovieFragmentRejection = null;
|
|
13173
13287
|
if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
|
|
13174
13288
|
this.#lastMovieFragmentRejection = "invalid";
|
|
@@ -13194,6 +13308,8 @@ var IsoBmffTimingResolver = class {
|
|
|
13194
13308
|
(sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
|
|
13195
13309
|
);
|
|
13196
13310
|
}
|
|
13311
|
+
const delivered = recovered ? this.#delivered.get(key) : void 0;
|
|
13312
|
+
if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
|
|
13197
13313
|
let freed = 0;
|
|
13198
13314
|
while (this.#fragments.size > 0 && (this.#pendingSamples + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
|
|
13199
13315
|
freed += this.#evictOldestFragment();
|
|
@@ -13207,6 +13323,7 @@ var IsoBmffTimingResolver = class {
|
|
|
13207
13323
|
return false;
|
|
13208
13324
|
}
|
|
13209
13325
|
this.#fragments.set(key, [...parsed.samples]);
|
|
13326
|
+
this.#firstSamples.set(key, parsed.samples[0]);
|
|
13210
13327
|
this.#pendingSamples += parsed.samples.length;
|
|
13211
13328
|
return true;
|
|
13212
13329
|
}
|
|
@@ -13264,6 +13381,7 @@ var IsoBmffTimingResolver = class {
|
|
|
13264
13381
|
let live = 0;
|
|
13265
13382
|
for (const sample of samples) if (sample !== null) live++;
|
|
13266
13383
|
this.#fragments.delete(oldest);
|
|
13384
|
+
this.#firstSamples.delete(oldest);
|
|
13267
13385
|
this.#pendingSamples -= live;
|
|
13268
13386
|
this.#evictedSamples += live;
|
|
13269
13387
|
return live;
|
|
@@ -13279,12 +13397,23 @@ var IsoBmffTimingResolver = class {
|
|
|
13279
13397
|
if (!samples || !samples[index]) return false;
|
|
13280
13398
|
samples[index] = null;
|
|
13281
13399
|
this.#pendingSamples--;
|
|
13282
|
-
if (samples.every((value) => value === null))
|
|
13400
|
+
if (samples.every((value) => value === null)) {
|
|
13401
|
+
this.#fragments.delete(key);
|
|
13402
|
+
const first = this.#firstSamples.get(key);
|
|
13403
|
+
this.#firstSamples.delete(key);
|
|
13404
|
+
this.#delivered.delete(key);
|
|
13405
|
+
this.#delivered.set(key, { first, sampleCount: samples.length });
|
|
13406
|
+
if (this.#delivered.size > this.maxPendingSamples) {
|
|
13407
|
+
this.#delivered.delete(this.#delivered.keys().next().value);
|
|
13408
|
+
}
|
|
13409
|
+
}
|
|
13283
13410
|
return true;
|
|
13284
13411
|
}
|
|
13285
13412
|
reset() {
|
|
13286
13413
|
this.#track = null;
|
|
13287
13414
|
this.#fragments.clear();
|
|
13415
|
+
this.#firstSamples.clear();
|
|
13416
|
+
this.#delivered.clear();
|
|
13288
13417
|
this.#pendingSamples = 0;
|
|
13289
13418
|
}
|
|
13290
13419
|
};
|
|
@@ -13766,6 +13895,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
13766
13895
|
this.#reclaimedTimingSamples = 0;
|
|
13767
13896
|
this.#evictedTimingSamples = 0;
|
|
13768
13897
|
this.#rejectedMovieFragments = 0;
|
|
13898
|
+
this.#recoveredHeadsAwaitingTiming = {
|
|
13899
|
+
held: 0,
|
|
13900
|
+
expired: 0,
|
|
13901
|
+
discarded: 0,
|
|
13902
|
+
superseded: 0
|
|
13903
|
+
};
|
|
13769
13904
|
this.#sequencers = /* @__PURE__ */ new Map();
|
|
13770
13905
|
this.#fecGate = null;
|
|
13771
13906
|
this.#fecSequencer = null;
|
|
@@ -13874,6 +14009,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
13874
14009
|
#reclaimedTimingSamples;
|
|
13875
14010
|
#evictedTimingSamples;
|
|
13876
14011
|
#rejectedMovieFragments;
|
|
14012
|
+
#recoveredHeadsAwaitingTiming;
|
|
13877
14013
|
#sequencers;
|
|
13878
14014
|
#fecGate;
|
|
13879
14015
|
#fecSequencer;
|
|
@@ -13994,6 +14130,14 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
13994
14130
|
rejectedMovieFragments: this.#rejectedMovieFragments
|
|
13995
14131
|
};
|
|
13996
14132
|
}
|
|
14133
|
+
/** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
|
|
14134
|
+
get recoveredHeadsAwaitingTiming() {
|
|
14135
|
+
let pending = 0;
|
|
14136
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
14137
|
+
for (const head of awaitingTiming) if (head.options?.recovered) pending++;
|
|
14138
|
+
}
|
|
14139
|
+
return { ...this.#recoveredHeadsAwaitingTiming, pending };
|
|
14140
|
+
}
|
|
13997
14141
|
get reorderStats() {
|
|
13998
14142
|
let bufferedFrames = this.#fecSequencer?.buffered ?? 0;
|
|
13999
14143
|
for (const { sequencer } of this.#sequencers.values()) bufferedFrames += sequencer.buffered;
|
|
@@ -14167,7 +14311,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14167
14311
|
const now = Date.now();
|
|
14168
14312
|
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
14169
14313
|
while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
|
|
14170
|
-
awaitingTiming.shift();
|
|
14314
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
14171
14315
|
}
|
|
14172
14316
|
}
|
|
14173
14317
|
}
|
|
@@ -14285,11 +14429,16 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14285
14429
|
if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
|
|
14286
14430
|
return this.#drop("fragment-unexpected-fragmentation");
|
|
14287
14431
|
const resolver2 = this.#timingResolver(streamKey, info.packetId, false);
|
|
14288
|
-
if (!resolver2) return this.#drop("fragment-no-resolver");
|
|
14432
|
+
if (!resolver2?.initialized) return this.#drop("fragment-no-resolver");
|
|
14289
14433
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
14290
14434
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
14291
14435
|
const remaining = this.#maxPendingTimingSamples - this.#pendingTimingSamples;
|
|
14292
|
-
const admitted = resolver2.addMovieFragment(
|
|
14436
|
+
const admitted = resolver2.addMovieFragment(
|
|
14437
|
+
mpu.mpuSequenceNumber,
|
|
14438
|
+
data,
|
|
14439
|
+
remaining,
|
|
14440
|
+
options?.recovered === true
|
|
14441
|
+
);
|
|
14293
14442
|
this.#pendingTimingSamples += resolver2.pendingSampleCount - pendingBefore;
|
|
14294
14443
|
this.#evictedTimingSamples += resolver2.evictedSampleCount - evictedBefore;
|
|
14295
14444
|
if (!admitted) {
|
|
@@ -14309,7 +14458,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14309
14458
|
};
|
|
14310
14459
|
}
|
|
14311
14460
|
if (fragmentType !== 2) return this.#drop("unknown-fragment-type");
|
|
14312
|
-
const resolver = this.#timingResolver(streamKey, info.packetId,
|
|
14461
|
+
const resolver = this.#timingResolver(streamKey, info.packetId, true);
|
|
14313
14462
|
if (!resolver) return this.#drop("mfu-no-resolver");
|
|
14314
14463
|
if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
|
|
14315
14464
|
return this.#drop("mfu-empty-payload");
|
|
@@ -14330,6 +14479,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14330
14479
|
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
14331
14480
|
movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
|
|
14332
14481
|
sampleNumber: mfuDuHeader.sampleNumber,
|
|
14482
|
+
sequenceNumber: info.sequenceNumber,
|
|
14333
14483
|
arrivedAt: Date.now()
|
|
14334
14484
|
});
|
|
14335
14485
|
return this.#drop("mfu-sample-timing-missing");
|
|
@@ -14718,6 +14868,9 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14718
14868
|
for (const { sequencer } of this.#sequencers.values()) sequencer.discard();
|
|
14719
14869
|
this.#sequencers.clear();
|
|
14720
14870
|
this.#fecSequencer?.discard();
|
|
14871
|
+
for (const { awaitingTiming } of this.#timingStreams.values()) {
|
|
14872
|
+
for (const head of awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
14873
|
+
}
|
|
14721
14874
|
this.#timingStreams.clear();
|
|
14722
14875
|
this.#pendingTimingSamples = 0;
|
|
14723
14876
|
this.#reliableMoqStartupGate.clear();
|
|
@@ -14736,10 +14889,23 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14736
14889
|
return resolver;
|
|
14737
14890
|
}
|
|
14738
14891
|
#awaitTiming(streamKey, head) {
|
|
14739
|
-
const
|
|
14740
|
-
if (!
|
|
14892
|
+
const state = this.#timingStreams.get(streamKey);
|
|
14893
|
+
if (!state) return;
|
|
14894
|
+
if (!head.options?.recovered) {
|
|
14895
|
+
const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
|
|
14896
|
+
this.#recoveredHeadsAwaitingTiming.superseded += state.awaitingTiming.length - kept.length;
|
|
14897
|
+
state.awaitingTiming = kept;
|
|
14898
|
+
}
|
|
14899
|
+
const awaitingTiming = state.awaitingTiming;
|
|
14741
14900
|
awaitingTiming.push(head);
|
|
14742
|
-
if (
|
|
14901
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming.held++;
|
|
14902
|
+
if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
|
|
14903
|
+
this.#leaveAwaitingTiming(awaitingTiming.shift(), "expired");
|
|
14904
|
+
}
|
|
14905
|
+
}
|
|
14906
|
+
/** Count a recovered head leaving the queue other than by replay or its source copy. */
|
|
14907
|
+
#leaveAwaitingTiming(head, outcome) {
|
|
14908
|
+
if (head.options?.recovered) this.#recoveredHeadsAwaitingTiming[outcome]++;
|
|
14743
14909
|
}
|
|
14744
14910
|
/**
|
|
14745
14911
|
* Feed again the heads whose timing the FT=1 just admitted declares. That
|
|
@@ -14767,6 +14933,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
14767
14933
|
const state = this.#timingStreams.get(streamKey);
|
|
14768
14934
|
if (!state) return;
|
|
14769
14935
|
this.#timingStreams.delete(streamKey);
|
|
14936
|
+
for (const head of state.awaitingTiming) this.#leaveAwaitingTiming(head, "discarded");
|
|
14770
14937
|
this.#pendingTimingSamples -= state.resolver.pendingSampleCount;
|
|
14771
14938
|
if (this.#pendingTimingSamples < 0) {
|
|
14772
14939
|
throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");
|
|
@@ -18381,7 +18548,7 @@ var MmtpRouter = class {
|
|
|
18381
18548
|
const cb = this.#getFrameCallback(packetId);
|
|
18382
18549
|
if (isVideo && !assembler) {
|
|
18383
18550
|
if (frameMeta.recovered) {
|
|
18384
|
-
this.#routeDirectRecoveredVideo(packetId, data, frameMeta, cb);
|
|
18551
|
+
this.#routeDirectRecoveredVideo(packetId, data, frameMeta, cb, meta.fecSbn);
|
|
18385
18552
|
} else {
|
|
18386
18553
|
this.#routeDirectSourceVideo(packetId, data, frameMeta, cb);
|
|
18387
18554
|
}
|
|
@@ -18771,6 +18938,10 @@ var MmtpRouter = class {
|
|
|
18771
18938
|
get keyframeOverlayStats() {
|
|
18772
18939
|
return { ...this.#overlayStats };
|
|
18773
18940
|
}
|
|
18941
|
+
/** Recovered FT=2 heads held for their FT=1, and how each left the wait (BLO-53369). */
|
|
18942
|
+
get recoveredHeadsAwaitingTiming() {
|
|
18943
|
+
return this.#parser.recoveredHeadsAwaitingTiming;
|
|
18944
|
+
}
|
|
18774
18945
|
#isOverlayRepair(repairPacketId, repairTrackName) {
|
|
18775
18946
|
return this.#overlayLegs.has(repairPacketId) || repairTrackName !== void 0 && this.#overlayRepairNames.has(repairTrackName);
|
|
18776
18947
|
}
|
|
@@ -20925,8 +21096,16 @@ var MmtpRouter = class {
|
|
|
20925
21096
|
this.#drainDirectRecoveredVideo(streamKey, packetId, (frame) => frame.timestamp <= meta.timestamp, cb);
|
|
20926
21097
|
this.#emitDirectVideo(packetId, data, meta, cb);
|
|
20927
21098
|
}
|
|
20928
|
-
#routeDirectRecoveredVideo(packetId, data, meta, cb) {
|
|
21099
|
+
#routeDirectRecoveredVideo(packetId, data, meta, cb, fecSbn) {
|
|
20929
21100
|
const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);
|
|
21101
|
+
if (this.#isFecTrack(packetId)) {
|
|
21102
|
+
const high = this.#deliveredVideoHighWaterByStream.get(streamKey);
|
|
21103
|
+
if (fecSbn !== void 0 || high !== void 0 && authoredAdjacent(high, meta)) {
|
|
21104
|
+
this.#drainDirectRecoveredVideo(streamKey, packetId, (frame) => frame.timestamp <= meta.timestamp, cb);
|
|
21105
|
+
this.#emitDirectVideo(packetId, data, meta, cb);
|
|
21106
|
+
return;
|
|
21107
|
+
}
|
|
21108
|
+
}
|
|
20930
21109
|
this.#enqueueDirectRecoveredVideo(packetId, data, meta);
|
|
20931
21110
|
const lastSourceTs = this.#lastDirectSourceVideoTsByStream.get(streamKey);
|
|
20932
21111
|
this.#drainDirectRecoveredVideo(
|
|
@@ -21180,16 +21359,29 @@ var MmtpRouter = class {
|
|
|
21180
21359
|
}
|
|
21181
21360
|
);
|
|
21182
21361
|
}
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
|
|
21187
|
-
|
|
21188
|
-
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
))
|
|
21192
|
-
|
|
21362
|
+
const untimedHead = (sym) => sym.fragmentType === 2 && sym.fragment.fragmentationIndicator === 1 && sym.fragment.movieFragmentSequenceNumber !== void 0 && sym.fragment.sampleNumber !== void 0 && !this.#parser.hasAuthoredTimingFor(
|
|
21363
|
+
ownerPacketId,
|
|
21364
|
+
sym.fragment.mpuSequenceNumber,
|
|
21365
|
+
sym.fragment.movieFragmentSequenceNumber,
|
|
21366
|
+
sym.fragment.sampleNumber,
|
|
21367
|
+
catalogTrackName
|
|
21368
|
+
);
|
|
21369
|
+
for (const sym of result.symbols) {
|
|
21370
|
+
if (!untimedHead(sym) || this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) continue;
|
|
21371
|
+
this.#feedViaParser(
|
|
21372
|
+
sym.rawSymbol,
|
|
21373
|
+
{ packetId: ownerPacketId, sequenceNumber: sym.packetSequenceNumber },
|
|
21374
|
+
void 0,
|
|
21375
|
+
{
|
|
21376
|
+
skipFecPayloadId: true,
|
|
21377
|
+
recovered: true,
|
|
21378
|
+
deliveryProvenance,
|
|
21379
|
+
...fecSbnOf(sym.index),
|
|
21380
|
+
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
21381
|
+
}
|
|
21382
|
+
);
|
|
21383
|
+
}
|
|
21384
|
+
result = { ...result, symbols: result.symbols.filter((sym) => !untimedHead(sym)) };
|
|
21193
21385
|
const gated = this.#isFecTrack(ownerPacketId);
|
|
21194
21386
|
const assembler = this.#mseAssemblerFor(ownerPacketId, catalogTrackName);
|
|
21195
21387
|
if (assembler) {
|