Changes:
* Added configurable scenecut. * Removed bframes 0 and 1 from nvenc default config. * Added b_ref_mode configuration option. * Fixed path parallelization. * Attempted libvmaf parallelization.
This commit is contained in:
+15
-7
@@ -25,16 +25,17 @@
|
|||||||
"film",
|
"film",
|
||||||
"grain",
|
"grain",
|
||||||
"animation"
|
"animation"
|
||||||
|
],
|
||||||
|
"scenecut": [
|
||||||
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"h264_nvenc": {
|
"h264_nvenc": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"pool": "nvenc",
|
"pool": "nvenc",
|
||||||
"parallel": 2,
|
"parallel": 3,
|
||||||
"gpu": -1,
|
"gpu": -1,
|
||||||
"presets": [
|
"presets": [
|
||||||
"p5",
|
|
||||||
"p6",
|
|
||||||
"p7"
|
"p7"
|
||||||
],
|
],
|
||||||
"tunes": [
|
"tunes": [
|
||||||
@@ -45,18 +46,22 @@
|
|||||||
16,
|
16,
|
||||||
32
|
32
|
||||||
],
|
],
|
||||||
"bf": [
|
"bframes": [
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
2,
|
||||||
3,
|
3,
|
||||||
4
|
4
|
||||||
|
],
|
||||||
|
"bframe_reference_mode": [
|
||||||
|
"middle"
|
||||||
|
],
|
||||||
|
"scenecut": [
|
||||||
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hevc_nvenc": {
|
"hevc_nvenc": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"pool": "nvenc",
|
"pool": "nvenc",
|
||||||
"parallel": 2,
|
"parallel": 3,
|
||||||
"gpu": -1
|
"gpu": -1
|
||||||
},
|
},
|
||||||
"h264_amf": {
|
"h264_amf": {
|
||||||
@@ -182,6 +187,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
"vmaf": {
|
||||||
|
"model": "vmaf_4k_rb_v0.6.2.pkl"
|
||||||
|
},
|
||||||
"resolutions": [
|
"resolutions": [
|
||||||
[
|
[
|
||||||
1280,
|
1280,
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ class h264_nvenc extends encoder {
|
|||||||
for (let preset of this.settings.presets) {
|
for (let preset of this.settings.presets) {
|
||||||
for (let tune of this.settings.tunes) {
|
for (let tune of this.settings.tunes) {
|
||||||
for (let rcla of this.settings["rc-lookahead"]) {
|
for (let rcla of this.settings["rc-lookahead"]) {
|
||||||
for (let ia of [0, 1]) {
|
for (let scenecut of this.settings.scenecut) {
|
||||||
if ((rcla == 0) && (ia != 0)) { // Requires lookahead.
|
if ((rcla == 0) && (scenecut == true)) { // Requires lookahead.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (let bf of this.settings.bf) {
|
for (let bf of this.settings.bframes) {
|
||||||
for (let bfm of ["disabled", "middle"]) {
|
for (let bfm of this.settings.bframe_reference_mode) {
|
||||||
if ((bf == 0) && (bfm != "disabled")) { // Requires B-Frames
|
if ((bf == 0) && (bfm != "disabled")) { // Requires B-Frames
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ class h264_nvenc extends encoder {
|
|||||||
"-rc", "cbr",
|
"-rc", "cbr",
|
||||||
"-cbr", 1,
|
"-cbr", 1,
|
||||||
"-rc-lookahead", rcla,
|
"-rc-lookahead", rcla,
|
||||||
"-no-scenecut", 1 - ia,
|
"-no-scenecut", scenecut == true ? 0 : 1,
|
||||||
"-bf", bf,
|
"-bf", bf,
|
||||||
"-b_ref_mode", bfm,
|
"-b_ref_mode", bfm,
|
||||||
"-b_adapt", 1,
|
"-b_adapt", 1,
|
||||||
@@ -124,7 +124,7 @@ class h264_nvenc extends encoder {
|
|||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(this.config.paths.output, "h264_nvenc.json"),
|
path.join(this.config.paths.output, "h264_nvenc.json"),
|
||||||
JSON.stringify(this.indexes, null, null),
|
JSON.stringify(this.indexes, null, '\t'),
|
||||||
{encoding: "utf8"}
|
{encoding: "utf8"}
|
||||||
);
|
);
|
||||||
if (global.debug) console.timeEnd("Generating...");
|
if (global.debug) console.timeEnd("Generating...");
|
||||||
|
|||||||
+9
-1
@@ -68,6 +68,7 @@ class libx264 extends encoder {
|
|||||||
this.combinations = [];
|
this.combinations = [];
|
||||||
for (let preset of this.settings.presets) {
|
for (let preset of this.settings.presets) {
|
||||||
for (let tune of this.settings.tunes) {
|
for (let tune of this.settings.tunes) {
|
||||||
|
for (let scenecut of this.settings.scenecut) {
|
||||||
let _opts = [
|
let _opts = [
|
||||||
"-profile:v", "high",
|
"-profile:v", "high",
|
||||||
"-preset", preset,
|
"-preset", preset,
|
||||||
@@ -78,6 +79,11 @@ class libx264 extends encoder {
|
|||||||
if (tune) {
|
if (tune) {
|
||||||
_opts.push("-tune", tune);
|
_opts.push("-tune", tune);
|
||||||
}
|
}
|
||||||
|
if (scenecut == false) {
|
||||||
|
_opts.push("-sc_threshold", 0);
|
||||||
|
} else if ((typeof scenecut) == "number") {
|
||||||
|
_opts.push("-sc_threshold", scenecut.toFixed(0));
|
||||||
|
}
|
||||||
|
|
||||||
let _name = name(_opts);
|
let _name = name(_opts);
|
||||||
let _hash = crypto.createHash("sha256").update(_name).digest("hex");
|
let _hash = crypto.createHash("sha256").update(_name).digest("hex");
|
||||||
@@ -91,12 +97,14 @@ class libx264 extends encoder {
|
|||||||
|
|
||||||
this.indexes[_hash] = combo.options;
|
this.indexes[_hash] = combo.options;
|
||||||
this.combinations.push(combo);
|
this.combinations.push(combo);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(this.config.paths.output, "libx264.json"),
|
path.join(this.config.paths.output, "libx264.json"),
|
||||||
JSON.stringify(this.indexes, null, null),
|
JSON.stringify(this.indexes, null, '\t'),
|
||||||
{encoding: "utf8"}
|
{encoding: "utf8"}
|
||||||
);
|
);
|
||||||
if (global.debug) console.timeEnd("Generating...");
|
if (global.debug) console.timeEnd("Generating...");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
function parse_duration(str) {
|
function parse_duration(str) {
|
||||||
// Duration is in the form HH:MM:SS.fraction
|
// Duration is in the form HH:MM:SS.fraction
|
||||||
@@ -152,6 +153,20 @@ class ffmpeg {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consolify(_file) {
|
||||||
|
//_file = path.resolve(_file);
|
||||||
|
if (os.platform() == 'win32') {
|
||||||
|
// Need to turn:
|
||||||
|
// C:\myfiles\model.pkl
|
||||||
|
// into:
|
||||||
|
// C\\:/myfiles/model.pkl
|
||||||
|
_file = _file.replace(/\\/g, '/').replace(':', '\\\\:');
|
||||||
|
} else {
|
||||||
|
// No further work needs to be done?
|
||||||
|
}
|
||||||
|
return _file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ffmpeg;
|
module.exports = ffmpeg;
|
||||||
|
|||||||
@@ -6,13 +6,6 @@ global.debug = false;
|
|||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
// Actual Code
|
// Actual Code
|
||||||
|
|
||||||
/*
|
|
||||||
Comparing VMAF instantly:
|
|
||||||
- Saves disk space - no need to keep copies around!
|
|
||||||
fn: .\ffmpeg.exe -i "..\..\cache\arma_3-002-1280x720x60.00.mkv" -i "..\..\videos\arma_3-002.mkv" -filter_complex_threads 8 -filter_complex [0:v:0]scale=flags=bicubic+full_chroma_inp+full_chroma_int:w=1920:h=1080,colorspace=all=bt709:range=pc,format=pix_fmts=yuv444p[main];[1:v:0]colorspace=all=bt709:range=pc,format=pix_fmts=yuv444p[ref];[main][ref]libvmaf=model_path=../vmaf/vmaf_4k_rb_v0.6.2.pkl:log_fmt=json:log_path=here2.json:enable_conf_interval=1:shortest=1[out] -map [out] -f null -
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Import modules
|
// Import modules
|
||||||
const ffmpeg = require('./ffmpeg.js');
|
const ffmpeg = require('./ffmpeg.js');
|
||||||
const poolqueue = require('./poolqueue.js');
|
const poolqueue = require('./poolqueue.js');
|
||||||
@@ -271,20 +264,10 @@ async function create_caches(config, ff, videos, encoders) { // Create Caches
|
|||||||
return videos;
|
return videos;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function transcode(config, ff, videos, encoders) {
|
async function queue(config, ff, videos, encoders) {
|
||||||
console.group("Transcoding...");
|
console.group("Queueing...");
|
||||||
console.time("Total");
|
console.time("Total");
|
||||||
|
|
||||||
/* Needs a massive overhaul:
|
|
||||||
- Check if the .json file for a configuration already exists, if not continue.
|
|
||||||
- Do not store transcodes for longer than needed, we only need to compare against source.
|
|
||||||
- Use poolqueue to still allow for parallelization.
|
|
||||||
- '-g' and bitrate options are controlled _from here_.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Build queues per cache.
|
|
||||||
console.group("Queueing...")
|
|
||||||
console.time("Subtotal");
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
for (let video_key of videos.keys()) {
|
for (let video_key of videos.keys()) {
|
||||||
promises.push(new Promise(async (resolve, reject) => {
|
promises.push(new Promise(async (resolve, reject) => {
|
||||||
@@ -349,7 +332,7 @@ async function transcode(config, ff, videos, encoders) {
|
|||||||
].concat(command.options).concat(encoder_extra).concat([file]);
|
].concat(command.options).concat(encoder_extra).concat([file]);
|
||||||
|
|
||||||
queue_commands.push(encoder_pool, line, command.cost);
|
queue_commands.push(encoder_pool, line, command.cost);
|
||||||
queue_files.push(encoder_pool, [file, file_json], command.cost);
|
queue_files.push(encoder_pool, [[file, file_json]], command.cost);
|
||||||
|
|
||||||
resolve2(true);
|
resolve2(true);
|
||||||
}));
|
}));
|
||||||
@@ -377,9 +360,11 @@ async function transcode(config, ff, videos, encoders) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
await Promise.allSettled(promises);
|
await Promise.allSettled(promises);
|
||||||
console.timeEnd("Subtotal");
|
console.timeEnd("Total");
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function work(config, ff, videos, encoders) {
|
||||||
// Process from here on out.
|
// Process from here on out.
|
||||||
// LOOP
|
// LOOP
|
||||||
// 1. Pull out front of the command and file queue.
|
// 1. Pull out front of the command and file queue.
|
||||||
@@ -388,53 +373,122 @@ async function transcode(config, ff, videos, encoders) {
|
|||||||
// 4. Delete encoded files.
|
// 4. Delete encoded files.
|
||||||
// 5. Repeat until queues empty, no more caches for video, and no more videos.
|
// 5. Repeat until queues empty, no more caches for video, and no more videos.
|
||||||
|
|
||||||
/*
|
let vmaf_model = ff.consolify(path.resolve(path.join(config.paths.ffmpeg, "vmaf", config.options.vmaf.model)));
|
||||||
console.group("Queueing...")
|
|
||||||
console.time("Subtotal");
|
console.group("Processing...")
|
||||||
|
console.time("Total");
|
||||||
for (let video_key of videos.keys()) {
|
for (let video_key of videos.keys()) {
|
||||||
console.group(video_key);
|
console.group(video_key);
|
||||||
console.time(video_key);
|
console.time(video_key);
|
||||||
let video = videos.get(video_key);
|
let video = videos.get(video_key);
|
||||||
for (let cache_key of video.caches.keys()) {
|
for (let cache_key of video.caches.keys()) {
|
||||||
let cache = video.caches.get(cache_key);
|
|
||||||
console.time(cache_key);
|
console.time(cache_key);
|
||||||
|
let cache = video.caches.get(cache_key);
|
||||||
|
|
||||||
console.timeEnd(cache_key);
|
let length = cache.queues.commands.length;
|
||||||
|
console.log(`0.0% (0 / ${length}): 0.000s`);
|
||||||
|
while (cache.queues.commands.length > 0) {
|
||||||
|
let commands = cache.queues.commands.shift();
|
||||||
|
let files = cache.queues.files.shift();
|
||||||
|
|
||||||
|
let prc1 = length - cache.queues.commands.length;
|
||||||
|
let prc2 = prc1 / length * 100.0;
|
||||||
|
let LABEL = `${prc2.toFixed(1)}% (${prc1} / ${length})`
|
||||||
|
console.time(LABEL)
|
||||||
|
console.group();
|
||||||
|
|
||||||
|
// Create directories.
|
||||||
|
for (let file of files) {
|
||||||
|
fs.mkdirSync(path.dirname(file[0]), { recursive: true });
|
||||||
}
|
}
|
||||||
console.timeEnd(video_key);
|
|
||||||
console.groupEnd();
|
// Encode
|
||||||
}
|
{
|
||||||
console.timeEnd("Subtotal");
|
console.time("Encoding");
|
||||||
console.groupEnd();
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
for (let video_name in videos) {
|
|
||||||
console.time(video_name);
|
|
||||||
console.group(video_name);
|
|
||||||
let video = videos[video_name];
|
|
||||||
for (let cache of video.caches) {
|
|
||||||
let key_cache = `${cache.x}x${cache.y}x${cache.fps.toFixed(2)}`;
|
|
||||||
console.time(key_cache);
|
|
||||||
console.group(key_cache);
|
|
||||||
for (let cmd of cache.commands) {
|
|
||||||
let opts = [
|
let opts = [
|
||||||
"-y",
|
"-y",
|
||||||
"-hide_banner",
|
"-hide_banner",
|
||||||
"-v", "error",
|
"-v", "error",
|
||||||
"-hwaccel", "auto",
|
"-hwaccel", "auto",
|
||||||
"-i", cache.file
|
"-i", cache.file
|
||||||
].concat(cmd);
|
].concat(commands);
|
||||||
let res = ff.ffmpegSync(opts);
|
let res = ff.ffmpegSync(opts);
|
||||||
|
if (res.status != 0) {
|
||||||
console.log(res.stdout.toString(), res.stderr.toString());
|
console.log(res.stdout.toString(), res.stderr.toString());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
console.groupEnd();
|
console.timeEnd("Encoding");
|
||||||
console.timeEnd(key_cache);
|
|
||||||
}
|
|
||||||
console.groupEnd();
|
|
||||||
console.timeEnd(video_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Comparing VMAF instantly:
|
||||||
|
- Saves disk space - no need to keep copies around!
|
||||||
|
fn: .\ffmpeg.exe -i "..\..\cache\arma_3-002-1280x720x60.00.mkv" -i "..\..\videos\arma_3-002.mkv" -filter_complex_threads 8 -filter_complex [0:v:0]scale=flags=bicubic+full_chroma_inp+full_chroma_int:w=1920:h=1080,colorspace=all=bt709:range=pc,format=pix_fmts=yuv444p[main];[1:v:0]colorspace=all=bt709:range=pc,format=pix_fmts=yuv444p[ref];[main][ref]libvmaf=model_path=../vmaf/vmaf_4k_rb_v0.6.2.pkl:log_fmt=json:log_path=here2.json:enable_conf_interval=1:shortest=1[out] -map [out] -f null -
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Process
|
||||||
|
{
|
||||||
|
console.time("Processing");
|
||||||
|
let opts = [
|
||||||
|
"-hide_banner",
|
||||||
|
"-v", "warning",
|
||||||
|
"-hwaccel", "auto",
|
||||||
|
"-i", video.file,
|
||||||
|
];
|
||||||
|
let filter = "";
|
||||||
|
let references = [];
|
||||||
|
|
||||||
|
// Build Filter Graph
|
||||||
|
if (files.length > 1) {
|
||||||
|
filter = `[0:v:0]split=${files.length}`
|
||||||
|
for (let idx = 0; idx < files.length; idx++) {
|
||||||
|
filter = `${filter}[ref:${idx}]`;
|
||||||
|
references[idx] = `[ref:${idx}]`;
|
||||||
|
}
|
||||||
|
filter = `${filter}`
|
||||||
|
} else {
|
||||||
|
references[0] = "[0:v:0]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescale and Resample all inputs and compare them.
|
||||||
|
for (let idx = 0; idx < files.length; idx++) {
|
||||||
|
let file = files[idx];
|
||||||
|
opts.push("-i", file[0]);
|
||||||
|
|
||||||
|
if (filter != "")
|
||||||
|
filter = `${filter};` // [temp:${idx}];[temp:${idx}]
|
||||||
|
filter = `${filter}[${idx}:v:0]scale=flags=bicubic+full_chroma_inp+full_chroma_int:w=${video.resolution.width.toFixed(0)}:h=${video.resolution.height.toFixed(0)},colorspace=space=${video.color.matrix}:trc=${video.color.trc}:primaries=${video.color.primaries}:range=${video.color.range},format=pix_fmts=yuv444p,fps=fps=${video.framerate.toFixed(2)},[ref:${idx}]libvmaf=model_path=${vmaf_model}:log_fmt=json:log_path=${ff.consolify(file[1])}:enable_conf_interval=1:n_threads=2[main:${idx}]`
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.push("-filter_complex", filter);
|
||||||
|
for (let idx = 0; idx < files.length; idx++) {
|
||||||
|
opts.push(
|
||||||
|
"-map", `[main:${idx}]`,
|
||||||
|
"-f", "null",
|
||||||
|
"-"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(opts);
|
||||||
|
let res = ff.ffmpegSync(opts);
|
||||||
|
if (res.status != 0) {
|
||||||
|
console.log(res.stdout.toString(), res.stderr.toString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
console.timeEnd("Processing");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(filter);
|
||||||
|
|
||||||
|
console.groupEnd();
|
||||||
|
console.timeEnd(LABEL);
|
||||||
|
}
|
||||||
|
console.timeEnd(cache_key);
|
||||||
|
}
|
||||||
|
console.timeEnd(video_key);
|
||||||
|
console.groupEnd();
|
||||||
|
}
|
||||||
console.timeEnd("Total");
|
console.timeEnd("Total");
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
}
|
||||||
@@ -450,13 +504,8 @@ async function main() {
|
|||||||
await load_encoders(config, ff).then((p) => { encoders = p; });
|
await load_encoders(config, ff).then((p) => { encoders = p; });
|
||||||
await load_videos(config, ff).then((p) => { videos = p; });
|
await load_videos(config, ff).then((p) => { videos = p; });
|
||||||
await create_caches(config, ff, videos, encoders);
|
await create_caches(config, ff, videos, encoders);
|
||||||
await transcode(config, ff, videos, encoders);
|
await queue(config, ff, videos, encoders);
|
||||||
|
await work(config, ff, videos, encoders);
|
||||||
|
|
||||||
// Queue
|
|
||||||
//await queue_transcodes(config, ff, videos, encoders);
|
|
||||||
|
|
||||||
// Transcode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
Reference in New Issue
Block a user