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", ]; }