fix: update GOP parameters for mediaAnalyzerGOP and transcode2PassX265 to enhance encoding efficiency and documentation
Build / Lint & Test (push) Successful in 16s

This commit is contained in:
PickleRick committed 2026-09-20 19:37:31 +02:00
1 parent 36184fbf32
commit 3f0b38d513
7 files changed
+38 -19

No files matched your search

@@ -6,7 +6,9 @@
* mediaAnalyzerGOP
* Analyzes the video stream and populates args.variables with encoding
* parameters consumed by downstream plugins:
* gopKeyint / gopMinKeyint -> 2 x FPS (rounded)
* gopKeyint -> 4 x FPS (rounded; maximum 4-second GOP)
* gopMinKeyint -> 1 x FPS (rounded; permits scene cuts)
* gopSceneCut -> x265 scene-cut threshold (40)
* isAnimation -> detected from animeSeries path list
* isMovie -> detected from moviePathPatterns
* vBitrate / vMaxRate / vBufSize
@@ -17,7 +19,7 @@
const details = () => ({
name: 'Media Analyzer & GOP Calculator',
description:
'Analyzes video metadata, calculates GOP keyint (2xFPS), target bitrate '
'Analyzes video metadata, calculates a streaming-friendly 4-second GOP, target bitrate '
+ 'and VBV parameters based on content type (film/series/anime). '
+ 'Exports all values to args.variables for downstream plugins.',
style: { borderColor: '#2196F3' },
@@ -123,8 +125,13 @@ const plugin = async (args) => {
}
jobLog(`[mediaAnalyzerGOP] FPS: ${fps.toFixed(3)}\n`);
const gopKeyint = String(Math.round(fps * 2));
jobLog(`[mediaAnalyzerGOP] keyint=${gopKeyint}\n`);
// A four-second maximum GOP is a compromise for remote streaming: it
// reduces regular I-frame overhead while keeping seek recovery short.
// A one-second minimum leaves x265 free to insert an I-frame at a real cut.
const gopKeyint = String(Math.round(fps * 4));
const gopMinKeyint = String(Math.round(fps));
const gopSceneCut = '40';
jobLog(`[mediaAnalyzerGOP] keyint=${gopKeyint} min-keyint=${gopMinKeyint} scenecut=${gopSceneCut}\n`);
// Normalize to forward slashes for cross-platform path matching
const normalizedPath = filePath.replace(/\\/g, '/');
@@ -149,7 +156,8 @@ const plugin = async (args) => {
jobLog(`[mediaAnalyzerGOP] vBitrate=${vBitrate} vMaxRate=${vMaxRate} vBufSize=${vBufSize}\n`);
variables.gopKeyint = gopKeyint;
variables.gopMinKeyint = gopKeyint;
variables.gopMinKeyint = gopMinKeyint;
variables.gopSceneCut = gopSceneCut;
variables.isAnimation = String(isAnimation);
variables.isMovie = String(isMovie);
variables.vBitrate = vBitrate;
@@ -166,8 +166,9 @@ const plugin = async (args) => {
// ── Read variables ────────────────────────────────────────────────────────
const {
gopKeyint = '48',
gopMinKeyint = '48',
gopKeyint = '96',
gopMinKeyint = '24',
gopSceneCut = '40',
vBitrate = '3000k',
vMaxRate = '6000k',
vBufSize = '12000k',
@@ -189,7 +190,8 @@ const plugin = async (args) => {
jobLog(
`[transcode2PassX265] Params: bitrate=${vBitrate} maxrate=${maxRateNum}k `
+ `bufsize=${bufSizeNum}k keyint=${gopKeyint} tune=${x265Tune || 'none'} preset=${x265Preset}\n`,
+ `bufsize=${bufSizeNum}k keyint=${gopKeyint} min-keyint=${gopMinKeyint} scenecut=${gopSceneCut} `
+ `tune=${x265Tune || 'none'} preset=${x265Preset}\n`,
);
const sourceBitrateKbps = getVideoBitrateKbps(videoStream, streams, format).toFixed(0);
jobLog(
@@ -219,7 +221,7 @@ const plugin = async (args) => {
const x265ParamsBase = [
`keyint=${gopKeyint}`,
`min-keyint=${gopMinKeyint}`,
`scenecut=0`,
`scenecut=${gopSceneCut}`,
`open-gop=0`,
`vbv-maxrate=${maxRateNum}`,
`vbv-bufsize=${bufSizeNum}`,