@blockcast/mmt-container 2.2.0-main.efb4fa4521af → 2.2.0-main.fb5c7dfbe2de
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/README.md +29 -17
- package/dist/browser-runtime.js +1336 -302
- package/dist/browser-runtime.js.map +3 -3
- package/dist/catalog-fec-params.d.ts +17 -1
- package/dist/catalog-fec-params.d.ts.map +1 -1
- package/dist/catalog-fec-params.js +15 -0
- package/dist/catalog-fec-params.js.map +2 -2
- package/dist/cmcd-producer.d.ts +60 -47
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js +138 -109
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js +26 -16
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +70 -12
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +403 -58
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-worker-setup.d.ts +1 -1
- package/dist/fec-worker-setup.js +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.d.ts +48 -13
- package/dist/iso-block-locator.d.ts.map +1 -1
- package/dist/iso-block-locator.js +97 -25
- 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/media-router.js.map +1 -1
- package/dist/mmtp-container-parser.d.ts +79 -10
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +207 -26
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +209 -29
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/mmtp-decode-pipeline.js +206 -26
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-router.d.ts +34 -8
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +555 -79
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.d.ts +13 -3
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +980 -146
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.js +28 -18
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.js +206 -26
- package/dist/no-modules.js.map +2 -2
- package/dist/repair-fetch.d.ts +36 -8
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +285 -26
- package/dist/repair-fetch.js.map +3 -3
- package/dist/repair-layer-subscriptions.d.ts +6 -2
- package/dist/repair-layer-subscriptions.d.ts.map +1 -1
- package/dist/repair-layer-subscriptions.js +6 -3
- package/dist/repair-layer-subscriptions.js.map +2 -2
- package/dist/track-session.d.ts +1 -1
- package/dist/track-session.d.ts.map +1 -1
- package/dist/track-session.js +1 -0
- package/dist/track-session.js.map +2 -2
- package/dist/wasm-mmtp-container-parser.js +206 -26
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +8 -5
- package/src/catalog-fec-params.ts +36 -1
- package/src/cmcd-producer.ts +189 -179
- package/src/fec-manager.ts +433 -32
- package/src/fec-worker-setup.ts +2 -2
- package/src/index.ts +9 -2
- package/src/iso-block-locator.ts +133 -31
- package/src/iso-bmff-timing.ts +40 -1
- package/src/mmtp-container-parser.ts +306 -29
- package/src/mmtp-router.ts +548 -38
- package/src/multicast-manager.ts +44 -6
- package/src/repair-fetch.ts +363 -49
- package/src/repair-layer-subscriptions.ts +13 -5
- package/src/track-session.ts +4 -1
|
@@ -5633,13 +5633,21 @@ var BlockcastMmt = (() => {
|
|
|
5633
5633
|
function sameSample(a, b) {
|
|
5634
5634
|
return a.decodeTime === b.decodeTime && a.presentationTime === b.presentationTime && a.duration === b.duration && a.timescale === b.timescale && a.size === b.size && a.bytePosition === b.bytePosition;
|
|
5635
5635
|
}
|
|
5636
|
-
var _track, _fragments, _pendingSamples, _evictedSamples, _lastMovieFragmentRejection, _IsoBmffTimingResolver_instances, evictOldestFragment_fn, clearSlot_fn;
|
|
5636
|
+
var _track, _fragments, _firstSamples, _delivered, _pendingSamples, _evictedSamples, _lastMovieFragmentRejection, _IsoBmffTimingResolver_instances, evictOldestFragment_fn, clearSlot_fn;
|
|
5637
5637
|
var IsoBmffTimingResolver = class {
|
|
5638
5638
|
constructor(maxPendingSamples) {
|
|
5639
5639
|
this.maxPendingSamples = maxPendingSamples;
|
|
5640
5640
|
__privateAdd(this, _IsoBmffTimingResolver_instances);
|
|
5641
5641
|
__privateAdd(this, _track, null);
|
|
5642
5642
|
__privateAdd(this, _fragments, /* @__PURE__ */ new Map());
|
|
5643
|
+
/** First sample of each pending fragment, kept in `#delivered` with its sample count. */
|
|
5644
|
+
__privateAdd(this, _firstSamples, /* @__PURE__ */ new Map());
|
|
5645
|
+
/**
|
|
5646
|
+
* First sample and sample count of the newest fragments whose every slot was
|
|
5647
|
+
* cleared. Abandonment counts: `releaseSample` clears a slot too, which
|
|
5648
|
+
* matches the combiner, since it retires an abandoned sample as well.
|
|
5649
|
+
*/
|
|
5650
|
+
__privateAdd(this, _delivered, /* @__PURE__ */ new Map());
|
|
5643
5651
|
__privateAdd(this, _pendingSamples, 0);
|
|
5644
5652
|
__privateAdd(this, _evictedSamples, 0);
|
|
5645
5653
|
__privateAdd(this, _lastMovieFragmentRejection, null);
|
|
@@ -5647,6 +5655,10 @@ var BlockcastMmt = (() => {
|
|
|
5647
5655
|
throw new Error("[IsoBmffTimingResolver] maxPendingSamples must be a positive integer");
|
|
5648
5656
|
}
|
|
5649
5657
|
}
|
|
5658
|
+
/** True once an FT=0 has bound the track that FT=1 timing is read against. */
|
|
5659
|
+
get initialized() {
|
|
5660
|
+
return __privateGet(this, _track) !== null;
|
|
5661
|
+
}
|
|
5650
5662
|
get pendingSampleCount() {
|
|
5651
5663
|
return __privateGet(this, _pendingSamples);
|
|
5652
5664
|
}
|
|
@@ -5670,7 +5682,14 @@ var BlockcastMmt = (() => {
|
|
|
5670
5682
|
__privateSet(this, _track, timing);
|
|
5671
5683
|
return true;
|
|
5672
5684
|
}
|
|
5673
|
-
|
|
5685
|
+
/**
|
|
5686
|
+
* `recovered` marks an FEC-recovered copy. A recovered block hands back
|
|
5687
|
+
* every FT=1 it holds, the received ones included, so a recovered copy of a
|
|
5688
|
+
* fragment whose samples are all done is a duplicate: admitting it again
|
|
5689
|
+
* would charge stale timing and could displace the live MPU's. A source
|
|
5690
|
+
* copy is still admitted again (a rendition switch re-arms timing that way).
|
|
5691
|
+
*/
|
|
5692
|
+
addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
|
|
5674
5693
|
__privateSet(this, _lastMovieFragmentRejection, null);
|
|
5675
5694
|
if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
|
|
5676
5695
|
__privateSet(this, _lastMovieFragmentRejection, "invalid");
|
|
@@ -5696,6 +5715,8 @@ var BlockcastMmt = (() => {
|
|
|
5696
5715
|
(sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
|
|
5697
5716
|
);
|
|
5698
5717
|
}
|
|
5718
|
+
const delivered = recovered ? __privateGet(this, _delivered).get(key) : void 0;
|
|
5719
|
+
if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
|
|
5699
5720
|
let freed = 0;
|
|
5700
5721
|
while (__privateGet(this, _fragments).size > 0 && (__privateGet(this, _pendingSamples) + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
|
|
5701
5722
|
freed += __privateMethod(this, _IsoBmffTimingResolver_instances, evictOldestFragment_fn).call(this);
|
|
@@ -5709,6 +5730,7 @@ var BlockcastMmt = (() => {
|
|
|
5709
5730
|
return false;
|
|
5710
5731
|
}
|
|
5711
5732
|
__privateGet(this, _fragments).set(key, [...parsed.samples]);
|
|
5733
|
+
__privateGet(this, _firstSamples).set(key, parsed.samples[0]);
|
|
5712
5734
|
__privateSet(this, _pendingSamples, __privateGet(this, _pendingSamples) + parsed.samples.length);
|
|
5713
5735
|
return true;
|
|
5714
5736
|
}
|
|
@@ -5755,11 +5777,15 @@ var BlockcastMmt = (() => {
|
|
|
5755
5777
|
reset() {
|
|
5756
5778
|
__privateSet(this, _track, null);
|
|
5757
5779
|
__privateGet(this, _fragments).clear();
|
|
5780
|
+
__privateGet(this, _firstSamples).clear();
|
|
5781
|
+
__privateGet(this, _delivered).clear();
|
|
5758
5782
|
__privateSet(this, _pendingSamples, 0);
|
|
5759
5783
|
}
|
|
5760
5784
|
};
|
|
5761
5785
|
_track = new WeakMap();
|
|
5762
5786
|
_fragments = new WeakMap();
|
|
5787
|
+
_firstSamples = new WeakMap();
|
|
5788
|
+
_delivered = new WeakMap();
|
|
5763
5789
|
_pendingSamples = new WeakMap();
|
|
5764
5790
|
_evictedSamples = new WeakMap();
|
|
5765
5791
|
_lastMovieFragmentRejection = new WeakMap();
|
|
@@ -5778,6 +5804,7 @@ var BlockcastMmt = (() => {
|
|
|
5778
5804
|
let live = 0;
|
|
5779
5805
|
for (const sample of samples) if (sample !== null) live++;
|
|
5780
5806
|
__privateGet(this, _fragments).delete(oldest);
|
|
5807
|
+
__privateGet(this, _firstSamples).delete(oldest);
|
|
5781
5808
|
__privateSet(this, _pendingSamples, __privateGet(this, _pendingSamples) - live);
|
|
5782
5809
|
__privateSet(this, _evictedSamples, __privateGet(this, _evictedSamples) + live);
|
|
5783
5810
|
return live;
|
|
@@ -5793,7 +5820,16 @@ var BlockcastMmt = (() => {
|
|
|
5793
5820
|
if (!samples || !samples[index]) return false;
|
|
5794
5821
|
samples[index] = null;
|
|
5795
5822
|
__privateWrapper(this, _pendingSamples)._--;
|
|
5796
|
-
if (samples.every((value) => value === null))
|
|
5823
|
+
if (samples.every((value) => value === null)) {
|
|
5824
|
+
__privateGet(this, _fragments).delete(key);
|
|
5825
|
+
const first = __privateGet(this, _firstSamples).get(key);
|
|
5826
|
+
__privateGet(this, _firstSamples).delete(key);
|
|
5827
|
+
__privateGet(this, _delivered).delete(key);
|
|
5828
|
+
__privateGet(this, _delivered).set(key, { first, sampleCount: samples.length });
|
|
5829
|
+
if (__privateGet(this, _delivered).size > this.maxPendingSamples) {
|
|
5830
|
+
__privateGet(this, _delivered).delete(__privateGet(this, _delivered).keys().next().value);
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5797
5833
|
return true;
|
|
5798
5834
|
};
|
|
5799
5835
|
function fragmentKey(mpuSequenceNumber, movieFragmentSequenceNumber) {
|
|
@@ -5969,7 +6005,10 @@ var BlockcastMmt = (() => {
|
|
|
5969
6005
|
function ssIdAfter(a, b) {
|
|
5970
6006
|
return (a - b | 0) > 0;
|
|
5971
6007
|
}
|
|
5972
|
-
|
|
6008
|
+
function holdsRebuilt(stream) {
|
|
6009
|
+
return stream.buf.some(({ meta }) => meta.fecSbnFirst !== void 0);
|
|
6010
|
+
}
|
|
6011
|
+
var _streams2, _gate, _emit2, _recordStat2, _deferDepth, _deferredStreams, _FecReleaseSequencer_instances, drainUnlessDeferred_fn, streamFor_fn2, drain_fn, drainLoop_fn, cursorPassed_fn, release_fn;
|
|
5973
6012
|
var FecReleaseSequencer = class {
|
|
5974
6013
|
constructor(gate, emit, recordStat) {
|
|
5975
6014
|
__privateAdd(this, _FecReleaseSequencer_instances);
|
|
@@ -6029,6 +6068,7 @@ var BlockcastMmt = (() => {
|
|
|
6029
6068
|
stream.lastSbn = void 0;
|
|
6030
6069
|
stream.discontinuity = false;
|
|
6031
6070
|
stream.rapSsId = void 0;
|
|
6071
|
+
stream.sourced = false;
|
|
6032
6072
|
}
|
|
6033
6073
|
if (stream.last !== null && compareFrameOrder(frame, stream.last) <= 0) {
|
|
6034
6074
|
__privateGet(this, _recordStat2).call(this, "droppedStale");
|
|
@@ -6055,6 +6095,7 @@ var BlockcastMmt = (() => {
|
|
|
6055
6095
|
if (compareFrameOrder(stream.buf[mid], frame) < 0) lo = mid + 1;
|
|
6056
6096
|
else hi = mid;
|
|
6057
6097
|
}
|
|
6098
|
+
if (stream.buf[lo]?.passed === true) frame.passed = true;
|
|
6058
6099
|
stream.buf.splice(lo, 0, frame);
|
|
6059
6100
|
__privateMethod(this, _FecReleaseSequencer_instances, drainUnlessDeferred_fn).call(this, stream);
|
|
6060
6101
|
}
|
|
@@ -6090,6 +6131,41 @@ var BlockcastMmt = (() => {
|
|
|
6090
6131
|
}
|
|
6091
6132
|
return false;
|
|
6092
6133
|
}
|
|
6134
|
+
/**
|
|
6135
|
+
* These streams' sink is being replaced by one that has seen no source
|
|
6136
|
+
* frame yet. First, from a stream that holds a frame the overlay rebuilt,
|
|
6137
|
+
* release to the sink being replaced the frames the unanchored path had
|
|
6138
|
+
* already released to it (#cursorPassed): there they waited in its queue,
|
|
6139
|
+
* which its replacement flushes. A stream that holds none is not drained:
|
|
6140
|
+
* it holds nothing the anchor kept back, and a drain would still force out
|
|
6141
|
+
* a frame held past the hold limit, which before the anchor waited for the
|
|
6142
|
+
* next push. Then treat the streams as unsourced until a source frame is
|
|
6143
|
+
* released.
|
|
6144
|
+
*/
|
|
6145
|
+
unsource(packetId, catalogTrackName) {
|
|
6146
|
+
for (const stream of __privateGet(this, _streams2).values()) {
|
|
6147
|
+
if (stream.packetId === packetId && (catalogTrackName === void 0 || stream.catalogTrackName === catalogTrackName)) {
|
|
6148
|
+
if (stream.draining === 0 && holdsRebuilt(stream)) {
|
|
6149
|
+
if (__privateGet(this, _deferDepth) > 0) __privateGet(this, _deferredStreams).add(stream);
|
|
6150
|
+
else __privateMethod(this, _FecReleaseSequencer_instances, drain_fn).call(this, stream, true);
|
|
6151
|
+
}
|
|
6152
|
+
stream.sourced = false;
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Whether a stream of `packetId` holds a frame the keyframe overlay rebuilt
|
|
6158
|
+
* (FrameMeta.fecSbnFirst). Without `catalogTrackName`, every stream of the
|
|
6159
|
+
* packetId.
|
|
6160
|
+
*/
|
|
6161
|
+
holdsRebuilt(packetId, catalogTrackName) {
|
|
6162
|
+
for (const stream of __privateGet(this, _streams2).values()) {
|
|
6163
|
+
if (stream.packetId !== packetId) continue;
|
|
6164
|
+
if (catalogTrackName !== void 0 && stream.catalogTrackName !== catalogTrackName) continue;
|
|
6165
|
+
if (holdsRebuilt(stream)) return true;
|
|
6166
|
+
}
|
|
6167
|
+
return false;
|
|
6168
|
+
}
|
|
6093
6169
|
/** Re-evaluate held gaps after FEC state changed. */
|
|
6094
6170
|
poke(streamKey) {
|
|
6095
6171
|
if (streamKey !== void 0) {
|
|
@@ -6193,16 +6269,32 @@ var BlockcastMmt = (() => {
|
|
|
6193
6269
|
last: null,
|
|
6194
6270
|
lastSbn: void 0,
|
|
6195
6271
|
discontinuity: false,
|
|
6196
|
-
rapSsId: void 0
|
|
6272
|
+
rapSsId: void 0,
|
|
6273
|
+
sourced: false,
|
|
6274
|
+
draining: 0
|
|
6197
6275
|
};
|
|
6198
6276
|
__privateGet(this, _streams2).set(streamKey, stream);
|
|
6199
6277
|
}
|
|
6200
6278
|
return stream;
|
|
6201
6279
|
};
|
|
6202
|
-
|
|
6280
|
+
/** `asUnanchored`: release overlay-rebuilt frames when unanchored did (#cursorPassed). */
|
|
6281
|
+
drain_fn = function(stream, asUnanchored = false) {
|
|
6282
|
+
stream.draining++;
|
|
6283
|
+
try {
|
|
6284
|
+
__privateMethod(this, _FecReleaseSequencer_instances, drainLoop_fn).call(this, stream, asUnanchored);
|
|
6285
|
+
} finally {
|
|
6286
|
+
stream.draining--;
|
|
6287
|
+
}
|
|
6288
|
+
};
|
|
6289
|
+
drainLoop_fn = function(stream, asUnanchored) {
|
|
6203
6290
|
while (stream.buf.length > 0 && stream.last !== null) {
|
|
6204
6291
|
const head = stream.buf[0];
|
|
6205
6292
|
const last = stream.last;
|
|
6293
|
+
if (compareFrameOrder(head, last) <= 0) {
|
|
6294
|
+
stream.buf.shift();
|
|
6295
|
+
__privateMethod(this, _FecReleaseSequencer_instances, release_fn).call(this, stream, head, void 0);
|
|
6296
|
+
continue;
|
|
6297
|
+
}
|
|
6206
6298
|
if (stream.discontinuity) {
|
|
6207
6299
|
stream.discontinuity = false;
|
|
6208
6300
|
stream.buf.shift();
|
|
@@ -6216,9 +6308,10 @@ var BlockcastMmt = (() => {
|
|
|
6216
6308
|
}
|
|
6217
6309
|
const headSbn = head.meta.fecSbn;
|
|
6218
6310
|
const from = stream.lastSbn ?? headSbn;
|
|
6219
|
-
const to = headSbn ?? stream.lastSbn;
|
|
6311
|
+
const to = head.meta.fecSbnFirst ?? headSbn ?? stream.lastSbn;
|
|
6220
6312
|
let lost = [];
|
|
6221
6313
|
const unanchored = from === void 0 && to === void 0;
|
|
6314
|
+
let unresolved = false;
|
|
6222
6315
|
if (from !== void 0 && to !== void 0) {
|
|
6223
6316
|
const resolved = __privateGet(this, _gate).resolvedLoss(
|
|
6224
6317
|
stream.packetId,
|
|
@@ -6227,7 +6320,8 @@ var BlockcastMmt = (() => {
|
|
|
6227
6320
|
Math.max(from, to),
|
|
6228
6321
|
from
|
|
6229
6322
|
);
|
|
6230
|
-
|
|
6323
|
+
unresolved = resolved === null;
|
|
6324
|
+
if (unresolved && !__privateMethod(this, _FecReleaseSequencer_instances, cursorPassed_fn).call(this, stream, asUnanchored)) {
|
|
6231
6325
|
const heldMs = performance.now() - head.arrivedAtMs;
|
|
6232
6326
|
if (heldMs <= __privateGet(this, _gate).holdLimitMs(stream.packetId, stream.catalogTrackName)) return;
|
|
6233
6327
|
__privateGet(this, _recordStat2).call(this, "forcedWindowFlushes");
|
|
@@ -6235,18 +6329,52 @@ var BlockcastMmt = (() => {
|
|
|
6235
6329
|
__privateMethod(this, _FecReleaseSequencer_instances, release_fn).call(this, stream, head, __privateGet(this, _gate).decide(last, head, [], true));
|
|
6236
6330
|
continue;
|
|
6237
6331
|
}
|
|
6238
|
-
lost = resolved;
|
|
6332
|
+
lost = resolved ?? [];
|
|
6239
6333
|
}
|
|
6240
6334
|
stream.buf.shift();
|
|
6241
|
-
__privateMethod(this, _FecReleaseSequencer_instances, release_fn).call(this, stream, head, lost.length > 0 || unanchored || authoredGap(last.meta, head.meta) ? __privateGet(this, _gate).decide(last, head, lost, false) : void 0);
|
|
6335
|
+
__privateMethod(this, _FecReleaseSequencer_instances, release_fn).call(this, stream, head, lost.length > 0 || unanchored || unresolved || authoredGap(last.meta, head.meta) ? __privateGet(this, _gate).decide(last, head, lost, false) : void 0);
|
|
6242
6336
|
}
|
|
6243
6337
|
};
|
|
6338
|
+
/**
|
|
6339
|
+
* An overlay-rebuilt frame (fecSbnFirst) waits no longer than the BLO-53342
|
|
6340
|
+
* source cursor holds it unanchored, and neither does a later frame behind
|
|
6341
|
+
* it. Unanchored, a frame that waits on no block (fecSbnFirst set, or no
|
|
6342
|
+
* fecSbn) left the gate once every frame ahead of it had: mark it `passed`,
|
|
6343
|
+
* like each frame that arrived in front of it since (stale there). The head
|
|
6344
|
+
* then goes, decided as it would be unanchored, once a frame the base path
|
|
6345
|
+
* delivered (a source frame, or one base FEC recovered) is buffered after a
|
|
6346
|
+
* marked one: the base path has passed that one, so a frame still missing
|
|
6347
|
+
* before it can only come from a repair. It also goes while no source frame
|
|
6348
|
+
* has been released, when the direct path shows a recovered frame at once.
|
|
6349
|
+
* And it goes at once while a staged switch decides on when frames arrive
|
|
6350
|
+
* (FecReleaseGate.releaseUnanchored), and when unsource releases into a
|
|
6351
|
+
* sink being replaced: unanchored, it had already reached the sink.
|
|
6352
|
+
* The scan also writes: the `passed` marks it sets stay on the frames,
|
|
6353
|
+
* and push marks a frame it inserts in front of a marked one from them.
|
|
6354
|
+
*/
|
|
6355
|
+
cursorPassed_fn = function(stream, asUnanchored) {
|
|
6356
|
+
let at = -1;
|
|
6357
|
+
for (let i = 0; i < stream.buf.length; i++) {
|
|
6358
|
+
const frame = stream.buf[i];
|
|
6359
|
+
if (frame.meta.fecSbnFirst !== void 0 || frame.meta.fecSbn === void 0) {
|
|
6360
|
+
frame.passed = true;
|
|
6361
|
+
if (at < 0) at = i;
|
|
6362
|
+
} else if (frame.passed !== true) break;
|
|
6363
|
+
}
|
|
6364
|
+
if (at < 0) return false;
|
|
6365
|
+
return asUnanchored || !stream.sourced || __privateGet(this, _gate).releaseUnanchored?.(stream.packetId, stream.catalogTrackName) === true || stream.buf.some(({ meta }, i) => i > at && meta.fecSbnFirst === void 0 && (meta.recovered !== true || meta.fecSbn !== void 0));
|
|
6366
|
+
};
|
|
6244
6367
|
release_fn = function(stream, frame, decision) {
|
|
6368
|
+
const stale = stream.last !== null && compareFrameOrder(frame, stream.last) <= 0;
|
|
6369
|
+
if (stale) __privateGet(this, _recordStat2).call(this, "droppedStale");
|
|
6245
6370
|
if (decision) frame.meta.gapBefore = decision;
|
|
6246
6371
|
stream.last = frame;
|
|
6247
|
-
if (frame.meta.fecSbn !== void 0
|
|
6372
|
+
if (frame.meta.fecSbn !== void 0 && (frame.meta.fecSbnFirst === void 0 || stream.lastSbn !== void 0)) {
|
|
6373
|
+
stream.lastSbn = frame.meta.fecSbn;
|
|
6374
|
+
}
|
|
6375
|
+
if (frame.meta.recovered !== true) stream.sourced = true;
|
|
6248
6376
|
__privateGet(this, _gate).onReleased?.(frame.data, frame.meta);
|
|
6249
|
-
__privateGet(this, _emit2).call(this, frame.data, frame.meta);
|
|
6377
|
+
if (!stale) __privateGet(this, _emit2).call(this, frame.data, frame.meta);
|
|
6250
6378
|
};
|
|
6251
6379
|
var FMPU_BOX_TYPES = /* @__PURE__ */ new Set(["ftyp", "moov", "moof"]);
|
|
6252
6380
|
function isFmp4Box(data) {
|
|
@@ -6255,7 +6383,7 @@ var BlockcastMmt = (() => {
|
|
|
6255
6383
|
return FMPU_BOX_TYPES.has(type);
|
|
6256
6384
|
}
|
|
6257
6385
|
var MAX_AWAITING_TIMING_HEADS = 8;
|
|
6258
|
-
var _tracks, _defaultTrack, _reassembler, _sampleCombiners, _sampleCombinerOptions, _retiredSampleCombinerStats, _timingStreams, _maxPendingTimingSamples, _pendingTimingSamples, _reclaimedTimingSamples, _evictedTimingSamples, _rejectedMovieFragments, _sequencers, _fecGate, _fecSequencer, _reorderWindow, _reassemblyPolicy, _frameCb, _initCb, _sourceSymbolCb, _trackFrameCbs, _reorderStats, _reliableMoqStartupGate, _disposed, _replayingAwaitingTiming, _fedObjects, _reassemblerFragmentsFed, _reassemblerDataUnitsReturned, _feedOutcomes, _deferredFeedOutcomes, _lastOutcome, _lastOutcomeCount, _MmtpContainerParser_instances, streamKey_fn, sampleCombinerFor_fn, releaseTimingSlot_fn, dropSampleCombiner_fn, countOutcome_fn, countDeferredOutcome_fn, drop_fn, feedCounted_fn, feed_fn, emitCombinedSample_fn, passReliableMoqStartupGate_fn, timingResolver_fn, awaitTiming_fn, replayAwaitingTiming_fn, dropTimingStream_fn, validateReassemblyPolicy_fn, buildFrameMeta_fn, sequencerFor_fn, flushSequencers_fn, emitFrame_fn2, _MmtpContainerParser_static, scanForKeyframe_fn, scanLengthPrefixedHevcForKeyframe_fn, scanAnnexBForKeyframe_fn;
|
|
6386
|
+
var _tracks, _defaultTrack, _reassembler, _sampleCombiners, _sampleCombinerOptions, _retiredSampleCombinerStats, _timingStreams, _maxPendingTimingSamples, _pendingTimingSamples, _reclaimedTimingSamples, _evictedTimingSamples, _rejectedMovieFragments, _recoveredHeadsAwaitingTiming, _sequencers, _fecGate, _fecSequencer, _reorderWindow, _reassemblyPolicy, _frameCb, _initCb, _sourceSymbolCb, _trackFrameCbs, _reorderStats, _reliableMoqStartupGate, _disposed, _replayingAwaitingTiming, _fedObjects, _reassemblerFragmentsFed, _reassemblerDataUnitsReturned, _feedOutcomes, _deferredFeedOutcomes, _lastOutcome, _lastOutcomeCount, _MmtpContainerParser_instances, streamKey_fn, sampleCombinerFor_fn, releaseTimingSlot_fn, dropSampleCombiner_fn, countOutcome_fn, countDeferredOutcome_fn, drop_fn, feedCounted_fn, feed_fn, emitCombinedSample_fn, passReliableMoqStartupGate_fn, timingResolver_fn, awaitTiming_fn, leaveAwaitingTiming_fn, replayAwaitingTiming_fn, dropTimingStream_fn, validateReassemblyPolicy_fn, buildFrameMeta_fn, sequencerFor_fn, flushSequencers_fn, emitFrame_fn2, _MmtpContainerParser_static, scanForKeyframe_fn, scanLengthPrefixedHevcForKeyframe_fn, scanAnnexBForKeyframe_fn;
|
|
6259
6387
|
var _MmtpContainerParser = class _MmtpContainerParser {
|
|
6260
6388
|
constructor(config) {
|
|
6261
6389
|
__privateAdd(this, _MmtpContainerParser_instances);
|
|
@@ -6291,6 +6419,12 @@ var BlockcastMmt = (() => {
|
|
|
6291
6419
|
__privateAdd(this, _reclaimedTimingSamples, 0);
|
|
6292
6420
|
__privateAdd(this, _evictedTimingSamples, 0);
|
|
6293
6421
|
__privateAdd(this, _rejectedMovieFragments, 0);
|
|
6422
|
+
__privateAdd(this, _recoveredHeadsAwaitingTiming, {
|
|
6423
|
+
held: 0,
|
|
6424
|
+
expired: 0,
|
|
6425
|
+
discarded: 0,
|
|
6426
|
+
superseded: 0
|
|
6427
|
+
});
|
|
6294
6428
|
__privateAdd(this, _sequencers, /* @__PURE__ */ new Map());
|
|
6295
6429
|
__privateAdd(this, _fecGate, null);
|
|
6296
6430
|
__privateAdd(this, _fecSequencer, null);
|
|
@@ -6418,11 +6552,12 @@ var BlockcastMmt = (() => {
|
|
|
6418
6552
|
/**
|
|
6419
6553
|
* Typed view of the ISO-BMFF timing-sample budget.
|
|
6420
6554
|
*
|
|
6421
|
-
*
|
|
6422
|
-
*
|
|
6423
|
-
*
|
|
6424
|
-
*
|
|
6425
|
-
*
|
|
6555
|
+
* Each stream admits timing against its own window of `capacity` slots
|
|
6556
|
+
* (BLO-52085). A stream at capacity displaces its own oldest timing to
|
|
6557
|
+
* admit the moof in hand, and `evicted` counts the slots so displaced:
|
|
6558
|
+
* timing a stream declared and never delivered shows up there. `pending`
|
|
6559
|
+
* sums every stream, so it can exceed `capacity`. Per repo policy this is
|
|
6560
|
+
* a producer-owned counter -- never infer budget state by parsing logs.
|
|
6426
6561
|
*/
|
|
6427
6562
|
get timingBudgetStats() {
|
|
6428
6563
|
return {
|
|
@@ -6433,6 +6568,14 @@ var BlockcastMmt = (() => {
|
|
|
6433
6568
|
rejectedMovieFragments: __privateGet(this, _rejectedMovieFragments)
|
|
6434
6569
|
};
|
|
6435
6570
|
}
|
|
6571
|
+
/** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
|
|
6572
|
+
get recoveredHeadsAwaitingTiming() {
|
|
6573
|
+
let pending = 0;
|
|
6574
|
+
for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
|
|
6575
|
+
for (const head of awaitingTiming) if (head.options?.recovered) pending++;
|
|
6576
|
+
}
|
|
6577
|
+
return { ...__privateGet(this, _recoveredHeadsAwaitingTiming), pending };
|
|
6578
|
+
}
|
|
6436
6579
|
get reorderStats() {
|
|
6437
6580
|
let bufferedFrames = __privateGet(this, _fecSequencer)?.buffered ?? 0;
|
|
6438
6581
|
for (const { sequencer } of __privateGet(this, _sequencers).values()) bufferedFrames += sequencer.buffered;
|
|
@@ -6474,6 +6617,14 @@ var BlockcastMmt = (() => {
|
|
|
6474
6617
|
fecReleaseRapAbove(packetId, catalogTrackName, ssId) {
|
|
6475
6618
|
return __privateGet(this, _fecSequencer)?.rapAbove(packetId, catalogTrackName, ssId) ?? false;
|
|
6476
6619
|
}
|
|
6620
|
+
/** See FecReleaseSequencer.unsource. */
|
|
6621
|
+
unsourceFecRelease(packetId, catalogTrackName) {
|
|
6622
|
+
__privateGet(this, _fecSequencer)?.unsource(packetId, catalogTrackName);
|
|
6623
|
+
}
|
|
6624
|
+
/** See FecReleaseSequencer.holdsRebuilt. */
|
|
6625
|
+
fecReleaseHoldsRebuilt(packetId, catalogTrackName) {
|
|
6626
|
+
return __privateGet(this, _fecSequencer)?.holdsRebuilt(packetId, catalogTrackName) ?? false;
|
|
6627
|
+
}
|
|
6477
6628
|
/** Re-evaluate held gaps after FEC block state changed. */
|
|
6478
6629
|
pokeFecRelease(packetId, catalogTrackName) {
|
|
6479
6630
|
if (!__privateGet(this, _fecSequencer)) return;
|
|
@@ -6584,7 +6735,7 @@ var BlockcastMmt = (() => {
|
|
|
6584
6735
|
const now = Date.now();
|
|
6585
6736
|
for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
|
|
6586
6737
|
while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
|
|
6587
|
-
awaitingTiming.shift();
|
|
6738
|
+
__privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, awaitingTiming.shift(), "expired");
|
|
6588
6739
|
}
|
|
6589
6740
|
}
|
|
6590
6741
|
}
|
|
@@ -6789,6 +6940,10 @@ var BlockcastMmt = (() => {
|
|
|
6789
6940
|
}
|
|
6790
6941
|
__privateMethod(this, _MmtpContainerParser_instances, emitFrame_fn2).call(this, data, meta);
|
|
6791
6942
|
}
|
|
6943
|
+
/** Whether FT=1 metadata already names this sample, without claiming it. */
|
|
6944
|
+
hasAuthoredTimingFor(packetId, mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber, catalogTrackName) {
|
|
6945
|
+
return __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, __privateMethod(this, _MmtpContainerParser_instances, streamKey_fn).call(this, packetId, catalogTrackName), packetId, false)?.sampleTiming(mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber) != null;
|
|
6946
|
+
}
|
|
6792
6947
|
/**
|
|
6793
6948
|
* Claim and return the authored timing for a recovered sample. Recovery does
|
|
6794
6949
|
* not pass through the ordinary `feed()` completion path, so it must retire
|
|
@@ -6833,6 +6988,9 @@ var BlockcastMmt = (() => {
|
|
|
6833
6988
|
for (const { sequencer } of __privateGet(this, _sequencers).values()) sequencer.discard();
|
|
6834
6989
|
__privateGet(this, _sequencers).clear();
|
|
6835
6990
|
__privateGet(this, _fecSequencer)?.discard();
|
|
6991
|
+
for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
|
|
6992
|
+
for (const head of awaitingTiming) __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, head, "discarded");
|
|
6993
|
+
}
|
|
6836
6994
|
__privateGet(this, _timingStreams).clear();
|
|
6837
6995
|
__privateSet(this, _pendingTimingSamples, 0);
|
|
6838
6996
|
__privateGet(this, _reliableMoqStartupGate).clear();
|
|
@@ -6861,6 +7019,7 @@ var BlockcastMmt = (() => {
|
|
|
6861
7019
|
_reclaimedTimingSamples = new WeakMap();
|
|
6862
7020
|
_evictedTimingSamples = new WeakMap();
|
|
6863
7021
|
_rejectedMovieFragments = new WeakMap();
|
|
7022
|
+
_recoveredHeadsAwaitingTiming = new WeakMap();
|
|
6864
7023
|
_sequencers = new WeakMap();
|
|
6865
7024
|
_fecGate = new WeakMap();
|
|
6866
7025
|
_fecSequencer = new WeakMap();
|
|
@@ -7019,11 +7178,16 @@ var BlockcastMmt = (() => {
|
|
|
7019
7178
|
if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
|
|
7020
7179
|
return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-unexpected-fragmentation");
|
|
7021
7180
|
const resolver2 = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId, false);
|
|
7022
|
-
if (!resolver2) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-no-resolver");
|
|
7181
|
+
if (!resolver2?.initialized) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-no-resolver");
|
|
7023
7182
|
const pendingBefore = resolver2.pendingSampleCount;
|
|
7024
7183
|
const evictedBefore = resolver2.evictedSampleCount;
|
|
7025
|
-
const remaining = __privateGet(this, _maxPendingTimingSamples) -
|
|
7026
|
-
const admitted = resolver2.addMovieFragment(
|
|
7184
|
+
const remaining = __privateGet(this, _maxPendingTimingSamples) - resolver2.pendingSampleCount;
|
|
7185
|
+
const admitted = resolver2.addMovieFragment(
|
|
7186
|
+
mpu.mpuSequenceNumber,
|
|
7187
|
+
data,
|
|
7188
|
+
remaining,
|
|
7189
|
+
options?.recovered === true
|
|
7190
|
+
);
|
|
7027
7191
|
__privateSet(this, _pendingTimingSamples, __privateGet(this, _pendingTimingSamples) + (resolver2.pendingSampleCount - pendingBefore));
|
|
7028
7192
|
__privateSet(this, _evictedTimingSamples, __privateGet(this, _evictedTimingSamples) + (resolver2.evictedSampleCount - evictedBefore));
|
|
7029
7193
|
if (!admitted) {
|
|
@@ -7043,7 +7207,7 @@ var BlockcastMmt = (() => {
|
|
|
7043
7207
|
};
|
|
7044
7208
|
}
|
|
7045
7209
|
if (fragmentType !== 2) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "unknown-fragment-type");
|
|
7046
|
-
const resolver = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId,
|
|
7210
|
+
const resolver = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId, true);
|
|
7047
7211
|
if (!resolver) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-no-resolver");
|
|
7048
7212
|
if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
|
|
7049
7213
|
return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-empty-payload");
|
|
@@ -7064,6 +7228,7 @@ var BlockcastMmt = (() => {
|
|
|
7064
7228
|
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
7065
7229
|
movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
|
|
7066
7230
|
sampleNumber: mfuDuHeader.sampleNumber,
|
|
7231
|
+
sequenceNumber: info.sequenceNumber,
|
|
7067
7232
|
arrivedAt: Date.now()
|
|
7068
7233
|
});
|
|
7069
7234
|
return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-sample-timing-missing");
|
|
@@ -7084,7 +7249,7 @@ var BlockcastMmt = (() => {
|
|
|
7084
7249
|
headTiming.size
|
|
7085
7250
|
);
|
|
7086
7251
|
if (readyOnRegister) {
|
|
7087
|
-
const deferred = __privateMethod(this, _MmtpContainerParser_instances, emitCombinedSample_fn).call(this, readyOnRegister, info, resolver, options?.recovered === true, options?.fecSbn);
|
|
7252
|
+
const deferred = __privateMethod(this, _MmtpContainerParser_instances, emitCombinedSample_fn).call(this, readyOnRegister, info, resolver, options?.recovered === true, options?.fecSbn, options?.fecSbnFirst);
|
|
7088
7253
|
if (deferred.record === void 0) __privateMethod(this, _MmtpContainerParser_instances, countDeferredOutcome_fn).call(this, deferred.reason);
|
|
7089
7254
|
}
|
|
7090
7255
|
}
|
|
@@ -7171,7 +7336,7 @@ var BlockcastMmt = (() => {
|
|
|
7171
7336
|
fragmentCounter: mpu.fragmentCounter
|
|
7172
7337
|
};
|
|
7173
7338
|
}
|
|
7174
|
-
const outcome = __privateMethod(this, _MmtpContainerParser_instances, emitCombinedSample_fn).call(this, combined, info, resolver, options?.recovered === true, options?.fecSbn);
|
|
7339
|
+
const outcome = __privateMethod(this, _MmtpContainerParser_instances, emitCombinedSample_fn).call(this, combined, info, resolver, options?.recovered === true, options?.fecSbn, options?.fecSbnFirst);
|
|
7175
7340
|
if (outcome.record !== void 0) return outcome.record;
|
|
7176
7341
|
return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, outcome.reason);
|
|
7177
7342
|
};
|
|
@@ -7190,7 +7355,7 @@ var BlockcastMmt = (() => {
|
|
|
7190
7355
|
* attribution exists to keep readable. Leaving the choice at the call site
|
|
7191
7356
|
* is what keeps both true.
|
|
7192
7357
|
*/
|
|
7193
|
-
emitCombinedSample_fn = function(combined, info, resolver, recovered, fecSbn) {
|
|
7358
|
+
emitCombinedSample_fn = function(combined, info, resolver, recovered, fecSbn, fecSbnFirst) {
|
|
7194
7359
|
const timing = resolver.sampleTiming(
|
|
7195
7360
|
combined.mpuSequenceNumber,
|
|
7196
7361
|
combined.movieFragmentSequenceNumber,
|
|
@@ -7201,6 +7366,7 @@ var BlockcastMmt = (() => {
|
|
|
7201
7366
|
const meta = __privateMethod(this, _MmtpContainerParser_instances, buildFrameMeta_fn).call(this, combined, info.packetId, info.sequenceNumber, timing, recovered);
|
|
7202
7367
|
if (!meta) return { reason: "combined-meta-build-failed" };
|
|
7203
7368
|
if (fecSbn !== void 0) meta.fecSbn = fecSbn;
|
|
7369
|
+
if (fecSbnFirst !== void 0) meta.fecSbnFirst = fecSbnFirst;
|
|
7204
7370
|
if (!resolver.consumeSample(
|
|
7205
7371
|
combined.mpuSequenceNumber,
|
|
7206
7372
|
combined.movieFragmentSequenceNumber,
|
|
@@ -7245,10 +7411,23 @@ var BlockcastMmt = (() => {
|
|
|
7245
7411
|
return resolver;
|
|
7246
7412
|
};
|
|
7247
7413
|
awaitTiming_fn = function(streamKey, head) {
|
|
7248
|
-
const
|
|
7249
|
-
if (!
|
|
7414
|
+
const state = __privateGet(this, _timingStreams).get(streamKey);
|
|
7415
|
+
if (!state) return;
|
|
7416
|
+
if (!head.options?.recovered) {
|
|
7417
|
+
const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
|
|
7418
|
+
__privateGet(this, _recoveredHeadsAwaitingTiming).superseded += state.awaitingTiming.length - kept.length;
|
|
7419
|
+
state.awaitingTiming = kept;
|
|
7420
|
+
}
|
|
7421
|
+
const awaitingTiming = state.awaitingTiming;
|
|
7250
7422
|
awaitingTiming.push(head);
|
|
7251
|
-
if (
|
|
7423
|
+
if (head.options?.recovered) __privateGet(this, _recoveredHeadsAwaitingTiming).held++;
|
|
7424
|
+
if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
|
|
7425
|
+
__privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, awaitingTiming.shift(), "expired");
|
|
7426
|
+
}
|
|
7427
|
+
};
|
|
7428
|
+
/** Count a recovered head leaving the queue other than by replay or its source copy. */
|
|
7429
|
+
leaveAwaitingTiming_fn = function(head, outcome) {
|
|
7430
|
+
if (head.options?.recovered) __privateGet(this, _recoveredHeadsAwaitingTiming)[outcome]++;
|
|
7252
7431
|
};
|
|
7253
7432
|
/**
|
|
7254
7433
|
* Feed again the heads whose timing the FT=1 just admitted declares. That
|
|
@@ -7276,6 +7455,7 @@ var BlockcastMmt = (() => {
|
|
|
7276
7455
|
const state = __privateGet(this, _timingStreams).get(streamKey);
|
|
7277
7456
|
if (!state) return;
|
|
7278
7457
|
__privateGet(this, _timingStreams).delete(streamKey);
|
|
7458
|
+
for (const head of state.awaitingTiming) __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, head, "discarded");
|
|
7279
7459
|
__privateSet(this, _pendingTimingSamples, __privateGet(this, _pendingTimingSamples) - state.resolver.pendingSampleCount);
|
|
7280
7460
|
if (__privateGet(this, _pendingTimingSamples) < 0) {
|
|
7281
7461
|
throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");
|