add hardware encoding to ffmpeg, binary choice

This commit is contained in:
waveringana 2023-10-29 17:20:22 -04:00
parent 8c3f2db3b2
commit e580b8fd76
6 changed files with 298 additions and 38 deletions

View file

@ -1,10 +1,33 @@
import type {RequestHandler as Middleware, NextFunction} from "express"; import type {RequestHandler as Middleware, NextFunction} from "express";
import ffmpeg from "fluent-ffmpeg"; import ffmpeg from 'fluent-ffmpeg';
import ffmpegpath from "@ffmpeg-installer/ffmpeg"; import ffmpegInstaller from '@ffmpeg-installer/ffmpeg';
import ffprobepath from "@ffprobe-installer/ffprobe"; import ffprobeInstaller from '@ffprobe-installer/ffprobe';
ffmpeg.setFfmpegPath(ffmpegpath.path); import which from 'which';
ffmpeg.setFfprobePath(ffprobepath.path);
//weird error that occurs where if I use the alias 'process', node cannot access it
import Process from 'node:process';
const getExecutablePath = (envVar: string, executable: string, installer: { path: string }) => {
if (Process.env[envVar]) {
return Process.env[envVar];
}
try {
return which.sync(executable);
} catch (error) {
return installer.path;
}
};
const ffmpegPath = getExecutablePath('EB_FFMPEG_PATH', 'ffmpeg', ffmpegInstaller);
const ffprobePath = getExecutablePath('EB_FFPROBE_PATH', 'ffprobe', ffprobeInstaller);
console.log(`Using ffmpeg from path: ${ffmpegPath}`);
console.log(`Using ffprobe from path: ${ffprobePath}`);
ffmpeg.setFfmpegPath(ffmpegPath!);
ffmpeg.setFfprobePath(ffprobePath!);
import fs from "fs"; import fs from "fs";
import process from "process"; import process from "process";
@ -12,6 +35,20 @@ import process from "process";
import {extension, videoExtensions, imageExtensions} from "./lib"; import {extension, videoExtensions, imageExtensions} from "./lib";
import {db, MediaParams, insertToDB} from "./db"; import {db, MediaParams, insertToDB} from "./db";
enum EncodingType {
CPU = 'libx264',
NVIDIA = 'h264_nvenc',
AMD = 'h264_vmf',
INTEL = 'h264_qsv',
APPLE = 'h264_videotoolbox'
}
let currentEncoding: EncodingType = EncodingType.CPU;
export const setEncodingType = (type: EncodingType) => {
currentEncoding = type;
};
export const checkAuth: Middleware = (req, res, next) => { export const checkAuth: Middleware = (req, res, next) => {
if (!req.user) { if (!req.user) {
return res.status(401); return res.status(401);
@ -73,11 +110,12 @@ export const convert: Middleware = (req, res, next) => {
if (videoExtensions.includes(nameAndExtension[1])) { if (videoExtensions.includes(nameAndExtension[1])) {
console.log("Converting " + nameAndExtension[0] + nameAndExtension[1] + " to gif"); console.log("Converting " + nameAndExtension[0] + nameAndExtension[1] + " to gif");
console.log(`Using ${currentEncoding} as encoder`);
const startTime = Date.now(); const startTime = Date.now();
ffmpeg() ffmpeg()
.input(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`) .input(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`)
.inputFormat(nameAndExtension[1].substring(1)) .inputFormat(nameAndExtension[1].substring(1))
.outputOptions(`-c:v ${currentEncoding}`)
.outputFormat("gif") .outputFormat("gif")
.output(`uploads/${nameAndExtension[0]}.gif`) .output(`uploads/${nameAndExtension[0]}.gif`)
.on("end", function() { .on("end", function() {
@ -88,6 +126,7 @@ export const convert: Middleware = (req, res, next) => {
.run(); .run();
} else if (nameAndExtension[1] == ".gif") { } else if (nameAndExtension[1] == ".gif") {
console.log(`Converting ${nameAndExtension[0]}${nameAndExtension[1]} to mp4`); console.log(`Converting ${nameAndExtension[0]}${nameAndExtension[1]} to mp4`);
console.log(`Using ${currentEncoding} as encoder`);
const startTime = Date.now(); const startTime = Date.now();
ffmpeg(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`) ffmpeg(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`)
@ -95,7 +134,7 @@ export const convert: Middleware = (req, res, next) => {
.outputFormat("mp4") .outputFormat("mp4")
.outputOptions([ .outputOptions([
"-pix_fmt yuv420p", "-pix_fmt yuv420p",
"-c:v libx264", `-c:v ${currentEncoding}`,
"-movflags +faststart" "-movflags +faststart"
]) ])
.noAudio() .noAudio()
@ -128,17 +167,20 @@ export const convertTo720p: Middleware = (req, res, next) => {
const startTime = Date.now(); const startTime = Date.now();
const outputOptions = [
'-vf', 'scale=-2:720',
'-c:v', currentEncoding,
];
ffmpeg() ffmpeg()
.input(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`) .input(`uploads/${nameAndExtension[0]}${nameAndExtension[1]}`)
.inputFormat(nameAndExtension[1].substring(1)) .inputFormat('mp4')
.outputOptions("-vf", "scale=-2:720") .outputOptions(outputOptions)
.output(`uploads/720p-${nameAndExtension[0]}${nameAndExtension[1]}`) .output(`uploads/720p-${nameAndExtension[0]}${nameAndExtension[1]}`)
.on("end", function() { .on('end', () => {
console.log(`720p copy complete, took ${Date.now() - startTime} to complete`); console.log(`720p copy complete using ${currentEncoding}, took ${Date.now() - startTime}ms to complete`);
console.log(`Uploaded to uploads/720p-${nameAndExtension[0]}${nameAndExtension[1]}`);
next();
}) })
.on("error", (e) => console.log(e)) .on('error', (e) => console.log(e))
.run(); .run();
} }

139
package-lock.json generated
View file

@ -25,7 +25,8 @@
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"probe-image-size": "^7.2.3", "probe-image-size": "^7.2.3",
"sqlite3": "^5.0.2" "sqlite3": "^5.0.2",
"which": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/connect-sqlite3": "^0.9.1", "@types/connect-sqlite3": "^0.9.1",
@ -36,10 +37,11 @@
"@types/fluent-ffmpeg": "^2.1.20", "@types/fluent-ffmpeg": "^2.1.20",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^18.11.10", "@types/node": "^18.18.7",
"@types/passport": "^1.0.11", "@types/passport": "^1.0.11",
"@types/passport-local": "^1.0.34", "@types/passport-local": "^1.0.34",
"@types/probe-image-size": "^7.2.0", "@types/probe-image-size": "^7.2.0",
"@types/which": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^5.46.1", "@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1", "@typescript-eslint/parser": "^5.46.1",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
@ -668,10 +670,13 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.11.10", "version": "18.18.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==",
"dev": true "dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
}, },
"node_modules/@types/passport": { "node_modules/@types/passport": {
"version": "1.0.11", "version": "1.0.11",
@ -753,6 +758,12 @@
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true "dev": true
}, },
"node_modules/@types/which": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.1.tgz",
"integrity": "sha512-OJWjr4k8gS1HXuOnCmQbBrQez+xqt/zqfp5PhgbKtsmEFEuojAg23arr+TiTZZ1TORdUF9RKXb/WKEpT1dwgSg==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.46.1", "version": "5.46.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz",
@ -1744,6 +1755,21 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/cross-spawn/node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -3485,6 +3511,21 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/node-gyp/node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"optional": true,
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/noms": { "node_modules/noms": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
@ -4749,6 +4790,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unique-filename": { "node_modules/unique-filename": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -4835,18 +4882,25 @@
} }
}, },
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
"devOptional": true,
"dependencies": { "dependencies": {
"isexe": "^2.0.0" "isexe": "^3.1.1"
}, },
"bin": { "bin": {
"node-which": "bin/node-which" "node-which": "bin/which.js"
}, },
"engines": { "engines": {
"node": ">= 8" "node": "^16.13.0 || >=18.0.0"
}
},
"node_modules/which/node_modules/isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
"engines": {
"node": ">=16"
} }
}, },
"node_modules/wide-align": { "node_modules/wide-align": {
@ -5416,10 +5470,13 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "18.11.10", "version": "18.18.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==",
"dev": true "dev": true,
"requires": {
"undici-types": "~5.26.4"
}
}, },
"@types/passport": { "@types/passport": {
"version": "1.0.11", "version": "1.0.11",
@ -5501,6 +5558,12 @@
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true "dev": true
}, },
"@types/which": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.1.tgz",
"integrity": "sha512-OJWjr4k8gS1HXuOnCmQbBrQez+xqt/zqfp5PhgbKtsmEFEuojAg23arr+TiTZZ1TORdUF9RKXb/WKEpT1dwgSg==",
"dev": true
},
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "5.46.1", "version": "5.46.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz",
@ -6213,6 +6276,17 @@
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
"which": "^2.0.1" "which": "^2.0.1"
},
"dependencies": {
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
} }
}, },
"debug": { "debug": {
@ -7529,6 +7603,15 @@
"gauge": "^4.0.3", "gauge": "^4.0.3",
"set-blocking": "^2.0.0" "set-blocking": "^2.0.0"
} }
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"optional": true,
"requires": {
"isexe": "^2.0.0"
}
} }
} }
}, },
@ -8443,6 +8526,12 @@
"random-bytes": "~1.0.0" "random-bytes": "~1.0.0"
} }
}, },
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"unique-filename": { "unique-filename": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -8517,12 +8606,18 @@
} }
}, },
"which": { "which": {
"version": "2.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
"devOptional": true,
"requires": { "requires": {
"isexe": "^2.0.0" "isexe": "^3.1.1"
},
"dependencies": {
"isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="
}
} }
}, },
"wide-align": { "wide-align": {

View file

@ -44,7 +44,8 @@
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"probe-image-size": "^7.2.3", "probe-image-size": "^7.2.3",
"sqlite3": "^5.0.2" "sqlite3": "^5.0.2",
"which": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/connect-sqlite3": "^0.9.1", "@types/connect-sqlite3": "^0.9.1",
@ -55,10 +56,11 @@
"@types/fluent-ffmpeg": "^2.1.20", "@types/fluent-ffmpeg": "^2.1.20",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^18.11.10", "@types/node": "^18.18.7",
"@types/passport": "^1.0.11", "@types/passport": "^1.0.11",
"@types/passport-local": "^1.0.34", "@types/passport-local": "^1.0.34",
"@types/probe-image-size": "^7.2.0", "@types/probe-image-size": "^7.2.0",
"@types/which": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^5.46.1", "@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1", "@typescript-eslint/parser": "^5.46.1",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",

65
tests/ffmpeg.ts Normal file
View file

@ -0,0 +1,65 @@
import ffmpeg from 'fluent-ffmpeg';
import ffmpegInstaller from '@ffmpeg-installer/ffmpeg';
import ffprobeInstaller from '@ffprobe-installer/ffprobe';
import which from 'which';
const getExecutablePath = (envVar: string, executable: string, installer: { path: string }) => {
if (process.env[envVar]) {
return process.env[envVar];
}
try {
return which.sync(executable);
} catch (error) {
return installer.path;
}
};
const ffmpegPath = getExecutablePath('EB_FFMPEG_PATH', 'ffmpeg', ffmpegInstaller);
const ffprobePath = getExecutablePath('EB_FFPROBE_PATH', 'ffprobe', ffprobeInstaller);
console.log(`Using ffmpeg from path: ${ffmpegPath}`);
console.log(`Using ffprobe from path: ${ffprobePath}`);
ffmpeg.setFfmpegPath(ffmpegPath!);
ffmpeg.setFfprobePath(ffprobePath!);
export enum EncodingType {
CPU = 'libx264',
NVIDIA = 'h264_nvenc',
AMD = 'h264_vmf',
INTEL = 'h264_qsv',
APPLE = 'h264_videotoolbox',
}
export const generateTestVideo = async (encodingType: EncodingType): Promise<void> => {
console.log(`Generating test video using ${encodingType}...`);
const startTime = Date.now();
const outputOptions = [
'-vf', 'scale=-2:720',
'-c:v', encodingType,
];
return new Promise<void>((resolve, reject) => {
ffmpeg()
.input('test.mp4')
.inputFormat('mp4')
.outputOptions(outputOptions)
.output(`720p-test-${encodingType}.mp4`)
.on('end', () => {
console.log(`720p copy complete using ${encodingType}, took ${Date.now() - startTime}ms to complete`);
resolve();
})
.on('error', (e) => reject(new Error(e)))
.run();
});
};
// Test commands (uncomment to use)
// generateTestVideo(EncodingType.CPU).catch(console.error);
// generateTestVideo(EncodingType.AMD).catch(console.error);
// generateTestVideo(EncodingType.INTEL).catch(console.error);
// generateTestVideo(EncodingType.NVIDIA).catch(console.error);
// generateTestVideo(EncodingType.APPLE).catch(console.error);

BIN
tests/test.mp4 Normal file

Binary file not shown.

56
tests/test.ts Normal file
View file

@ -0,0 +1,56 @@
import readline from 'readline';
import { generateTestVideo, EncodingType } from "./ffmpeg";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const questionAsync = (query: string) => {
return new Promise<string>(resolve => {
rl.question(query, resolve);
});
};
const main = async () => {
console.log("Testing software encoder: ");
await generateTestVideo(EncodingType.CPU).catch(console.error);
const answer = await questionAsync('Would you like to test other hardware encoders? (yes/no): ');
if (answer.toLowerCase() === 'yes') {
const encoder = await questionAsync('Which hardware encoder would you like to test? (INTEL/NVIDIA/AMD/APPLE): ');
let selectedEncoder: EncodingType;
switch (encoder.toUpperCase()) {
case 'INTEL':
selectedEncoder = EncodingType.INTEL;
break;
case 'NVIDIA':
selectedEncoder = EncodingType.NVIDIA;
break;
case 'AMD':
selectedEncoder = EncodingType.AMD;
break;
case 'APPLE':
selectedEncoder = EncodingType.APPLE;
break;
default:
console.log('Invalid choice. Exiting.');
rl.close();
return;
}
console.log(`Testing ${selectedEncoder} encoder:`);
await generateTestVideo(selectedEncoder).catch(console.error);
} else {
console.log("Exiting.");
}
rl.close();
};
main().catch(err => {
console.error("An error occurred:", err);
rl.close();
});