@torrent-tv/proxy 2.13.0 → 2.14.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/CHANGELOG.md +756 -742
- package/package.json +1 -1
- package/routes/api/transcode-sessions/post.js +6 -0
- package/routes/transcode/audio-file/get.js +45 -0
- package/server.js +8 -1
- package/services/hls-session-manager.js +517 -55
- package/services/playback-planner.js +14 -0
- package/test/decode-cost.test.js +347 -401
- package/test/quality-variants.test.js +166 -0
package/package.json
CHANGED
|
@@ -73,6 +73,11 @@ export async function handleApiTranscodeSessionsPost(req, reply, { hlsSessionMan
|
|
|
73
73
|
// (capped to source), with the realtime budget's auto-downscale + runtime
|
|
74
74
|
// downswitch disabled for the session.
|
|
75
75
|
const manualQuality = payload.manualQuality === true;
|
|
76
|
+
// Whether this browser will take its audio from a separate rendition group in
|
|
77
|
+
// the master playlist rather than muxed into the picture. It has to say so:
|
|
78
|
+
// publishing renditions AND muxing the same audio would play it twice, while
|
|
79
|
+
// a browser that does not know about them would get no sound at all.
|
|
80
|
+
const audioRenditions = payload.audioRenditions === true;
|
|
76
81
|
const startPositionSeconds = Number(payload.startPositionSeconds);
|
|
77
82
|
const audioTrackIndex = Number(payload.audioTrackIndex);
|
|
78
83
|
// Which container to produce. The browser knows what its media stack will
|
|
@@ -96,6 +101,7 @@ export async function handleApiTranscodeSessionsPost(req, reply, { hlsSessionMan
|
|
|
96
101
|
targetWidth: Number.isInteger(targetWidth) && targetWidth > 0 ? targetWidth : 0,
|
|
97
102
|
targetHeight: Number.isInteger(targetHeight) && targetHeight > 0 ? targetHeight : 0,
|
|
98
103
|
manualQuality,
|
|
104
|
+
audioRenditions,
|
|
99
105
|
startPositionSeconds:
|
|
100
106
|
Number.isFinite(startPositionSeconds) && startPositionSeconds > 0
|
|
101
107
|
? startPositionSeconds
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file GET /transcode/:sessionId/a/:trackIndex/:fileName — one file of an audio
|
|
3
|
+
* rendition.
|
|
4
|
+
*
|
|
5
|
+
* A rendition is one audio track of the file, encoded once and shared by every
|
|
6
|
+
* quality rung, published in the master playlist as `#EXT-X-MEDIA`. It is an
|
|
7
|
+
* ordinary session underneath, living one directory level below the base
|
|
8
|
+
* session so every relative name inside its playlist resolves to it unchanged —
|
|
9
|
+
* the same arrangement quality variants use.
|
|
10
|
+
*
|
|
11
|
+
* Unlike a variant, a rendition is fetched ALONGSIDE the picture rather than
|
|
12
|
+
* instead of it: both encoders run, one for the rung and one for the audio.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { serveSessionFile } from "../session-file/get.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import("fastify").FastifyRequest} req
|
|
19
|
+
* @param {import("fastify").FastifyReply} reply
|
|
20
|
+
* @param {{ hlsSessionManager: import("../../../services/hls-session-manager.js").HlsSessionManager }} deps
|
|
21
|
+
* @returns {Promise<void>}
|
|
22
|
+
*/
|
|
23
|
+
export async function handleTranscodeAudioFileGet(req, reply, { hlsSessionManager }) {
|
|
24
|
+
const baseSessionId = typeof req.params.sessionId === "string" ? req.params.sessionId : "";
|
|
25
|
+
const trackIndex = Number(req.params.trackIndex);
|
|
26
|
+
const fileName = typeof req.params.fileName === "string" ? req.params.fileName : "";
|
|
27
|
+
|
|
28
|
+
const resolved = await hlsSessionManager.resolveAudioRenditionFile(baseSessionId, trackIndex, fileName);
|
|
29
|
+
if (resolved.error) {
|
|
30
|
+
// Retryable, like every other not-ready answer on this path: a 500 for a
|
|
31
|
+
// rendition playlist would end the stream over something the next attempt
|
|
32
|
+
// may well get past.
|
|
33
|
+
reply.header("Retry-After", "1");
|
|
34
|
+
return reply.code(503).send({ error: `Could not prepare the audio rendition: ${resolved.error}` });
|
|
35
|
+
}
|
|
36
|
+
if (!resolved.sessionId) {
|
|
37
|
+
return reply.code(404).send({ error: "No such audio rendition for this transcode session." });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return serveSessionFile(req, reply, {
|
|
41
|
+
hlsSessionManager,
|
|
42
|
+
sessionId: resolved.sessionId,
|
|
43
|
+
fileName
|
|
44
|
+
});
|
|
45
|
+
}
|
package/server.js
CHANGED
|
@@ -30,6 +30,7 @@ import { handleApiTranscodeSessionSeekPost } from "./routes/api/transcode-sessio
|
|
|
30
30
|
import { handleStreamGet } from "./routes/stream/get.js";
|
|
31
31
|
import { handleTranscodeSessionFileGet } from "./routes/transcode/session-file/get.js";
|
|
32
32
|
import { handleTranscodeVariantFileGet } from "./routes/transcode/variant-file/get.js";
|
|
33
|
+
import { handleTranscodeAudioFileGet } from "./routes/transcode/audio-file/get.js";
|
|
33
34
|
import { handleTranscodeVariantWarmGet } from "./routes/transcode/variant-warm/get.js";
|
|
34
35
|
import { createSourceRegistry } from "./store/source-registry.js";
|
|
35
36
|
import { WorkerTorrentPool } from "./services/torrent-worker/pool-adapter.js";
|
|
@@ -173,7 +174,10 @@ export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin,
|
|
|
173
174
|
// Reuse the media info the planner already probed for this file (same
|
|
174
175
|
// ffmpeg scan) so createSession skips its own probe. Late-bound: invoked
|
|
175
176
|
// only at session-create time, after playbackPlanner is initialised.
|
|
176
|
-
getCachedMediaInfo: (params) => playbackPlanner.getCachedMediaInfo(params)
|
|
177
|
+
getCachedMediaInfo: (params) => playbackPlanner.getCachedMediaInfo(params),
|
|
178
|
+
// The file's audio tracks, for the master playlist's rendition group. Already
|
|
179
|
+
// probed for the browser's audio menu; read from there rather than probed again.
|
|
180
|
+
getCachedAudioTracks: (params) => playbackPlanner.getCachedAudioTracks(params)
|
|
177
181
|
});
|
|
178
182
|
const playbackPlanner = createPlaybackPlanner({
|
|
179
183
|
ffmpegBin,
|
|
@@ -245,6 +249,9 @@ export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin,
|
|
|
245
249
|
app.get("/transcode/:sessionId/v/:height/warm", async (req, reply) =>
|
|
246
250
|
handleTranscodeVariantWarmGet(req, reply, { hlsSessionManager })
|
|
247
251
|
);
|
|
252
|
+
app.get("/transcode/:sessionId/a/:trackIndex/:fileName", async (req, reply) =>
|
|
253
|
+
handleTranscodeAudioFileGet(req, reply, { hlsSessionManager })
|
|
254
|
+
);
|
|
248
255
|
app.get("/transcode/:sessionId/v/:height/:fileName", async (req, reply) =>
|
|
249
256
|
handleTranscodeVariantFileGet(req, reply, { hlsSessionManager })
|
|
250
257
|
);
|