( options: EncoderOptions, inputArgs: string[], outputPath: string, gpuEncoder: GpuEncoder = null, )
| 134 | export { detectGpuEncoder, type GpuEncoder } from "../utils/gpuEncoder.js"; |
| 135 | |
| 136 | export function buildEncoderArgs( |
| 137 | options: EncoderOptions, |
| 138 | inputArgs: string[], |
| 139 | outputPath: string, |
| 140 | gpuEncoder: GpuEncoder = null, |
| 141 | ): string[] { |
| 142 | const { |
| 143 | fps, |
| 144 | codec = "h264", |
| 145 | preset = "medium", |
| 146 | quality = 23, |
| 147 | bitrate, |
| 148 | pixelFormat = "yuv420p", |
| 149 | vp9CpuUsed, |
| 150 | useGpu = false, |
| 151 | } = options; |
| 152 | |
| 153 | // libx264 cannot encode HDR. If a caller passes hdr with codec=h264 we'd |
| 154 | // produce a "half-HDR" file (BT.2020 container tags but a BT.709 VUI block |
| 155 | // inside the bitstream) which confuses HDR-aware players. Strip hdr and |
| 156 | // log a warning so the caller picks h265 (the SDR-tagged output is honest). |
| 157 | if (options.hdr && codec === "h264") { |
| 158 | console.warn( |
| 159 | "[chunkEncoder] HDR is not supported with codec=h264 (libx264 has no HDR support). " + |
| 160 | "Stripping HDR metadata and tagging output as SDR/BT.709. Use codec=h265 for HDR output.", |
| 161 | ); |
| 162 | options = { ...options, hdr: undefined }; |
| 163 | } |
| 164 | |
| 165 | const args: string[] = [...inputArgs, "-r", fpsToFfmpegArg(fps)]; |
| 166 | const shouldUseGpu = useGpu && gpuEncoder !== null; |
| 167 | |
| 168 | if (codec === "h264" || codec === "h265") { |
| 169 | if (shouldUseGpu) { |
| 170 | const encoderName = getGpuEncoderName(gpuEncoder, codec); |
| 171 | args.push("-c:v", encoderName); |
| 172 | |
| 173 | switch (gpuEncoder) { |
| 174 | case "nvenc": |
| 175 | args.push("-preset", mapPresetForGpuEncoder("nvenc", preset)); |
| 176 | if (bitrate) args.push("-b:v", bitrate); |
| 177 | else args.push("-cq", String(quality)); |
| 178 | break; |
| 179 | case "videotoolbox": |
| 180 | if (bitrate) args.push("-b:v", bitrate); |
| 181 | else { |
| 182 | const vtQuality = Math.max(0, Math.min(100, 100 - quality * 2)); |
| 183 | args.push("-q:v", String(vtQuality)); |
| 184 | } |
| 185 | args.push("-allow_sw", "1"); |
| 186 | break; |
| 187 | case "vaapi": |
| 188 | args.unshift("-vaapi_device", "/dev/dri/renderD128"); |
| 189 | args.push("-vf", "format=nv12,hwupload"); |
| 190 | if (bitrate) args.push("-b:v", bitrate); |
| 191 | else args.push("-qp", String(quality)); |
| 192 | break; |
| 193 | case "qsv": |
no test coverage detected