@torrent-tv/proxy 2.100.14 → 2.101.0
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/bin/cli.js +2 -0
- package/package.json +1 -1
- package/server.js +4 -0
- package/services/encode/EncodeInputs.js +45 -5
- package/services/server/wire-outputs.js +7 -0
package/bin/cli.js
CHANGED
|
@@ -90,6 +90,7 @@ program
|
|
|
90
90
|
.option("--no-port-mapping", "Disable automatic UPnP/NAT-PMP port mapping")
|
|
91
91
|
.option("--delivery-sink", "Serve /api/delivery-sink, a torrent-free byte stream for delivery testing")
|
|
92
92
|
.option("--pin-preset <name>", "Encode every software picture at this libx264 preset only, for runs that must be compared at one quality (off by default)")
|
|
93
|
+
.option("--input-stretch-bytes <bytes>", "Size every original-input stretch to this many bytes instead of copy rate times open, for runs that compare input sizes; memory, downloaded bytes and the plan still bound it (off by default)")
|
|
93
94
|
.option("--heap-snapshots", "Write a heap snapshot of a thread whose memory reaches a new high-water; each one stops that thread while it is written (off by default)")
|
|
94
95
|
.option("--profile-start <seconds>", "Profile the CPU of the main thread and the torrent thread for this many seconds from the first source opened, written beside the state (off by default)")
|
|
95
96
|
.option(
|
|
@@ -399,6 +400,7 @@ try {
|
|
|
399
400
|
profileStartSeconds: Number(options.profileStart) || 0,
|
|
400
401
|
heapSnapshots: options.heapSnapshots === true,
|
|
401
402
|
pinnedPreset: typeof options.pinPreset === "string" && options.pinPreset.trim() ? options.pinPreset.trim() : null,
|
|
403
|
+
inputStretchBytes: Number(options.inputStretchBytes) > 0 ? Math.floor(Number(options.inputStretchBytes)) : null,
|
|
402
404
|
// Late-bound the same way `webRtcManager` is below: the torrent pool is
|
|
403
405
|
// built inside `startProxyServer`, before `dataChannelHandler` — the
|
|
404
406
|
// thing that actually owns a channel to push down — exists.
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -134,6 +134,9 @@ export async function startProxyServer({
|
|
|
134
134
|
// The one software preset every picture is encoded at, for runs compared at
|
|
135
135
|
// one quality; null leaves the choice to the machine.
|
|
136
136
|
pinnedPreset = null,
|
|
137
|
+
// The size every original-input stretch is given in place of copy rate
|
|
138
|
+
// times open, for runs that compare input sizes; null keeps the rule.
|
|
139
|
+
inputStretchBytes = null,
|
|
137
140
|
// What viewers' connections carry beyond the film, per byte of film; the
|
|
138
141
|
// transport counts it and is built after this server, so it is late-bound.
|
|
139
142
|
serviceShare = () => null,
|
|
@@ -854,6 +857,7 @@ export async function startProxyServer({
|
|
|
854
857
|
let sourcePreparation;
|
|
855
858
|
const outputParts = wireOutputs({
|
|
856
859
|
pinnedPreset,
|
|
860
|
+
inputStretchBytes,
|
|
857
861
|
indexMemory,
|
|
858
862
|
serviceShare,
|
|
859
863
|
readMetadataActivity: () => containerOrchestrator.activity(),
|
|
@@ -62,10 +62,20 @@ export class EncodeInputs {
|
|
|
62
62
|
* @type {(output: object, from: number, to: number, input: object) => void}
|
|
63
63
|
*/
|
|
64
64
|
#stretchCopied;
|
|
65
|
+
/**
|
|
66
|
+
* A stretch size the operator named for comparison runs
|
|
67
|
+
* (`--input-stretch-bytes`), in place of copy rate times open; null keeps
|
|
68
|
+
* the measured rule. Only the target changes: memory, downloaded bytes and
|
|
69
|
+
* the plan's bound still end a stretch (torrent-tv/meta#182).
|
|
70
|
+
*
|
|
71
|
+
* @type {number | null}
|
|
72
|
+
*/
|
|
73
|
+
#fixedStretchBytes;
|
|
65
74
|
|
|
66
75
|
constructor({ resolve, readRanges, heldRanges = null, reviseBudget, changed, failed, capacity = () => null, urgent = () => true,
|
|
67
|
-
rankAt = () => null, openOf = () => null, log = () => {}, now = Date.now, stretchCopied = () => {} }) {
|
|
76
|
+
rankAt = () => null, openOf = () => null, log = () => {}, now = Date.now, stretchCopied = () => {}, stretchBytes = null }) {
|
|
68
77
|
this.#stretchCopied = stretchCopied;
|
|
78
|
+
this.#fixedStretchBytes = Number.isSafeInteger(stretchBytes) && stretchBytes > 0 ? stretchBytes : null;
|
|
69
79
|
this.#openOf = openOf;
|
|
70
80
|
this.#resolve = resolve;
|
|
71
81
|
this.#read = readRanges;
|
|
@@ -337,9 +347,19 @@ export class EncodeInputs {
|
|
|
337
347
|
* @returns {{ bytes: number, openMeasured: boolean } | null}
|
|
338
348
|
*/
|
|
339
349
|
stretchFor(output) {
|
|
350
|
+
const sizing = this.#sizingFor(output);
|
|
351
|
+
return sizing ? { bytes: sizing.bytes, openMeasured: sizing.openMeasured } : null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#sizingFor(output) {
|
|
340
355
|
const rate = middleOf(this.#copyRates);
|
|
341
356
|
const open = this.#openOf(output);
|
|
342
|
-
|
|
357
|
+
if (this.#fixedStretchBytes !== null) {
|
|
358
|
+
return { bytes: this.#fixedStretchBytes, openMeasured: true, openSeconds: open?.seconds ?? null,
|
|
359
|
+
copyBytesPerMs: rate, fixed: true };
|
|
360
|
+
}
|
|
361
|
+
return rate === null || !open ? null : { bytes: Math.floor(rate * open.seconds * 1000),
|
|
362
|
+
openMeasured: open.measured, openSeconds: open.seconds, copyBytesPerMs: rate };
|
|
343
363
|
}
|
|
344
364
|
|
|
345
365
|
/**
|
|
@@ -493,6 +513,9 @@ export class EncodeInputs {
|
|
|
493
513
|
*/
|
|
494
514
|
async #admitStretch(request, firstSources) {
|
|
495
515
|
const { output, from } = request;
|
|
516
|
+
// Record existing decisions only: diagnostics must not resolve another
|
|
517
|
+
// interval or ask storage for an additional availability reading.
|
|
518
|
+
const probes = [], reservations = [];
|
|
496
519
|
let minimum = originalInputBytes(firstSources);
|
|
497
520
|
const stretches = new Map([[from, { sources: firstSources, bytes: minimum }]]);
|
|
498
521
|
const held = new Map();
|
|
@@ -508,7 +531,11 @@ export class EncodeInputs {
|
|
|
508
531
|
};
|
|
509
532
|
const fits = async (end, maxBytes) => {
|
|
510
533
|
const stretch = await at(end);
|
|
511
|
-
|
|
534
|
+
const withinLimit = Boolean(stretch) && stretch.bytes <= maxBytes;
|
|
535
|
+
const result = !stretch ? "unresolved" : !withinLimit ? "size"
|
|
536
|
+
: await this.#present(stretch.sources, held, request.timing) ? "fits" : "missing-bytes";
|
|
537
|
+
probes.push({ end, limitBytes: maxBytes, bytes: stretch?.bytes ?? null, result });
|
|
538
|
+
return result === "fits";
|
|
512
539
|
};
|
|
513
540
|
// Grows by doubling and then halves back: a handful of resolutions find
|
|
514
541
|
// the end whether it is a piece away or the rest of the film.
|
|
@@ -535,10 +562,11 @@ export class EncodeInputs {
|
|
|
535
562
|
}
|
|
536
563
|
return good;
|
|
537
564
|
};
|
|
538
|
-
const sizing = this
|
|
565
|
+
const sizing = this.#sizingFor(output);
|
|
539
566
|
const target = sizing?.bytes ?? null;
|
|
540
567
|
if (sizing && !sizing.openMeasured) request.timing.openApproximate = true;
|
|
541
568
|
let end = target === null || this.#heldRanges === null ? floor : await longest(target);
|
|
569
|
+
const targetEnd = end;
|
|
542
570
|
if (this.#requests.get(request.key) !== request) return undefined;
|
|
543
571
|
this.#wanted.set(request.key, { wanted: stretches.get(end).bytes, required: minimum });
|
|
544
572
|
await this.#spent(request.timing, "budgetMs", () => this.#revise());
|
|
@@ -576,7 +604,13 @@ export class EncodeInputs {
|
|
|
576
604
|
if (this.#requests.get(request.key) !== request) return undefined;
|
|
577
605
|
this.#wanted.set(request.key, { wanted: chosen.bytes, required: chosen.bytes });
|
|
578
606
|
result = await admitOriginalInput({ sources: chosen.sources, readRanges: this.#read, now: this.#now,
|
|
579
|
-
reserve: async bytes =>
|
|
607
|
+
reserve: async bytes => {
|
|
608
|
+
const reservation = { bytes, allowed: this.#allowed, held: this.#held, capacity,
|
|
609
|
+
room: roomNow(), end };
|
|
610
|
+
const release = this.#hold(request, bytes);
|
|
611
|
+
reservations.push({ ...reservation, admitted: typeof release === "function" });
|
|
612
|
+
return release;
|
|
613
|
+
} });
|
|
580
614
|
// Another output's copy can take the room this one was sized for while
|
|
581
615
|
// it was being chosen. A shorter stretch is tried at once rather than
|
|
582
616
|
// waiting for a memory change that may never come.
|
|
@@ -585,6 +619,12 @@ export class EncodeInputs {
|
|
|
585
619
|
}
|
|
586
620
|
if (result.kind === "needs-memory") this.#wanted.set(request.key, { wanted: minimum, required: minimum });
|
|
587
621
|
if (result.kind === "result") {
|
|
622
|
+
this.#log(`encode input selection of ${addressOf(output)}: ${JSON.stringify({
|
|
623
|
+
from, requestedTo: request.to, serves, floor, targetEnd, chosenEnd: end,
|
|
624
|
+
targetBytes: target, targetFixed: sizing?.fixed === true, minimumBytes: minimum, inputBytes: result.bytes,
|
|
625
|
+
copyBytesPerMs: sizing?.copyBytesPerMs ?? null, openSeconds: sizing?.openSeconds ?? null,
|
|
626
|
+
openMeasured: sizing?.openMeasured ?? null, reservations, probes
|
|
627
|
+
})}`);
|
|
588
628
|
Object.assign(result, { from, to: end });
|
|
589
629
|
// The stretch is copied after the answer, while the run opens; the rate
|
|
590
630
|
// is the whole copy's, learned once it is done.
|
|
@@ -195,6 +195,9 @@ export function wireOutputs({
|
|
|
195
195
|
// A software preset every picture is encoded at, for runs compared at one
|
|
196
196
|
// quality (`--pin-preset`); null leaves the choice to the machine.
|
|
197
197
|
pinnedPreset = null,
|
|
198
|
+
// A size every original-input stretch is given in place of copy rate
|
|
199
|
+
// times open (`--input-stretch-bytes`); null keeps the rule.
|
|
200
|
+
inputStretchBytes = null,
|
|
198
201
|
// What every encoder this host may use was qualified and measured to do at
|
|
199
202
|
// startup, by kind (`encode/calibration.js`). Takes the place of the one
|
|
200
203
|
// array above, which a test may still hand in on its own.
|
|
@@ -1097,6 +1100,7 @@ export function wireOutputs({
|
|
|
1097
1100
|
readRanges: readEncodeRanges,
|
|
1098
1101
|
heldRanges: readEncodeHeldRanges,
|
|
1099
1102
|
stretchCopied,
|
|
1103
|
+
stretchBytes: inputStretchBytes,
|
|
1100
1104
|
reviseBudget: () => parts.machineBudget.revise("memory", "admitted encode inputs"),
|
|
1101
1105
|
capacity: () => parts.machineBudget.capacityOf("memory"),
|
|
1102
1106
|
// How long this output's input takes to open, from the one owner of start
|
|
@@ -1120,6 +1124,9 @@ export function wireOutputs({
|
|
|
1120
1124
|
parts.serving.invalidateWaits(output);
|
|
1121
1125
|
}
|
|
1122
1126
|
}) : null);
|
|
1127
|
+
if (parts.encodeInputs && inputStretchBytes) {
|
|
1128
|
+
logger.info(`encode input: every original-input stretch sized to ${inputStretchBytes} bytes (--input-stretch-bytes)`);
|
|
1129
|
+
}
|
|
1123
1130
|
// One owner of the disk, and the list of what takes it lives with the owner.
|
|
1124
1131
|
parts.machineBudget = wireMachineBudget({
|
|
1125
1132
|
segmentStore: parts.segmentStore,
|