UNPKG

@xan105/video-capture

Version:

Record your screen using hardware accelerated encoder.

153 lines (129 loc) 4.65 kB
/* Copyright (c) Anthony Beaumont This source code is licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3 found in the LICENSE file in the root directory of this source tree. */ import { spawn } from "node:child_process"; import { join, parse } from "node:path"; import { Failure } from "@xan105/error"; import { dirname } from "@xan105/fs/path"; const hw_codec = ["h264_nvenc","h264_amf","hevc_nvenc","hevc_amf"]; function getDefaultProfile(codec, bits10 = false){ const profiles = { h264_nvenc: "-rc:v vbr -level:v 4.2 -g:v 120 -bf:v 3 -qp:v 19", h264_amf: "-rc:v cqp -level:v 4.2 -g:v 120 -bf:v 3", hevc_nvenc: "-rc:v vbr -level:v 4.1 -qp:v 19", hevc_amf: "-rc:v cqp -level:v 4.1" }; const profile = profiles[codec]; if(codec === "hevc_nvenc" || codec === "hevc_amf" ) { if(bits10 === true) { return "-profile:v main10 " + profile; } else { return "-profile:v main " + profile; } } else { return "-profile:v high " + profile; } } function ffmpeg(cmdline) { return new Promise((resolve, reject) => { const binaryPath = join(dirname(import.meta.url), "/dist/ffmpeg.exe"); const ffmpeg = spawn(`"${binaryPath}"`, cmdline, { cwd: parse(binaryPath).dir, stdio: ["pipe", "pipe", "pipe"], windowsHide: true, windowsVerbatimArguments: true, shell: true, }); let errorMessage = []; ffmpeg.stderr.on("data", (data) => { errorMessage.push(data); }); ffmpeg.on("exit", (code) => { if (code == 0) { return resolve(); } else { return reject(new Failure(errorMessage.join(""), { code: "ERR_FFMPEG", info: { cmdline: cmdline }})); } }); }); } function makeCommandLine(options, codec) { const videoCodec = ["-c:v", codec]; const audioCodec = ["-c:a", "aac"]; const videoInterface = [ "-f", "gdigrab", "-t", options.timeLength, "-framerate", options.framerate, "-probesize", `${options.probesize}M`, "-draw_mouse", options.mouse ? "1" : "0", "-offset_x", "0", "-offset_y", "0", "-video_size", options.size, "-show_region", "0", //offset_x/y: Active display is always at 0,0 "-thread_queue_size", options.threadQueue, "-i", "desktop", ]; const audioInterface = [ "-f", "dshow", "-t", options.timeLength, "-audio_buffer_size", // Reduce startup latency (https://trac.ffmpeg.org/wiki/DirectShow#BufferingLatency) "50", // msdn suggests 80ms "-thread_queue_size", options.threadQueue, "-i", `audio="${options.audioInterface}"`, ]; let cmdline = ["-hide_banner"]; //Suppress ffmpeg printing copyright notice, build options and library versions //Input Interface cmdline = cmdline.concat(videoInterface); if (options.audioInterface) cmdline = cmdline.concat(audioInterface); //Codec cmdline = cmdline.concat(videoCodec); if (options.audioInterface) cmdline = cmdline.concat(audioCodec); //Output encoding //Video bitrate cmdline = cmdline.concat(["-b:v",`${options.bitrate.video}k`, "-minrate:v",`${options.bitrate.min}k`, "-maxrate:v",`${options.bitrate.max}k`, "-bufsize:v",`${options.bitrate.max}k`]); const videoEncodingOptions = options.videoEncodingOptions || getDefaultProfile(codec, options.bits10); cmdline = cmdline.concat(videoEncodingOptions.split(" "), ["-r:v", options.framerate]); if (options.audioInterface) { cmdline = cmdline.concat(["-b:a", `${options.bitrate.audio}k`]); if (options.audioEncodingOptions) cmdline = cmdline.concat(options.audioEncodingOptions.split(" ")); } //Misc //Force encoding for 'dumb players' which only support the YUV planar color space with 4:2:0 chroma subsampling //NB: HEVC is already 4:2:0 chroma sampling (version 1 profile) if (options.bits10 && (codec === "hevc_nvenc" || codec === "hevc_amf" )){ cmdline = cmdline.concat(["-pix_fmt","yuv420p10le"]); //10bits } else { cmdline = cmdline.concat(["-pix_fmt","yuv420p"]); //8bits } if (options.audioInterface) { if (options.audioDelay > 0) { cmdline = cmdline.concat(["-af", `"adelay=delays=${options.audioDelay}:all=1, aresample=async=1"`]); //Apply an audio filter to delay all audio channels for x ms, compensate audio drift (?) } else { cmdline = cmdline.concat(["-af", `"aresample=async=1"`]); //compensate audio drift (?) } } cmdline.push("-y"); //Overwrite target file if any return cmdline; } export { hw_codec, ffmpeg, makeCommandLine };