Files
js-encoding-samples/ffmpeg.js
T
Michael Fabian 'Xaymar' Dirks 6e2ac5e513 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.
2020-10-25 22:26:04 +01:00

173 lines
3.9 KiB
JavaScript

const path = require('path');
const process = require('process');
const child_process = require('child_process');
const os = require('os');
function parse_duration(str) {
// Duration is in the form HH:MM:SS.fraction
let parts = str.split(":");
for (let idx = 0; idx < parts.length; idx++) {
parts[idx] = parseFloat(parts[idx]);
}
return ((parts[0] * 60) + parts[1]) * 60 + parts[2];
}
class ffmpeg {
constructor(ffpath) {
// Figure out the location of FFmpeg
if (!ffpath)
ffpath = process.cwd();
this.bin = {
ffmpeg: path.join(ffpath, "bin/ffmpeg.exe"),
ffprobe: path.join(ffpath, "bin/ffprobe.exe")
};
}
_probeParse(buffer) {
let parsed = JSON.parse(buffer);
if (parsed.streams && (parsed.streams.length > 0)) {
for (let idx = 0; idx < parsed.streams.length; idx++) {
let stream = parsed.streams[idx];
if (stream.tags && stream.tags["DURATION"]) {
stream.duration = parse_duration(stream.tags["DURATION"]);
}
}
}
return parsed;
}
probeSync(file) {
let result = child_process.spawnSync(this.bin.ffprobe,
[
"-hide_banner",
"-v", "quiet",
"-print_format", "json",
"-show_format",
"-show_streams",
"-i", file
]
);
return this._probeParse(result.stdout);
}
probe(file) {
return new Promise((resolve, reject) => {
let proc = child_process.spawn(this.bin.ffprobe,
[
"-hide_banner",
"-v", "quiet",
"-print_format", "json",
"-show_format",
"-show_streams",
"-i", file
],
{stdio: ['pipe', 'pipe', 'pipe']}
);
let buffer = "";
proc.stdout.on('data', (data) => {
buffer += data.toString();
});
proc.on('close', (code, signal) => {
if (code == 0) {
try {
resolve(this._probeParse(buffer));
} catch (ex) {
reject(ex);
}
} else {
if (code == null) {
reject(signal);
} else {
reject(code);
}
}
});
proc.on('error', (error) => {
reject(error);
})
});
}
get_encoder_caps(encoder) {
let result = {
hardware: false,
formats: [],
devices: []
};
// Output is a list of options.
let temp = child_process.execFileSync(this.bin.ffmpeg,
[
"-hide_banner",
"-v", "quiet",
"-h", `encoder=${encoder}`
]
).toString();
let lines = temp.split('\r\n');
for (let line of lines) {
if (line.includes("General capabilities:")) {
result.hardware = (line.includes("hardware"));
} else if (line.includes("Supported hardware devices:")) {
let data = line.substr(line.indexOf(':') + 2);
result.devices = data.split(' ');
} else if (line.includes("Supported pixel formats:")) {
let data = line.substr(line.indexOf(':') + 2);
result.formats = data.split(' ');
}
}
return result;
}
ffmpegSync(params) {
return child_process.spawnSync(this.bin.ffmpeg, params);
}
ffmpeg(params) {
return new Promise((resolve, reject) => {
let proc = child_process.spawn(this.bin.ffmpeg, params);
let buf_stdout = "";
let buf_stderr = "";
proc.stdout.on('data', (data) => {
buf_stdout += data.toString();
});
proc.stderr.on('data', (data) => {
buf_stderr += data.toString();
});
proc.on('close', (code, signal) => {
if (code == 0) {
try {
resolve([code, buf_stdout, buf_stderr]);
} catch (ex) {
reject([ex, buf_stdout, buf_stderr]);
}
} else {
if (code == null) {
reject([signal, buf_stdout, buf_stderr]);
} else {
reject([code, buf_stdout, buf_stderr]);
}
}
});
proc.on('error', (error) => {
reject([error, buf_stdout, buf_stderr]);
})
});
}
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;