@torrent-tv/proxy 2.101.39 → 2.101.41
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/package.json
CHANGED
|
@@ -195,11 +195,12 @@ export class PacketIndex {
|
|
|
195
195
|
const ordered = packets.ptsAscending;
|
|
196
196
|
const scanFrom = ordered ? packets.firstAtOrAfter(startAt - packets.longestDuration) : 0;
|
|
197
197
|
const scanTo = ordered ? packets.firstAtOrAfter(to) : packets.length;
|
|
198
|
-
|
|
199
|
-
const pts = packets.ptsAt(index);
|
|
198
|
+
const consider = (index, pts = packets.ptsAt(index)) => {
|
|
200
199
|
if (pts < to && (pts >= startAt ||
|
|
201
200
|
((mode === "transcode" || track.type === "audio") && pts + packets.durationAt(index) > startAt))) selected.push(index);
|
|
202
|
-
}
|
|
201
|
+
};
|
|
202
|
+
if (ordered) for (let index = scanFrom; index < scanTo; index++) consider(index);
|
|
203
|
+
else packets.forEachInTimeRange(startAt, to, consider);
|
|
203
204
|
if (selected.length === 0) {
|
|
204
205
|
if (track.type === "subtitle" && (track.complete || track.coveredThrough >= to)) {
|
|
205
206
|
return { kind: "result", trackId, ranges: [], packets: [], from, to, decodeFrom: from };
|
|
@@ -221,7 +222,7 @@ export class PacketIndex {
|
|
|
221
222
|
? { kind: "terminal", reason: "video-interval-has-no-decode-start" }
|
|
222
223
|
: { kind: "needs-index", trackId, from: startAt, to };
|
|
223
224
|
let endsAtKeyframe = false;
|
|
224
|
-
if (mode === "copy")
|
|
225
|
+
if (mode === "copy") endsAtKeyframe = packets.someAtTime(to, index => packets.keyframeAt(index));
|
|
225
226
|
if (mode === "copy" && (packets.ptsAt(first) !== from ||
|
|
226
227
|
(!endsAtKeyframe && (!track.complete || to < this.boundsOf(trackId).end)))) {
|
|
227
228
|
return { kind: "terminal", reason: "video-copy-cut-is-not-a-keyframe" };
|
|
@@ -16,6 +16,8 @@ const HEADER_BYTES = 56;
|
|
|
16
16
|
|
|
17
17
|
export class PacketRecords {
|
|
18
18
|
#blocks = [];
|
|
19
|
+
// Conservative time bounds for each existing storage block.
|
|
20
|
+
#timeBounds = [];
|
|
19
21
|
#addresses = [];
|
|
20
22
|
#used = 0;
|
|
21
23
|
#length = 0;
|
|
@@ -75,6 +77,7 @@ export class PacketRecords {
|
|
|
75
77
|
const previousBytes = this.#bytes;
|
|
76
78
|
if (value === 0) {
|
|
77
79
|
this.#blocks = [];
|
|
80
|
+
this.#timeBounds = [];
|
|
78
81
|
this.#addresses = [];
|
|
79
82
|
this.#used = 0;
|
|
80
83
|
this.#bytes = 0;
|
|
@@ -85,6 +88,8 @@ export class PacketRecords {
|
|
|
85
88
|
const at = address.readUInt32LE(addressAt % BLOCK_BYTES + 4);
|
|
86
89
|
const block = this.#blocks[blockIndex];
|
|
87
90
|
this.#blocks.length = blockIndex + 1;
|
|
91
|
+
this.#timeBounds.length = blockIndex + 1;
|
|
92
|
+
this.#timeBounds[blockIndex].end = value;
|
|
88
93
|
this.#addresses.length = Math.ceil(value * ADDRESS_BYTES / BLOCK_BYTES);
|
|
89
94
|
// The last address block back to the size its records need, as it grew.
|
|
90
95
|
const lastAddresses = this.#addresses.at(-1);
|
|
@@ -121,6 +126,27 @@ export class PacketRecords {
|
|
|
121
126
|
return low;
|
|
122
127
|
}
|
|
123
128
|
|
|
129
|
+
/** Visit stored records whose presentation intervals can overlap [from, to). */
|
|
130
|
+
forEachInTimeRange(from, to, visit) {
|
|
131
|
+
for (const bounds of this.#timeBounds) {
|
|
132
|
+
if (bounds.minPts >= to || (bounds.maxPts < from && bounds.maxEnd <= from)) continue;
|
|
133
|
+
for (let index = bounds.first; index < bounds.end; index++) {
|
|
134
|
+
const pts = this.ptsAt(index);
|
|
135
|
+
if (pts < to && (pts >= from || pts + this.durationAt(index) > from)) visit(index, pts);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
someAtTime(seconds, matches) {
|
|
141
|
+
for (const bounds of this.#timeBounds) {
|
|
142
|
+
if (seconds < bounds.minPts || seconds > bounds.maxPts) continue;
|
|
143
|
+
for (let index = bounds.first; index < bounds.end; index++) {
|
|
144
|
+
if (this.ptsAt(index) === seconds && matches(index)) return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
124
150
|
push(packet) {
|
|
125
151
|
if (this.#disposed) throw new Error("Packet records were released.");
|
|
126
152
|
if (!Number.isFinite(packet.pts) || !Array.isArray(packet.ranges) ||
|
|
@@ -161,6 +187,8 @@ export class PacketRecords {
|
|
|
161
187
|
if (newBlock) {
|
|
162
188
|
block = payloadAllocation;
|
|
163
189
|
this.#blocks.push(block);
|
|
190
|
+
this.#timeBounds.push({ first: this.#length, end: this.#length, minPts: Infinity, maxPts: -Infinity,
|
|
191
|
+
maxEnd: -Infinity });
|
|
164
192
|
this.#bytes += block.length;
|
|
165
193
|
this.#used = 0;
|
|
166
194
|
}
|
|
@@ -186,6 +214,11 @@ export class PacketRecords {
|
|
|
186
214
|
if (packet.streamId !== undefined) block.writeUInt32LE(packet.streamId, at + bytes - 4 - (packet.decodeFromIndex !== undefined ? 4 : 0));
|
|
187
215
|
if (packet.decodeFromIndex !== undefined) block.writeUInt32LE(packet.decodeFromIndex, at + bytes - 4);
|
|
188
216
|
this.#used += bytes;
|
|
217
|
+
const timeBounds = this.#timeBounds.at(-1);
|
|
218
|
+
timeBounds.end = this.#length + 1;
|
|
219
|
+
timeBounds.minPts = Math.min(timeBounds.minPts, packet.pts);
|
|
220
|
+
timeBounds.maxPts = Math.max(timeBounds.maxPts, packet.pts);
|
|
221
|
+
if (Number.isFinite(packet.duration)) timeBounds.maxEnd = Math.max(timeBounds.maxEnd, packet.pts + packet.duration);
|
|
189
222
|
if (this.#descentAt < 0 && packet.pts < this.#lastPts) this.#descentAt = this.#length;
|
|
190
223
|
this.#lastPts = packet.pts;
|
|
191
224
|
if (packet.duration > this.#longest) this.#longest = packet.duration;
|
|
@@ -258,6 +291,10 @@ export class PacketRecords {
|
|
|
258
291
|
const [block, at] = this.#address(this.#length - 1);
|
|
259
292
|
const duration = block.readDoubleLE(at + 8) + seconds;
|
|
260
293
|
block.writeDoubleLE(duration, at + 8);
|
|
294
|
+
const address = this.#addresses[Math.floor((this.#length - 1) * ADDRESS_BYTES / BLOCK_BYTES)];
|
|
295
|
+
const blockIndex = address.readUInt32LE(((this.#length - 1) * ADDRESS_BYTES) % BLOCK_BYTES);
|
|
296
|
+
const bounds = this.#timeBounds[blockIndex];
|
|
297
|
+
bounds.maxEnd = Math.max(bounds.maxEnd, block.readDoubleLE(at) + duration);
|
|
261
298
|
if (duration > this.#longest) this.#longest = duration;
|
|
262
299
|
}
|
|
263
300
|
|
|
@@ -267,6 +304,10 @@ export class PacketRecords {
|
|
|
267
304
|
}
|
|
268
305
|
const [block, at] = this.#address(index);
|
|
269
306
|
block.writeDoubleLE(seconds, at + 8);
|
|
307
|
+
const address = this.#addresses[Math.floor(index * ADDRESS_BYTES / BLOCK_BYTES)];
|
|
308
|
+
const blockIndex = address.readUInt32LE((index * ADDRESS_BYTES) % BLOCK_BYTES);
|
|
309
|
+
const bounds = this.#timeBounds[blockIndex];
|
|
310
|
+
bounds.maxEnd = Math.max(bounds.maxEnd, block.readDoubleLE(at) + seconds);
|
|
270
311
|
if (seconds > this.#longest) this.#longest = seconds;
|
|
271
312
|
const flags = block.readUInt32LE(at + 48);
|
|
272
313
|
block.writeUInt32LE(derived ? flags | 4 : flags & ~4, at + 48);
|
|
@@ -107,6 +107,21 @@ export class PacketRecordsChain {
|
|
|
107
107
|
return low;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
forEachInTimeRange(from, to, visit) {
|
|
111
|
+
for (let part = 0; part < this.#parts.length; part++) {
|
|
112
|
+
const offset = this.#offsets[part];
|
|
113
|
+
this.#parts[part].forEachInTimeRange(from, to, (index, pts) => visit(offset + index, pts));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
someAtTime(seconds, matches) {
|
|
118
|
+
for (let part = 0; part < this.#parts.length; part++) {
|
|
119
|
+
const offset = this.#offsets[part];
|
|
120
|
+
if (this.#parts[part].someAtTime(seconds, index => matches(offset + index))) return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
/** The tables are the container's: a request's index lets go of nothing. */
|
|
111
126
|
dispose() {}
|
|
112
127
|
releaseUnusedCapacity() {}
|
|
@@ -999,8 +999,13 @@ export class ViewerRequests {
|
|
|
999
999
|
} });
|
|
1000
1000
|
if (waitEstimate) forecast.waitEstimate = waitEstimate;
|
|
1001
1001
|
}
|
|
1002
|
-
|
|
1002
|
+
const unavailableInput = forecast.unavailableSource;
|
|
1003
|
+
const forecastInputKey = unavailableInput ? JSON.stringify([
|
|
1004
|
+
unavailableInput.sourceId, unavailableInput.trackId, unavailableInput.segmentIndex, unavailableInput.phase
|
|
1005
|
+
]) : null;
|
|
1006
|
+
if (measurement.forecastReason !== forecast.reason || measurement.forecastInputKey !== forecastInputKey) {
|
|
1003
1007
|
measurement.forecastReason = forecast.reason;
|
|
1008
|
+
measurement.forecastInputKey = forecastInputKey;
|
|
1004
1009
|
logger.info(`playback readiness ${session.id} consumer=${consumerId} ${JSON.stringify({
|
|
1005
1010
|
...forecast,
|
|
1006
1011
|
positionSeconds,
|
|
@@ -258,6 +258,7 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
258
258
|
}
|
|
259
259
|
const link = rateCurve(input.linkReadings, now);
|
|
260
260
|
let unknownReason = null;
|
|
261
|
+
let unavailableSource = null;
|
|
261
262
|
if (!link) {
|
|
262
263
|
return result(false, null, buffered, reserve, null, "link-rate-unavailable", 0);
|
|
263
264
|
}
|
|
@@ -323,7 +324,10 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
323
324
|
} else if (source.source.complete !== true) {
|
|
324
325
|
const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
|
|
325
326
|
if (ranges === null) {
|
|
326
|
-
unknownReason
|
|
327
|
+
if (unknownReason === null) {
|
|
328
|
+
unknownReason = "source-input-ranges-unavailable";
|
|
329
|
+
unavailableSource = { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "near" };
|
|
330
|
+
}
|
|
327
331
|
} else {
|
|
328
332
|
if (sourceByteIntervals === NO_SOURCE_BYTES) sourceByteIntervals = new Map();
|
|
329
333
|
sourceByteIntervals.set(sourceId, ranges);
|
|
@@ -348,7 +352,8 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
348
352
|
}
|
|
349
353
|
|
|
350
354
|
if (unknownReason) {
|
|
351
|
-
return result(false, null, buffered, reserve, null, unknownReason, preparedSegments)
|
|
355
|
+
return { ...result(false, null, buffered, reserve, null, unknownReason, preparedSegments),
|
|
356
|
+
...(unavailableSource ? { unavailableSource } : {}) };
|
|
352
357
|
}
|
|
353
358
|
for (const { track, curve } of trackState) {
|
|
354
359
|
if (jobs.some((job) => job.track === track && !job.produced) && !curve) {
|
|
@@ -671,7 +676,8 @@ function scheduleTail({ trackState, sourceState, sourceByteCoverage, sourceArriv
|
|
|
671
676
|
if (!source) return { reason: "source-measurement-unavailable" };
|
|
672
677
|
if (source.source.complete === true) continue;
|
|
673
678
|
const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
|
|
674
|
-
if (ranges === null) return { reason: "source-input-ranges-unavailable"
|
|
679
|
+
if (ranges === null) return { reason: "source-input-ranges-unavailable",
|
|
680
|
+
unavailableSource: { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "tail" } };
|
|
675
681
|
const bytes = supplyOf(sourceId);
|
|
676
682
|
let order = 0;
|
|
677
683
|
for (const range of ranges) {
|