@torrent-tv/proxy 2.101.18 → 2.101.20
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
|
@@ -43,8 +43,11 @@ export class Renditions {
|
|
|
43
43
|
*/
|
|
44
44
|
#moveWaitsSaid = new Map();
|
|
45
45
|
|
|
46
|
+
/** Viewers waiting for a preset's measured speed before a quality step up. @type {Set<string>} */
|
|
47
|
+
#presetSpeedWaitSaid = new Set();
|
|
48
|
+
|
|
46
49
|
/**
|
|
47
|
-
* @param {object} host - `viewerSecondsOn`, `audioStartSecondsFor`, `activeOutputFor`, `viewersOf`, `audioRenditionName`, `logger`, `placeViewer`, `viewerLeaves`, `createOrGetSession`, `planEncodersSoon`, `disposeSession`, `viewerPositionOf`, `encodeRuns`, `fileStartTimeReads`, `getCachedAudioTracks`, `getCachedMediaInfo`, `getContainerMediaInfo`, `localBaseUrl`, `outputTimes`, `outputs`, `quality`, `qualityOffer`, `segmentDurationSec`, `sourceFiles`, `viewers`, `generationOfRequest`, `givenOutputOf`, `noteGivenOutput`, `chosenOutputOf`, `chooseOutput`, `storedPieceReady`, `headersCompatible`, `audioChoiceOf`, `linkMbpsOf`, `qualityModeOf`, `noteServingVerdict`, `limitsFor`, `heightsChosenAs`, `highestGivenSegmentOf`, `sameHeightSwitchOf`, `noteSameHeightSwitch`, `switchingOnto`, `switchingFrom`, `segmentClosed`, `secondsUntilClosed`, `invalidateWaits`, `bufferedSecondsOf`, `minimumBufferSecondsFor`
|
|
50
|
+
* @param {object} host - `viewerSecondsOn`, `audioStartSecondsFor`, `activeOutputFor`, `viewersOf`, `audioRenditionName`, `logger`, `placeViewer`, `viewerLeaves`, `createOrGetSession`, `planEncodersSoon`, `disposeSession`, `viewerPositionOf`, `encodeRuns`, `fileStartTimeReads`, `getCachedAudioTracks`, `getCachedMediaInfo`, `getContainerMediaInfo`, `localBaseUrl`, `outputTimes`, `outputs`, `quality`, `qualityOffer`, `segmentDurationSec`, `sourceFiles`, `viewers`, `generationOfRequest`, `givenOutputOf`, `noteGivenOutput`, `chosenOutputOf`, `chooseOutput`, `storedPieceReady`, `headersCompatible`, `audioChoiceOf`, `linkMbpsOf`, `qualityModeOf`, `noteServingVerdict`, `limitsFor`, `heightsChosenAs`, `highestGivenSegmentOf`, `sameHeightSwitchOf`, `noteSameHeightSwitch`, `switchingOnto`, `switchingFrom`, `segmentClosed`, `secondsUntilClosed`, `invalidateWaits`, `bufferedSecondsOf`, `minimumBufferSecondsFor`, `processingSpeedOf`, `requiredSpeedOf`
|
|
48
51
|
*/
|
|
49
52
|
constructor(host) {
|
|
50
53
|
this.#host = host;
|
|
@@ -1052,6 +1055,25 @@ export class Renditions {
|
|
|
1052
1055
|
return;
|
|
1053
1056
|
}
|
|
1054
1057
|
}
|
|
1058
|
+
// A quality step up may choose a slower encoding preset. The benchmark is
|
|
1059
|
+
// only a forecast; keep the current output until this candidate has closed
|
|
1060
|
+
// pieces at the source's required speed. A step down is urgent and must
|
|
1061
|
+
// not wait on a measurement from the output meant to rescue playback.
|
|
1062
|
+
if (move.direction === "up" && move.changes === "preset" && prepared.spec?.transcodesVideo) {
|
|
1063
|
+
const speed = this.#host.processingSpeedOf?.(prepared) ?? null;
|
|
1064
|
+
const required = this.#host.requiredSpeedOf?.(prepared) ?? 1;
|
|
1065
|
+
if (!Number.isFinite(speed) || speed < required) {
|
|
1066
|
+
if (!this.#presetSpeedWaitSaid.has(consumerId)) {
|
|
1067
|
+
this.#presetSpeedWaitSaid.add(consumerId);
|
|
1068
|
+
this.#host.logger.info(
|
|
1069
|
+
`transcode ${base.id} keeps ${consumerId} on ${current.outputKey} while ${prepared.outputKey} ` +
|
|
1070
|
+
`proves its preset at ${Number.isFinite(speed) ? `${speed.toFixed(2)}x` : "an unmeasured speed"}; ` +
|
|
1071
|
+
`requires ${required.toFixed(2)}x`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1055
1077
|
// The player fetches the fMP4 init once and decodes every later fragment
|
|
1056
1078
|
// against it. Changing bitrate outputs is safe only when the prepared
|
|
1057
1079
|
// output's init says the same thing to that decoder. The new segment is
|
|
@@ -1151,6 +1173,7 @@ export class Renditions {
|
|
|
1151
1173
|
*/
|
|
1152
1174
|
#completeSameHeightSwitch(base, consumerId, move, prepared, current, next) {
|
|
1153
1175
|
this.#moveWaitsSaid.delete(consumerId);
|
|
1176
|
+
this.#presetSpeedWaitSaid.delete(consumerId);
|
|
1154
1177
|
for (const height of move.askedHeights) {
|
|
1155
1178
|
this.#choose(consumerId, height, prepared, base);
|
|
1156
1179
|
}
|
|
@@ -1189,6 +1212,7 @@ export class Renditions {
|
|
|
1189
1212
|
*/
|
|
1190
1213
|
#cancelSameHeightSwitch(base, consumerId, reason) {
|
|
1191
1214
|
this.#moveWaitsSaid.delete(consumerId);
|
|
1215
|
+
this.#presetSpeedWaitSaid.delete(consumerId);
|
|
1192
1216
|
const move = this.#host.sameHeightSwitchOf(consumerId);
|
|
1193
1217
|
if (!move) {
|
|
1194
1218
|
return;
|
|
@@ -430,7 +430,10 @@ export class QualityController {
|
|
|
430
430
|
// it is read as a copy.
|
|
431
431
|
const unchanged = standing && standingBefore &&
|
|
432
432
|
standing.height === standingBefore.height && standing.at === standingBefore.at;
|
|
433
|
-
|
|
433
|
+
const urgentMachineDown = unchanged && standing.urgent === true &&
|
|
434
|
+
standing.reason.startsWith("machine-bound ");
|
|
435
|
+
const machineRecovered = !urgentMachineDown || this.#roomToSpare(session, consumerId);
|
|
436
|
+
if (unchanged && !this.#affirmed.has(consumerId) && machineRecovered) {
|
|
434
437
|
this.#host.dropAskOf(base, consumerId);
|
|
435
438
|
logger.info(
|
|
436
439
|
`[budget] transcode ${base.id} no longer asks ${consumerId} for ${standing.height}p: ` +
|
|
@@ -457,6 +457,7 @@ export function wireOutputs({
|
|
|
457
457
|
presetLadder: () => (parts.encoders.current?.kind === "software" && Array.isArray(parts.softwarePresetBenchmark)
|
|
458
458
|
? parts.softwarePresetBenchmark.map((entry) => entry.preset) : []),
|
|
459
459
|
presetSpeedOf: (output, preset) => parts.encodeCost.presetSpeedOf(output, preset, { segmentSeconds: parts.segmentDurationSec }),
|
|
460
|
+
processingSpeedOf: (output) => parts.encodeCost.latestSpeedReadingOf(output)?.speed ?? null,
|
|
460
461
|
// The price a move was decided at, given to an output with no measured
|
|
461
462
|
// piece: one already measured stands on its readings, and an output that
|
|
462
463
|
// runs was priced with its own cost counted beside the one on screen.
|