@torrent-tv/proxy 2.7.0 → 2.8.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/package.json +1 -1
- package/services/hls-session-manager.js +1 -113
package/package.json
CHANGED
|
@@ -24,12 +24,6 @@ const DEFAULT_SEGMENT_DURATION_SEC = 4;
|
|
|
24
24
|
// is allowed to be before we restart ffmpeg at that position (server-side seek).
|
|
25
25
|
// Requests within the window are served by waiting for the running encode.
|
|
26
26
|
const MAX_LOOKAHEAD_SEGMENTS = 8;
|
|
27
|
-
// Just-in-time pacing: pause the encoder once it is this many segments ahead of
|
|
28
|
-
// the player's current position, and resume once the lead shrinks to the resume
|
|
29
|
-
// threshold. This keeps a comfortable buffer for smooth playback while capping
|
|
30
|
-
// CPU near real-time instead of racing through the whole file at once.
|
|
31
|
-
const PACING_PAUSE_AHEAD_SEGMENTS = 8;
|
|
32
|
-
const PACING_RESUME_AHEAD_SEGMENTS = 4;
|
|
33
27
|
// Idle TTL: a session is disposed this long after the last segment/playlist
|
|
34
28
|
// access. Kept short so an ffmpeg process does not keep burning CPU after the
|
|
35
29
|
// viewer stops or navigates away. Active playback refreshes the timer on every
|
|
@@ -498,11 +492,6 @@ export class HlsSessionManager {
|
|
|
498
492
|
encodeStartIndex: 0,
|
|
499
493
|
// Guards against repeatedly restarting to the same seek position.
|
|
500
494
|
pendingRestartIndex: -1,
|
|
501
|
-
// Just-in-time pacing: highest segment index the player has requested
|
|
502
|
-
// (≈ playhead) and whether the encoder is currently paused for running
|
|
503
|
-
// far enough ahead.
|
|
504
|
-
playheadIndex: 0,
|
|
505
|
-
paused: false,
|
|
506
495
|
progress: {
|
|
507
496
|
state: "starting",
|
|
508
497
|
processedSeconds: 0,
|
|
@@ -590,19 +579,14 @@ export class HlsSessionManager {
|
|
|
590
579
|
|
|
591
580
|
// Terminate any existing encode process before starting a new one. The
|
|
592
581
|
// old process's exit handler no-ops because session.ffmpeg is reassigned
|
|
593
|
-
// below (it checks identity).
|
|
594
|
-
// termination signal is actually delivered.
|
|
582
|
+
// below (it checks identity).
|
|
595
583
|
if (session.ffmpeg && !session.ffmpeg.killed) {
|
|
596
584
|
try {
|
|
597
|
-
if (session.paused) {
|
|
598
|
-
session.ffmpeg.kill("SIGCONT");
|
|
599
|
-
}
|
|
600
585
|
session.ffmpeg.kill("SIGTERM");
|
|
601
586
|
} catch (_error) {
|
|
602
587
|
// Best effort.
|
|
603
588
|
}
|
|
604
589
|
}
|
|
605
|
-
session.paused = false;
|
|
606
590
|
|
|
607
591
|
const videoCodecArgs = session.transcodeVideo
|
|
608
592
|
? [
|
|
@@ -669,7 +653,6 @@ export class HlsSessionManager {
|
|
|
669
653
|
session.ffmpeg = ffmpeg;
|
|
670
654
|
session.encodeStartIndex = safeIndex;
|
|
671
655
|
session.pendingRestartIndex = -1;
|
|
672
|
-
session.playheadIndex = safeIndex;
|
|
673
656
|
session.state = session.state === "disposed" ? "disposed" : "starting";
|
|
674
657
|
session.progress.state = "running";
|
|
675
658
|
session.progress.processedSeconds = startSeconds;
|
|
@@ -742,8 +725,6 @@ export class HlsSessionManager {
|
|
|
742
725
|
` speed=${session.progress.speed || "n/a"}`
|
|
743
726
|
);
|
|
744
727
|
}
|
|
745
|
-
// Just-in-time pacing: pause once we are far enough ahead of the player.
|
|
746
|
-
this.#maybePauseEncoder(session);
|
|
747
728
|
}
|
|
748
729
|
});
|
|
749
730
|
|
|
@@ -819,79 +800,6 @@ export class HlsSessionManager {
|
|
|
819
800
|
this.#startEncodeRun(session, index);
|
|
820
801
|
}
|
|
821
802
|
|
|
822
|
-
/**
|
|
823
|
-
* Segment index ffmpeg has encoded up to so far, derived from the output
|
|
824
|
-
* timestamp reported via -progress.
|
|
825
|
-
*
|
|
826
|
-
* @param {HlsSession} session
|
|
827
|
-
* @returns {number}
|
|
828
|
-
*/
|
|
829
|
-
#producedSegmentIndex(session) {
|
|
830
|
-
const processed = Number(session.progress?.processedSeconds);
|
|
831
|
-
if (!Number.isFinite(processed) || processed <= 0) {
|
|
832
|
-
return session.encodeStartIndex;
|
|
833
|
-
}
|
|
834
|
-
return Math.floor(processed / this.segmentDurationSec);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
/**
|
|
838
|
-
* Pause the encoder once it has produced enough segments ahead of the
|
|
839
|
-
* player's current position (just-in-time pacing). No-op on platforms
|
|
840
|
-
* without POSIX job-control signals (fail-open: the encoder keeps running).
|
|
841
|
-
*
|
|
842
|
-
* @param {HlsSession} session
|
|
843
|
-
* @returns {void}
|
|
844
|
-
*/
|
|
845
|
-
#maybePauseEncoder(session) {
|
|
846
|
-
if (session.paused || !session.ffmpeg || session.ffmpeg.killed) {
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
|
-
if (process.platform === "win32") {
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
const ahead = this.#producedSegmentIndex(session) - session.playheadIndex;
|
|
853
|
-
if (ahead < PACING_PAUSE_AHEAD_SEGMENTS) {
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
try {
|
|
857
|
-
session.ffmpeg.kill("SIGSTOP");
|
|
858
|
-
session.paused = true;
|
|
859
|
-
logger.info(
|
|
860
|
-
`transcode ${session.id} paused (buffered ${ahead} segments ahead) "${session.fileName}"`
|
|
861
|
-
);
|
|
862
|
-
} catch (_error) {
|
|
863
|
-
// Fail-open: if we cannot pause, let the encoder keep running.
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Resume a paused encoder when the player has caught up enough that the
|
|
869
|
-
* buffered lead has shrunk to the resume threshold.
|
|
870
|
-
*
|
|
871
|
-
* @param {HlsSession} session
|
|
872
|
-
* @returns {void}
|
|
873
|
-
*/
|
|
874
|
-
#maybeResumeEncoder(session) {
|
|
875
|
-
if (!session.paused || !session.ffmpeg || session.ffmpeg.killed) {
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
const ahead = this.#producedSegmentIndex(session) - session.playheadIndex;
|
|
879
|
-
if (ahead > PACING_RESUME_AHEAD_SEGMENTS) {
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
try {
|
|
883
|
-
session.ffmpeg.kill("SIGCONT");
|
|
884
|
-
session.paused = false;
|
|
885
|
-
session.progress.updatedAt = Date.now();
|
|
886
|
-
logger.info(`transcode ${session.id} resumed (lead ${ahead} segments) "${session.fileName}"`);
|
|
887
|
-
} catch (_error) {
|
|
888
|
-
// If resume fails, force a restart from the player's position so it does
|
|
889
|
-
// not stall (fail-safe toward smooth playback).
|
|
890
|
-
session.paused = false;
|
|
891
|
-
this.#startEncodeRun(session, session.playheadIndex);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
803
|
#buildVideoFilter(targetWidth, targetHeight) {
|
|
896
804
|
const safeWidth = Number.isInteger(targetWidth) && targetWidth > 0 ? targetWidth : 1280;
|
|
897
805
|
const safeHeight = Number.isInteger(targetHeight) && targetHeight > 0 ? targetHeight : 720;
|
|
@@ -970,16 +878,6 @@ export class HlsSessionManager {
|
|
|
970
878
|
}
|
|
971
879
|
session.lastAccessedAt = Date.now();
|
|
972
880
|
|
|
973
|
-
// Track the player position (≈ playhead) from segment requests and resume
|
|
974
|
-
// a paused encoder as the player approaches the buffered edge.
|
|
975
|
-
if (fileName !== PLAYLIST_FILE_NAME) {
|
|
976
|
-
const requestedIndex = segmentIndexFromName(fileName);
|
|
977
|
-
if (requestedIndex >= 0) {
|
|
978
|
-
session.playheadIndex = Math.max(session.playheadIndex, requestedIndex);
|
|
979
|
-
this.#maybeResumeEncoder(session);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
881
|
// Serve the synthetic VOD playlist (full duration, terminated with
|
|
984
882
|
// #EXT-X-ENDLIST) so the player gets the correct total length and a fully
|
|
985
883
|
// seekable timeline up-front, independent of how far ffmpeg has encoded.
|
|
@@ -1127,16 +1025,6 @@ export class HlsSessionManager {
|
|
|
1127
1025
|
this.sessionIdBySource.delete(session.sourceMapKey);
|
|
1128
1026
|
|
|
1129
1027
|
if (session.ffmpeg && !session.ffmpeg.killed) {
|
|
1130
|
-
// Resume first if paused, otherwise SIGTERM is not delivered to a
|
|
1131
|
-
// stopped process.
|
|
1132
|
-
if (session.paused) {
|
|
1133
|
-
try {
|
|
1134
|
-
session.ffmpeg.kill("SIGCONT");
|
|
1135
|
-
} catch (_error) {
|
|
1136
|
-
// Best effort.
|
|
1137
|
-
}
|
|
1138
|
-
session.paused = false;
|
|
1139
|
-
}
|
|
1140
1028
|
session.ffmpeg.kill("SIGTERM");
|
|
1141
1029
|
await waitForChildExit(session.ffmpeg);
|
|
1142
1030
|
}
|