@torrent-tv/proxy 2.95.37 → 2.95.39
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
|
@@ -2,6 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { matroskaInput, matroskaTrackIdentity } from "./MatroskaInput.js";
|
|
3
3
|
import { pcmNormalization, normalizePcmSlices } from "./pcm-normalization.js";
|
|
4
4
|
|
|
5
|
+
const INPUT_WRITE_BATCH_BYTES = 64 * 1024;
|
|
6
|
+
|
|
5
7
|
/** Acquire complete source ranges before any process can consume the input. */
|
|
6
8
|
export async function admitInput({ sources, reserve, readRanges }) {
|
|
7
9
|
if (!Array.isArray(sources) || sources.length === 0 || typeof reserve !== "function" || typeof readRanges !== "function") {
|
|
@@ -106,27 +108,37 @@ export async function writeAdmittedInput(input, stdin, { next = null } = {}) {
|
|
|
106
108
|
let current = input, includeHeader = true;
|
|
107
109
|
try {
|
|
108
110
|
while (current) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
if (JSON.stringify(current.tracks.map(input => matroskaTrackIdentity(input.track))) !== declarations) {
|
|
112
|
+
throw new Error("Consecutive encoder input changed its declared tracks.");
|
|
113
|
+
}
|
|
114
|
+
for (let index = 0; index < current.tracks.length; index++) {
|
|
115
|
+
const end = current.tracks[index].sourceEndSeconds;
|
|
116
|
+
if (Number.isFinite(end)) input.tracks[index].sourceEndSeconds = end;
|
|
117
|
+
}
|
|
118
|
+
let batch = Buffer.allocUnsafe(INPUT_WRITE_BATCH_BYTES);
|
|
119
|
+
let used = 0;
|
|
120
|
+
const flush = async () => {
|
|
121
|
+
if (used === 0) return;
|
|
122
|
+
const chunk = batch.subarray(0, used);
|
|
123
|
+
batch = Buffer.allocUnsafe(INPUT_WRITE_BATCH_BYTES);
|
|
124
|
+
used = 0;
|
|
125
|
+
await writeChunk(stdin, chunk);
|
|
126
|
+
};
|
|
127
|
+
for await (const chunk of current.stream({ originSeconds, includeHeader, seen })) {
|
|
128
|
+
if (!Buffer.isBuffer(chunk)) throw new TypeError("Admitted input stream yielded a non-buffer chunk.");
|
|
129
|
+
let offset = 0;
|
|
130
|
+
while (offset < chunk.length) {
|
|
131
|
+
const copied = Math.min(INPUT_WRITE_BATCH_BYTES - used, chunk.length - offset);
|
|
132
|
+
chunk.copy(batch, used, offset, offset + copied);
|
|
133
|
+
used += copied;
|
|
134
|
+
offset += copied;
|
|
135
|
+
if (used === INPUT_WRITE_BATCH_BYTES) await flush();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
await flush();
|
|
139
|
+
current.release();
|
|
140
|
+
current = next ? await next() : null;
|
|
141
|
+
includeHeader = false;
|
|
130
142
|
}
|
|
131
143
|
stdin.end();
|
|
132
144
|
} finally {
|
|
@@ -134,3 +146,15 @@ export async function writeAdmittedInput(input, stdin, { next = null } = {}) {
|
|
|
134
146
|
input.release();
|
|
135
147
|
}
|
|
136
148
|
}
|
|
149
|
+
|
|
150
|
+
function writeChunk(stdin, chunk) {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const failed = error => { stdin.off("error", failed); reject(error); };
|
|
153
|
+
stdin.once("error", failed);
|
|
154
|
+
stdin.write(chunk, error => {
|
|
155
|
+
stdin.off("error", failed);
|
|
156
|
+
if (error) reject(error);
|
|
157
|
+
else resolve();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
@@ -1682,13 +1682,15 @@ export function pickSoftwarePreset(benchmark, pixelsPerSecNeeded, cost = {}, fra
|
|
|
1682
1682
|
if (encodePixelsPerSec === null) {
|
|
1683
1683
|
continue;
|
|
1684
1684
|
}
|
|
1685
|
-
const speed =
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1685
|
+
const speed = typeof cost.speedForPreset === "function"
|
|
1686
|
+
? cost.speedForPreset([entry], frame, pixelsPerSecNeeded)
|
|
1687
|
+
: predictedRealtimeSpeed({
|
|
1688
|
+
decodeModel: cost.decodeModel ?? null,
|
|
1689
|
+
encodePixelsPerSec,
|
|
1690
|
+
outputPixelsPerSec: pixelsPerSecNeeded,
|
|
1691
|
+
source: cost.source ?? null,
|
|
1692
|
+
observedDecodeCostSec: observed
|
|
1693
|
+
});
|
|
1692
1694
|
if (speed !== null && speed >= bar) {
|
|
1693
1695
|
return entry.preset;
|
|
1694
1696
|
}
|
|
@@ -1786,12 +1788,15 @@ export function chooseSoftwareEncodeSettings(benchmark, ceiling, outputFps, cost
|
|
|
1786
1788
|
if (fastest === null) {
|
|
1787
1789
|
continue;
|
|
1788
1790
|
}
|
|
1789
|
-
const
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1791
|
+
const outputPixelsPerSec = ladder[i].width * ladder[i].height * fps;
|
|
1792
|
+
const speed = typeof cost.speedForPreset === "function"
|
|
1793
|
+
? cost.speedForPreset([benchmark.at(-1)], ladder[i], outputPixelsPerSec)
|
|
1794
|
+
: predictedRealtimeSpeed({
|
|
1795
|
+
decodeModel: cost.decodeModel ?? null,
|
|
1796
|
+
encodePixelsPerSec: fastest,
|
|
1797
|
+
outputPixelsPerSec,
|
|
1798
|
+
source: cost.source ?? null
|
|
1799
|
+
});
|
|
1795
1800
|
if (speed !== null && speed >= bar) {
|
|
1796
1801
|
chosenIndex = i;
|
|
1797
1802
|
break;
|
|
@@ -172,6 +172,21 @@ export class EncodeCost {
|
|
|
172
172
|
);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
#committedMachineCostSec() {
|
|
176
|
+
let cost = 0;
|
|
177
|
+
for (const session of this.#outputs.values()) {
|
|
178
|
+
if (!processCanBeSignalled(this.#stateFor(session))) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const measured = this.#loadOfSession(session)?.costSec;
|
|
182
|
+
if (!Number.isFinite(measured) || measured <= 0) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
cost += measured;
|
|
186
|
+
}
|
|
187
|
+
return cost;
|
|
188
|
+
}
|
|
189
|
+
|
|
175
190
|
// The last refusal printed. The offer is recomputed on the path that serves
|
|
176
191
|
// every playlist, init and segment, and the figures behind it move every few
|
|
177
192
|
// seconds — so the line is written when the ANSWER changes, not when it is
|
|
@@ -1085,11 +1100,26 @@ export class EncodeCost {
|
|
|
1085
1100
|
if (!ceiling) {
|
|
1086
1101
|
return null;
|
|
1087
1102
|
}
|
|
1103
|
+
const concurrentCostSec = this.#committedMachineCostSec();
|
|
1104
|
+
const speedForPreset = (benchmark, frame, outputPixelsPerSec) => {
|
|
1105
|
+
if (concurrentCostSec === null) {
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
return this.#speedOnThisMachine({
|
|
1109
|
+
benchmark,
|
|
1110
|
+
source,
|
|
1111
|
+
width: frame.width,
|
|
1112
|
+
height: frame.height,
|
|
1113
|
+
fps: outputPixelsPerSec / (frame.width * frame.height),
|
|
1114
|
+
observedDecodeCostSec: null,
|
|
1115
|
+
concurrentCostSec
|
|
1116
|
+
});
|
|
1117
|
+
};
|
|
1088
1118
|
return chooseSoftwareEncodeSettings(
|
|
1089
1119
|
this.#host().benchmark,
|
|
1090
1120
|
{ width: ceiling.w, height: ceiling.h },
|
|
1091
1121
|
outputFps,
|
|
1092
|
-
{ decodeModel: this.#host().decodeModel, source, requiredSpeed }
|
|
1122
|
+
{ decodeModel: this.#host().decodeModel, source, requiredSpeed, speedForPreset }
|
|
1093
1123
|
);
|
|
1094
1124
|
}
|
|
1095
1125
|
}
|