@torrent-tv/proxy 2.107.7 → 2.107.9
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
|
@@ -905,7 +905,7 @@ export class ViewerRequests {
|
|
|
905
905
|
const tracks = [];
|
|
906
906
|
for (const output of outputs) {
|
|
907
907
|
const trackProgress = output === session ? progress : this.#host.encodeRuns.progressOf(output);
|
|
908
|
-
const trackRates = this.#trackRateReadings(output, now);
|
|
908
|
+
const trackRates = this.#trackRateReadings(output, now, trackProgress, measurement);
|
|
909
909
|
const trackName = output === session ? "video" : "audio";
|
|
910
910
|
// The page states the offset its player applied to this track, and the
|
|
911
911
|
// init it applied it to is the session's. Until both are known, the
|
|
@@ -1244,7 +1244,8 @@ export class ViewerRequests {
|
|
|
1244
1244
|
const id = typeof consumerId === "string" ? consumerId : "";
|
|
1245
1245
|
let state = byViewer.get(id);
|
|
1246
1246
|
if (!state) {
|
|
1247
|
-
state = { link: new this.#host.playbackReadiness.RateTrend(this.#host.lookaheadSeconds), sources: new Map()
|
|
1247
|
+
state = { link: new this.#host.playbackReadiness.RateTrend(this.#host.lookaheadSeconds), sources: new Map(),
|
|
1248
|
+
trackProgress: new WeakMap() };
|
|
1248
1249
|
byViewer.set(id, state);
|
|
1249
1250
|
}
|
|
1250
1251
|
return state;
|
|
@@ -1257,23 +1258,37 @@ export class ViewerRequests {
|
|
|
1257
1258
|
* runs of one output can report out of order, so a summary kept and added to
|
|
1258
1259
|
* would drop the later-told, earlier-taken one (torrent-tv/meta#178).
|
|
1259
1260
|
*/
|
|
1260
|
-
#trackRateReadings(output, now) {
|
|
1261
|
+
#trackRateReadings(output, now, progress, measurement) {
|
|
1261
1262
|
const readings = (this.#host.encodeSpeedReadingsOf?.(output) ?? []).filter(reading => reading.at <= now);
|
|
1262
1263
|
const summary = readings.length > 0 ? this.#host.playbackReadiness.rateSummaryOf(readings) : null;
|
|
1263
|
-
if (
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1264
|
+
if (summary) return summary;
|
|
1265
|
+
// ONLY UNTIL A PIECE IS MEASURED does the prediction stand for the
|
|
1266
|
+
// encoder, and it is not kept among the readings. Added on every poll
|
|
1267
|
+
// between runs, it put the machine's share of that moment — 1 % at a
|
|
1268
|
+
// cold start — on the whole film, and the forecast read 0.03x
|
|
1269
|
+
// (torrent-tv/meta#178).
|
|
1270
|
+
const projected = Number(this.#host.predictedEncodeSpeedOf?.(output) ?? this.#host.projectedEncodeSpeedOf?.(output));
|
|
1271
|
+
if (Number.isFinite(projected) && projected > 0) {
|
|
1272
|
+
const seed = new this.#host.playbackReadiness.RateTrend();
|
|
1273
|
+
seed.add(now, projected);
|
|
1274
|
+
return seed.snapshot();
|
|
1275
|
+
}
|
|
1276
|
+
// A separate soundtrack may have no calibrated codec reading and no
|
|
1277
|
+
// completed segment yet. Its progress is still a measured rate: retain the
|
|
1278
|
+
// time since the previous advance so source waits reduce, rather than
|
|
1279
|
+
// inflate, the speed used by the forecast.
|
|
1280
|
+
if (output?.spec?.carries === "audio-only") {
|
|
1281
|
+
let trend = measurement.trackProgress.get(output);
|
|
1282
|
+
if (!trend) {
|
|
1283
|
+
trend = new this.#host.playbackReadiness.ProgressRate(
|
|
1284
|
+
this.#host.lookaheadSeconds,
|
|
1285
|
+
this.#host.segmentDurationSec
|
|
1286
|
+
);
|
|
1287
|
+
measurement.trackProgress.set(output, trend);
|
|
1274
1288
|
}
|
|
1289
|
+
return trend.note(now, progress?.processedSeconds, progress?.startPositionSeconds);
|
|
1275
1290
|
}
|
|
1276
|
-
return
|
|
1291
|
+
return null;
|
|
1277
1292
|
}
|
|
1278
1293
|
|
|
1279
1294
|
/**
|
|
@@ -72,7 +72,7 @@ import { ViewerRequests } from "./ViewerRequests.js";
|
|
|
72
72
|
import { OutputLifecycle } from "./OutputLifecycle.js";
|
|
73
73
|
import { SegmentServing } from "./SegmentServing.js";
|
|
74
74
|
import { audioStartSecondsFor } from "../viewer/audio-start.js";
|
|
75
|
-
import { forecastRate, rateSummaryOf, predictPlaybackReadiness, RateTrend } from "../viewer/playback-readiness.js";
|
|
75
|
+
import { forecastRate, rateSummaryOf, predictPlaybackReadiness, ProgressRate, RateTrend } from "../viewer/playback-readiness.js";
|
|
76
76
|
import { audioRenditionName } from "../media/audio-inventory.js";
|
|
77
77
|
import { Renditions } from "../encode/Renditions.js";
|
|
78
78
|
import { CushionReport, LOOKAHEAD_PAUSE_SECONDS } from "../encode/CushionReport.js";
|
|
@@ -640,7 +640,7 @@ export function wireOutputs({
|
|
|
640
640
|
get encodeRuns() { return parts.encodeRuns; },
|
|
641
641
|
// When this viewer's playback can start and run to the end: the viewer
|
|
642
642
|
// component's forecast, which models the viewer's browser.
|
|
643
|
-
playbackReadiness: { predict: predictPlaybackReadiness, forecastRate, rateSummaryOf, RateTrend },
|
|
643
|
+
playbackReadiness: { predict: predictPlaybackReadiness, forecastRate, rateSummaryOf, RateTrend, ProgressRate },
|
|
644
644
|
// Whether the subtitle a viewer chose has been read where they stand — the
|
|
645
645
|
// source preparation's answer, set on these parts by the assembly.
|
|
646
646
|
subtitleReadyFor: (viewer) => parts.subtitleReadyFor?.(viewer) ?? true,
|
|
@@ -108,6 +108,44 @@ export class RateTrend {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Rates the media an output actually produced between progress reports.
|
|
113
|
+
* Unchanged progress keeps its original time, so a source wait is included
|
|
114
|
+
* instead of being mistaken for encoder throughput.
|
|
115
|
+
*/
|
|
116
|
+
export class ProgressRate {
|
|
117
|
+
#trend;
|
|
118
|
+
#horizonSeconds;
|
|
119
|
+
#adjacentRunSeconds;
|
|
120
|
+
#previous = null;
|
|
121
|
+
|
|
122
|
+
constructor(horizonSeconds, adjacentRunSeconds = 4) {
|
|
123
|
+
this.#horizonSeconds = horizonSeconds;
|
|
124
|
+
this.#adjacentRunSeconds = Number.isFinite(adjacentRunSeconds) && adjacentRunSeconds > 0
|
|
125
|
+
? adjacentRunSeconds
|
|
126
|
+
: 4;
|
|
127
|
+
this.#trend = new RateTrend(horizonSeconds);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
note(at, processedSeconds, startPositionSeconds) {
|
|
131
|
+
if (!Number.isFinite(at) || !Number.isFinite(processedSeconds)) return this.#trend.snapshot();
|
|
132
|
+
const start = Number.isFinite(startPositionSeconds) ? startPositionSeconds : 0;
|
|
133
|
+
const discontinuousStart = this.#previous && start !== this.#previous.start &&
|
|
134
|
+
(start < this.#previous.start || start > this.#previous.processedSeconds + this.#adjacentRunSeconds);
|
|
135
|
+
if (!this.#previous || discontinuousStart || processedSeconds < this.#previous.processedSeconds) {
|
|
136
|
+
this.#previous = { at, processedSeconds, start };
|
|
137
|
+
this.#trend = new RateTrend(this.#horizonSeconds);
|
|
138
|
+
return this.#trend.snapshot();
|
|
139
|
+
}
|
|
140
|
+
const elapsedMs = at - this.#previous.at;
|
|
141
|
+
if (processedSeconds > this.#previous.processedSeconds && elapsedMs > 0) {
|
|
142
|
+
this.#trend.add(at, (processedSeconds - this.#previous.processedSeconds) * 1000 / elapsedMs);
|
|
143
|
+
this.#previous = { at, processedSeconds, start };
|
|
144
|
+
}
|
|
145
|
+
return this.#trend.snapshot();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
111
149
|
/**
|
|
112
150
|
* @param {object} input
|
|
113
151
|
* @param {number} input.positionSeconds
|