Files
scrypted/common/src/ffmpeg-hardware-acceleration.ts
2022-07-19 08:32:25 -07:00

151 lines
4.2 KiB
TypeScript

import os from 'os';
const PI_MODEL_NO = [
// https://www.raspberrypi.org/documentation/hardware/raspberrypi/
'BCM2708',
'BCM2709',
'BCM2710',
'BCM2835', // Raspberry Pi 1 and Zero
'BCM2836', // Raspberry Pi 2
'BCM2837', // Raspberry Pi 3 (and later Raspberry Pi 2)
'BCM2837B0', // Raspberry Pi 3B+ and 3A+
'BCM2711' // Raspberry Pi 4B
];
function isPi(model: string) {
return PI_MODEL_NO.indexOf(model) > -1;
}
export function isRaspberryPi() {
let cpuInfo: string;
try {
cpuInfo = require('realfs').readFileSync('/proc/cpuinfo', { encoding: 'utf8' });
}
catch (e) {
// if this fails, this is probably not a pi
return false;
}
const model = cpuInfo
.split('\n')
.map(line => line.replace(/\t/g, ''))
.filter(line => line.length > 0)
.map(line => line.split(':'))
.map(pair => pair.map(entry => entry.trim()))
.filter(pair => pair[0] === 'Hardware')
if (!model || model.length == 0) {
return false;
}
const number = model[0][1];
return isPi(number);
}
export type CodecArgs = { [type: string]: string[] };
const V4L2 = 'Video4Linux (Docker compatible)';
export function getH264DecoderArgs(): CodecArgs {
if (isRaspberryPi()) {
const ret: CodecArgs = {};
// ret['Raspberry Pi MMAL'] = ['-c:v', 'h264_mmal'];
// ret[V4L2] = ['-c:v', 'h264_v4l2m2m'];
return ret;
}
else if (os.platform() === 'darwin') {
return {
// specifying videotoolbox seems to cause issues with multiple? unclear why.
// the ffmpeg process tears down, yet it seems like something is not disposed.
'VideoToolbox': ['-hwaccel', 'auto']
}
}
const ret: CodecArgs = {
'Nvidia CUDA': [
'-vsync', '0', '-hwaccel', 'cuda', '-hwaccel_output_format', 'nv12',
],
'Nvidia CUVID': [
'-vsync', '0', '-hwaccel', 'cuvid', '-c:v', 'h264_cuvid', '-hwaccel_output_format', 'nv12',
],
};
if (isRaspberryPi()) {
ret['Raspberry Pi'] = ['-c:v', 'h264_mmal'];
ret[V4L2] = ['-c:v', 'h264_v4l2m2m'];
}
else if (os.platform() === 'linux') {
ret[V4L2] = ['-c:v', 'h264_v4l2m2m'];
}
else if (os.platform() === 'win32') {
ret['Intel QuickSync'] = ['-c:v', 'h264_qsv'];
}
else {
return {};
}
return ret;
}
const LIBX264_ENCODER_TITLE = 'libx264 (Software)';
export function getH264EncoderArgs() {
const encoders: { [type: string]: string } = {};
encoders['Copy Video, Transcode Audio'] = 'copy';
if (isRaspberryPi()) {
// encoders['Raspberry Pi OMX'] = 'h264_omx';
// encoders[V4L2] = 'h264_v4l2m2m';
}
else if (os.platform() === 'darwin') {
encoders['VideoToolbox'] = 'h264_videotoolbox';
}
else if (os.platform() === 'win32') {
// h264_amf h264_nvenc h264_qsv
encoders['Intel QuickSync'] = 'h264_qsv';
encoders['AMD'] = 'h264_amf';
encoders['Nvidia'] = 'h264_nvenc';
}
else if (os.platform() === 'linux') {
// h264_v4l2m2m h264_vaapi nvenc_h264
encoders['V4L2'] = 'h264_v4l2m2m';
encoders['VAAPI'] = 'h264_vaapi';
encoders['Nvidia'] = 'h264_nvenc';
}
const encoderArgs: CodecArgs = {};
for (const [name, encoder] of Object.entries(encoders)) {
encoderArgs[name] = [
'-c:v',
encoder,
]
}
if (isRaspberryPi()) {
encoderArgs['Raspberry Pi'] = [
'-pix_fmt', 'yuv420p', '-c:v', 'h264_v4l2m2m',
]
}
encoderArgs[LIBX264_ENCODER_TITLE] = getDebugModeH264EncoderArgs();
return encoderArgs;
}
export function getDebugModeH264EncoderArgs() {
return [
"-c:v", "libx264",
// consider using yuv420 as that is simpler?
// https://trac.ffmpeg.org/wiki/Encode/H.264
'-pix_fmt', 'yuvj420p',
// this seems to force constrained baseline? even if the
// profile is explicitly set?
'-preset', 'ultrafast',
// this seems to be very picky about bitrate and fails easy.
// '-tune', 'zerolatency',
"-bf", "0",
];
}