@eleven-am/transcoder
Version:
High-performance HLS transcoding library with hardware acceleration, intelligent client management, and distributed processing support for Node.js
621 lines • 31.2 kB
JavaScript
;
/*
* @eleven-am/transcoder
* Copyright (C) 2025 Roy OSSAI
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.HardwareAccelerationDetector = void 0;
const child_process_1 = require("child_process");
const os = __importStar(require("os"));
const util_1 = require("util");
const fp_1 = require("@eleven-am/fp");
const types_1 = require("./types");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class HardwareAccelerationDetector {
/**
* Detect the best available hardware acceleration method
* @returns A TaskEither with the hardware acceleration configuration
*/
detectHardwareAcceleration() {
const platform = os.platform();
return this.checkFfmpegHardwareAccelerationSupport()
.matchTask([
{
predicate: (isSupported) => !isSupported,
run: () => fp_1.TaskEither.of(this.getSoftwareConfig()),
},
{
predicate: () => platform === 'darwin',
run: () => this.detectVideoToolbox()
.orElse(() => this.detectCuda()),
},
{
predicate: () => platform === 'linux',
run: () => this.detectVAAPI()
.orElse(() => this.detectCuda())
.orElse(() => this.detectQsv()),
},
{
predicate: () => platform === 'win32',
run: () => this.detectCuda()
.orElse(() => this.detectAmf())
.orElse(() => this.detectQsv()),
},
])
.orElse(() => fp_1.TaskEither.of(this.getSoftwareConfig()));
}
/**
* Apply the hardware acceleration configuration to FFmpeg options
* @param hwConfig Hardware acceleration configuration
* @param width Target width
* @param height Target height
* @param codec Target codec (h264 or h265)
* @returns Object with inputOptions, outputOptions, and videoFilters
*/
applyHardwareConfig(hwConfig, width, height, codec = 'h264') {
hwConfig = hwConfig || this.getSoftwareConfig();
const outputOptions = hwConfig.outputOptions[codec] || hwConfig.outputOptions['h264'];
let videoFilters = '';
if (hwConfig.videoFilters.scale) {
const scaleFilter = hwConfig.videoFilters.scale
.replace('%width%', width.toString())
.replace('%height%', height.toString());
videoFilters += scaleFilter;
}
if (hwConfig.videoFilters.deinterlace) {
if (videoFilters) {
videoFilters += ',';
}
videoFilters += hwConfig.videoFilters.deinterlace;
}
return {
inputOptions: hwConfig.inputOptions,
outputOptions,
videoFilters,
};
}
/**
* Check if FFmpeg supports hardware acceleration with comprehensive testing
* @returns A TaskEither with a boolean indicating if hardware acceleration is supported
*/
checkFfmpegHardwareAccelerationSupport() {
const checkFfmpegInstallTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -version'), 'Failed to check FFmpeg installation')
.map((result) => result.stdout.includes('ffmpeg version'))
.orElse(() => fp_1.TaskEither.of(false));
const checkFfmpegVersionTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -version'), 'Failed to check FFmpeg version')
.map((result) => {
const versionMatch = result.stdout.match(/ffmpeg version (\d+)\.(\d+)/);
if (!versionMatch) {
return false;
}
const major = parseInt(versionMatch[1], 10);
const minor = parseInt(versionMatch[2], 10);
return major > 4 || (major === 4 && minor >= 0);
})
.orElse(() => fp_1.TaskEither.of(false));
const checkHwAccelListTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -hwaccels'), 'Failed to check hardware acceleration methods')
.map((result) => {
const output = result.stdout.toLowerCase();
return output.includes('hardware acceleration') ||
output.includes('hwaccels') ||
(output.includes('cuda') || output.includes('vaapi') ||
output.includes('videotoolbox') || output.includes('qsv') ||
output.includes('dxva2') || output.includes('d3d11va'));
})
.orElse(() => fp_1.TaskEither.of(false));
const checkHardwareCodecsTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders'), 'Failed to check hardware encoders')
.map((result) => {
const output = result.stdout.toLowerCase();
return output.includes('nvenc') ||
output.includes('vaapi') ||
output.includes('videotoolbox') ||
output.includes('qsv') ||
output.includes('amf') ||
output.includes('v4l2');
})
.orElse(() => fp_1.TaskEither.of(false));
const testBasicFunctionalityTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=0.1:size=64x64:rate=1 -t 0.1 -f null -c:v libx264 - 2>&1'), 'Failed to test basic FFmpeg functionality')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const hasCriticalErrors = output.includes('Unknown encoder') ||
output.includes('Encoder not found') ||
output.includes('No such file or directory') ||
output.includes('Permission denied') ||
output.includes('command not found');
return !hasCriticalErrors;
})
.orElse(() => fp_1.TaskEither.of(false));
const checkEssentialLibrariesTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -version'), 'Failed to check FFmpeg libraries')
.map((result) => {
const output = result.stdout.toLowerCase();
return output.includes('libavcodec') &&
output.includes('libavformat') &&
output.includes('libavutil') &&
(output.includes('libx264') || output.includes('openh264'));
})
.orElse(() => fp_1.TaskEither.of(false));
const checkHardwareConfigTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -version'), 'Failed to check FFmpeg configuration')
.map((result) => {
const output = result.stdout.toLowerCase();
return output.includes('--enable-cuda') ||
output.includes('--enable-vaapi') ||
output.includes('--enable-videotoolbox') ||
output.includes('--enable-qsv') ||
output.includes('--enable-nvenc') ||
output.includes('--enable-amf') ||
(!output.includes('--disable-cuda') &&
!output.includes('--disable-vaapi') &&
!output.includes('--disable-videotoolbox'));
})
.orElse(() => fp_1.TaskEither.of(true));
return fp_1.TaskEither
.fromBind({
ffmpegInstalled: checkFfmpegInstallTask,
ffmpegVersion: checkFfmpegVersionTask,
hwAccelList: checkHwAccelListTask,
hardwareCodecs: checkHardwareCodecsTask,
basicFunctionality: testBasicFunctionalityTask,
essentialLibraries: checkEssentialLibrariesTask,
hardwareConfig: checkHardwareConfigTask,
})
.map(({ ffmpegInstalled, ffmpegVersion, hwAccelList, hardwareCodecs, basicFunctionality, essentialLibraries, hardwareConfig, }) => {
const criticalChecks = ffmpegInstalled && ffmpegVersion && basicFunctionality && essentialLibraries;
const hardwareChecks = hwAccelList || hardwareCodecs;
return criticalChecks && hardwareChecks && hardwareConfig;
});
}
/**
* Detect CUDA (NVIDIA) hardware acceleration support with comprehensive testing
* @returns A TaskEither with the CUDA hardware acceleration configuration
*/
detectCuda() {
const checkCudaSupportTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -hwaccels'), 'Failed to check CUDA support')
.map((result) => result.stdout.includes('cuda'));
const checkNvidiaGpuTask = fp_1.TaskEither
.tryCatch(() => execAsync('nvidia-smi -L'), 'Failed to check NVIDIA GPU')
.map((result) => result.stdout.toLowerCase().includes('gpu'))
.orElse(() => fp_1.TaskEither.of(false));
const checkCuvidDecodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -decoders | grep _cuvid'), 'Failed to check CUVID decoders')
.map((result) => {
const decoders = result.stdout
.split('\n')
.filter((line) => line.includes('_cuvid'))
.map((line) => line.trim().split(' ')[1])
.filter(Boolean);
return decoders.length > 0;
})
.orElse(() => fp_1.TaskEither.of(false));
const checkNvencEncodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders | grep nvenc'), 'Failed to check NVENC encoders')
.map((result) => {
const encoders = result.stdout
.split('\n')
.filter((line) => line.includes('nvenc'))
.map((line) => line.trim().split(' ')[1])
.filter(Boolean);
return encoders.length > 0;
})
.orElse(() => fp_1.TaskEither.of(false));
const testCudaInitTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -init_hw_device cuda -f null - 2>&1'), 'Failed to test CUDA initialization')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
return !output.includes('Device creation failed') &&
!output.includes('No device available') &&
!output.includes('CUDA not available') &&
!output.includes('Cannot load nvcuda.dll') &&
!output.includes('Cannot load libcuda.so');
})
.orElse(() => fp_1.TaskEither.of(false));
const testNvencDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -t 1 -f null -hwaccel cuda -hwaccel_output_format cuda -c:v h264_nvenc - 2>&1'), 'Failed to test NVENC device')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const hasDeviceError = output.includes('Device creation failed') ||
output.includes('No device available') ||
output.includes('CUDA') && (output.includes('not available') ||
output.includes('not supported') ||
output.includes('failed') ||
output.includes('error')) ||
output.includes('NVENC') && (output.includes('not available') ||
output.includes('not supported') ||
output.includes('failed') ||
output.includes('error')) ||
output.includes('Hardware device setup failed') ||
output.includes('Cannot load nvcuda') ||
output.includes('Cannot load libcuda') ||
output.includes('No NVENC capable devices found') ||
output.includes('Driver does not support NVENC');
return !hasDeviceError;
})
.orElse(() => fp_1.TaskEither.of(false));
const testNvencCapabilitiesTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=0.1:size=64x64:rate=1 -t 0.1 -f null -c:v h264_nvenc - 2>&1'), 'Failed to test NVENC capabilities')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const hasCriticalError = output.includes('Unknown encoder') ||
output.includes('No NVENC capable devices') ||
output.includes('Driver does not support NVENC');
return !hasCriticalError;
})
.orElse(() => fp_1.TaskEither.of(false));
const checkNvidiaDriverTask = fp_1.TaskEither
.tryCatch(() => execAsync('nvidia-smi --query-gpu=driver_version --format=csv,noheader,nounits'), 'Failed to check NVIDIA driver version')
.map((result) => {
const version = result.stdout.trim();
const versionNumber = parseFloat(version);
return !isNaN(versionNumber) && versionNumber >= 390;
})
.orElse(() => fp_1.TaskEither.of(true));
return fp_1.TaskEither
.fromBind({
cudaSupport: checkCudaSupportTask,
nvidiaGpu: checkNvidiaGpuTask,
cuvidDecoders: checkCuvidDecodersTask,
nvencEncoders: checkNvencEncodersTask,
cudaInit: testCudaInitTask,
nvencDevice: testNvencDeviceTask,
nvencCapabilities: testNvencCapabilitiesTask,
nvidiaDriver: checkNvidiaDriverTask,
})
.filter(({ cudaSupport, nvidiaGpu, cuvidDecoders, nvencEncoders, cudaInit, nvencDevice, nvencCapabilities, nvidiaDriver, }) => cudaSupport && nvidiaGpu && cuvidDecoders && nvencEncoders &&
cudaInit && (nvencDevice || nvencCapabilities) && nvidiaDriver, () => (0, fp_1.createInternalError)('CUDA/NVENC acceleration not available or device not functional'))
.map(() => this.getCudaConfig());
}
/**
* Detect VAAPI (Intel/AMD on Linux) hardware acceleration support with comprehensive testing
* @returns A TaskEither with the VAAPI hardware acceleration configuration
*/
detectVAAPI() {
const checkVaapiSupportTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -hwaccels'), 'Failed to check VAAPI support')
.map((result) => result.stdout.includes('vaapi'));
const checkVaapiEncodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders | grep vaapi'), 'Failed to check VAAPI encoders')
.map((result) => result.stdout.includes('vaapi'));
const checkVaapiDecodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -decoders | grep vaapi'), 'Failed to check VAAPI decoders')
.map((result) => result.stdout.includes('vaapi'))
.orElse(() => fp_1.TaskEither.of(true));
const checkRenderDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ls /dev/dri/renderD*'), 'Failed to check render device')
.map((result) => result.stdout.trim().split('\n'))
.filter((devices) => devices.length > 0, () => (0, fp_1.createInternalError)('No render device found'))
.map(([device]) => device)
.orElse(() => fp_1.TaskEither.of(null));
const checkVainfoTask = fp_1.TaskEither
.tryCatch(() => execAsync('vainfo'), 'Failed to check vainfo')
.map((result) => result.stdout.includes('VAProfile'))
.orElse(() => fp_1.TaskEither.of(false));
const testVaapiDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=0.1:size=320x240:rate=1 -t 1 -f null -hwaccel vaapi -hwaccel_output_format vaapi -c:v h264_vaapi - 2>&1'), 'Failed to test VAAPI device')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const hasDeviceError = output.includes('Device creation failed') ||
output.includes('No device available') ||
output.includes('VAAPI') && (output.includes('not available') ||
output.includes('not supported') ||
output.includes('failed') ||
output.includes('error')) ||
output.includes('Hardware device setup failed') ||
output.includes('Cannot load libva') ||
output.includes('vaInitialize failed');
return !hasDeviceError;
})
.orElse(() => fp_1.TaskEither.of(false));
const testVaapiInitTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -init_hw_device vaapi -f null - 2>&1'), 'Failed to test VAAPI initialization')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
return !output.includes('Device creation failed') &&
!output.includes('No device available') &&
!output.includes('vaInitialize failed');
})
.orElse(() => fp_1.TaskEither.of(false));
return fp_1.TaskEither
.fromBind({
vaapiSupport: checkVaapiSupportTask,
vaapiEncoders: checkVaapiEncodersTask,
vaapiDecoders: checkVaapiDecodersTask,
renderDevice: checkRenderDeviceTask,
vainfoAvailable: checkVainfoTask,
vaapiDevice: testVaapiDeviceTask,
vaapiInit: testVaapiInitTask,
})
.filter(({ vaapiSupport, vaapiEncoders, vaapiDecoders, renderDevice, vainfoAvailable, vaapiDevice, vaapiInit, }) => vaapiSupport && vaapiEncoders && vaapiDecoders &&
Boolean(renderDevice) && vainfoAvailable &&
(vaapiDevice || vaapiInit), () => (0, fp_1.createInternalError)('VAAPI acceleration not available or device not functional'))
.map(({ renderDevice }) => this.getVAAPIConfig(renderDevice));
}
/**
* Detect VideoToolbox (macOS) hardware acceleration support with device testing
* @returns A TaskEither with the VideoToolbox hardware acceleration configuration
*/
detectVideoToolbox() {
if (os.platform() !== 'darwin') {
return fp_1.TaskEither.error((0, fp_1.createBadRequestError)('VideoToolbox is only available on macOS'));
}
const checkVideoToolboxEncodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders | grep videotoolbox'), 'Failed to check VideoToolbox encoders')
.map((result) => result.stdout.includes('videotoolbox'));
const checkVideoToolboxDecodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -decoders | grep videotoolbox'), 'Failed to check VideoToolbox decoders')
.map((result) => result.stdout.includes('videotoolbox'))
.orElse(() => fp_1.TaskEither.of(true));
const testVideoToolboxDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -t 1 -f null -c:v h264_videotoolbox - 2>&1'), 'Failed to test VideoToolbox device')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const hasDeviceError = output.includes('VideoToolbox') && (output.includes('Error') ||
output.includes('Failed') ||
output.includes('not available') ||
output.includes('not supported') ||
output.includes('Invalid argument') ||
output.includes('Operation not supported'));
const hasGenericHardwareError = output.includes('Hardware acceleration') &&
output.includes('not available');
return !hasDeviceError && !hasGenericHardwareError;
})
.orElse(() => fp_1.TaskEither.of(false));
const testVideoToolboxCapabilitiesTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f videotoolbox -list_devices true -f null - 2>&1'), 'Failed to check VideoToolbox capabilities')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
return !output.includes('Unknown input format') ||
!output.includes('VideoToolbox not available');
})
.orElse(() => fp_1.TaskEither.of(true));
return fp_1.TaskEither
.fromBind({
videoToolboxEncoders: checkVideoToolboxEncodersTask,
videoToolboxDecoders: checkVideoToolboxDecodersTask,
videoToolboxDevice: testVideoToolboxDeviceTask,
videoToolboxCapabilities: testVideoToolboxCapabilitiesTask,
})
.filter(({ videoToolboxEncoders, videoToolboxDecoders, videoToolboxDevice, videoToolboxCapabilities, }) => videoToolboxEncoders && videoToolboxDecoders && videoToolboxDevice && videoToolboxCapabilities, () => (0, fp_1.createInternalError)('VideoToolbox acceleration not available or device not functional'))
.map(() => this.getVideoToolboxConfig());
}
/**
* Detect AMF (AMD) hardware acceleration support with device testing
* @returns A TaskEither with the AMF hardware acceleration configuration
*/
detectAmf() {
const checkAmfEncodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders'), 'Failed to check AMF encoders')
.map((result) => result.stdout.includes('h264_amf') || result.stdout.includes('hevc_amf'));
const checkAmfHwaccelTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -hwaccels'), 'Failed to check AMF hwaccel')
.map((result) => result.stdout.toLowerCase().includes('amf'));
const testAmfDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -t 1 -f null -c:v h264_amf - 2>&1'), 'Failed to test AMF device')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
const errorKeywords = [
'Device creation failed',
'No device available',
'AMF',
'not available',
'not supported',
'failed',
'error',
];
return !errorKeywords.some((keyword) => output.includes(keyword));
})
.orElse(() => fp_1.TaskEither.of(false));
return fp_1.TaskEither
.fromBind({
amfEncoders: checkAmfEncodersTask,
amfHwaccel: checkAmfHwaccelTask,
amfDevice: testAmfDeviceTask,
})
.filter(({ amfEncoders, amfHwaccel, amfDevice }) => amfEncoders && amfHwaccel && amfDevice, () => (0, fp_1.createInternalError)('AMF acceleration not available or device not functional'))
.map(() => this.getAmfConfig());
}
/**
* Detect Intel QuickSync Video support with device testing
* @returns A TaskEither with the QSV hardware acceleration configuration
*/
detectQsv() {
const checkQsvEncodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -encoders | grep qsv'), 'Failed to check QSV encoders')
.map((result) => result.stdout.includes('qsv'));
const checkQsvDecodersTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -decoders | grep qsv'), 'Failed to check QSV decoders')
.map((result) => result.stdout.includes('qsv'));
const testQsvDeviceTask = fp_1.TaskEither
.tryCatch(() => execAsync('ffmpeg -hide_banner -init_hw_device qsv -f null - 2>&1'), 'Failed to test QSV device')
.map((result) => {
const output = (result.stderr || '') + (result.stdout || '');
return !output.includes('Device creation failed') &&
!output.includes('No device available');
})
.orElse(() => fp_1.TaskEither.of(false));
return fp_1.TaskEither
.fromBind({
qsvEncoders: checkQsvEncodersTask,
qsvDecoders: checkQsvDecodersTask,
qsvDevice: testQsvDeviceTask,
})
.filter(({ qsvEncoders, qsvDecoders, qsvDevice }) => qsvEncoders && qsvDecoders && qsvDevice, () => (0, fp_1.createInternalError)('QSV acceleration not available'))
.map(() => this.getQsvConfig());
}
/**
* Get software encoding configuration (fallback)
* @returns Software encoding configuration
*/
getSoftwareConfig() {
return {
method: types_1.HardwareAccelerationMethod.NONE,
inputOptions: [],
outputOptions: {
h264: ['-c:v', 'libx264', '-preset', 'faster'],
h265: ['-c:v', 'libx265', '-preset', 'faster'],
},
videoFilters: {
scale: 'scale=w=%width%:h=%height%:force_original_aspect_ratio=decrease:force_divisible_by=2',
},
};
}
/**
* Get CUDA hardware acceleration configuration
* @returns CUDA hardware acceleration configuration
*/
getCudaConfig() {
return {
method: types_1.HardwareAccelerationMethod.CUDA,
inputOptions: [
'-hwaccel',
'cuda',
'-hwaccel_output_format',
'cuda',
],
outputOptions: {
h264: ['-c:v', 'h264_nvenc', '-preset', 'fast', '-tune', 'hq', '-rc', 'constqp', '-cq', '23'],
h265: ['-c:v', 'hevc_nvenc', '-preset', 'fast', '-tune', 'hq', '-rc', 'constqp', '-cq', '25'],
},
videoFilters: {
scale: 'scale_cuda=w=%width%:h=%height%:force_original_aspect_ratio=decrease:force_divisible_by=2',
deinterlace: 'yadif_cuda=0:-1:0',
},
};
}
/**
* Get VAAPI hardware acceleration configuration
* @param renderDevice Path to render device (e.g. /dev/dri/renderD128)
* @returns VAAPI hardware acceleration configuration
*/
getVAAPIConfig(renderDevice) {
return {
method: types_1.HardwareAccelerationMethod.VAAPI,
deviceInfo: renderDevice,
inputOptions: [
'-hwaccel',
'vaapi',
'-hwaccel_device',
renderDevice,
'-hwaccel_output_format',
'vaapi',
],
outputOptions: {
h264: ['-c:v', 'h264_vaapi', '-qp', '23'],
h265: ['-c:v', 'hevc_vaapi', '-qp', '25'],
},
videoFilters: {
scale: 'scale_vaapi=w=%width%:h=%height%:force_original_aspect_ratio=decrease:force_divisible_by=2',
},
};
}
/**
* Get VideoToolbox hardware acceleration configuration
* @returns VideoToolbox hardware acceleration configuration
*/
getVideoToolboxConfig() {
return {
method: types_1.HardwareAccelerationMethod.VIDEOTOOLBOX,
inputOptions: [],
outputOptions: {
h264: ['-c:v', 'h264_videotoolbox', '-q:v', '30'],
h265: ['-c:v', 'hevc_videotoolbox', '-q:v', '32'],
},
videoFilters: {
scale: 'scale=w=%width%:h=%height%:force_original_aspect_ratio=decrease:force_divisible_by=2',
},
};
}
/**
* Get AMD AMF hardware acceleration configuration
* @returns AMF hardware acceleration configuration
*/
getAmfConfig() {
return {
method: types_1.HardwareAccelerationMethod.AMF,
inputOptions: [],
outputOptions: {
h264: ['-c:v', 'h264_amf', '-quality', 'balanced'],
h265: ['-c:v', 'hevc_amf', '-quality', 'balanced'],
},
videoFilters: {
scale: 'scale=w=%width%:h=%height%:force_original_aspect_ratio=decrease:force_divisible_by=2',
},
};
}
/**
* Get Intel QuickSync Video hardware acceleration configuration
* @returns QSV hardware acceleration configuration
*/
getQsvConfig() {
return {
method: types_1.HardwareAccelerationMethod.QSV,
inputOptions: [
'-hwaccel',
'qsv',
'-hwaccel_output_format',
'qsv',
],
outputOptions: {
h264: ['-c:v', 'h264_qsv', '-q', '23'],
h265: ['-c:v', 'hevc_qsv', '-q', '25'],
},
videoFilters: {
scale: 'scale_qsv=w=%width%:h=%height%:force_divisible_by=2',
},
};
}
}
exports.HardwareAccelerationDetector = HardwareAccelerationDetector;
//# sourceMappingURL=hardwareAccelerationDetector.js.map