+
+function applyEncoderOptions (command: ffmpeg.FfmpegCommand, options: EncoderOptions): ffmpeg.FfmpegCommand {
+ return command
+ .inputOptions(options.inputOptions ?? [])
+ .outputOptions(options.outputOptions ?? [])
+}
+
+function getScaleFilter (options: EncoderOptions): string {
+ if (options.scaleFilter) return options.scaleFilter.name
+
+ return 'scale'
+}
+
+// ---------------------------------------------------------------------------
+// Utils
+// ---------------------------------------------------------------------------
+
+function getFFmpeg (input: string, type: 'live' | 'vod') {
+ // We set cwd explicitly because ffmpeg appears to create temporary files when trancoding which fails in read-only file systems
+ const command = ffmpeg(input, {
+ niceness: type === 'live' ? FFMPEG_NICE.LIVE : FFMPEG_NICE.VOD,
+ cwd: CONFIG.STORAGE.TMP_DIR
+ })
+
+ const threads = type === 'live'
+ ? CONFIG.LIVE.TRANSCODING.THREADS
+ : CONFIG.TRANSCODING.THREADS
+
+ if (threads > 0) {
+ // If we don't set any threads ffmpeg will chose automatically
+ command.outputOption('-threads ' + threads)
+ }
+
+ return command
+}
+
+function getFFmpegVersion () {
+ return new Promise<string>((res, rej) => {
+ (ffmpeg() as any)._getFfmpegPath((err, ffmpegPath) => {
+ if (err) return rej(err)
+ if (!ffmpegPath) return rej(new Error('Could not find ffmpeg path'))
+
+ return execPromise(`${ffmpegPath} -version`)
+ .then(stdout => {
+ const parsed = stdout.match(/ffmpeg version .?(\d+\.\d+(\.\d+)?)/)
+ if (!parsed || !parsed[1]) return rej(new Error(`Could not find ffmpeg version in ${stdout}`))
+
+ // Fix ffmpeg version that does not include patch version (4.4 for example)
+ let version = parsed[1]
+ if (version.match(/^\d+\.\d+$/)) {
+ version += '.0'
+ }
+
+ return res(version)
+ })
+ .catch(err => rej(err))
+ })
+ })
+}
+
+async function runCommand (options: {
+ command: ffmpeg.FfmpegCommand
+ silent?: boolean // false
+ job?: Job
+}) {
+ const { command, silent = false, job } = options
+
+ return new Promise<void>((res, rej) => {
+ command.on('error', (err, stdout, stderr) => {
+ if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr })
+
+ rej(err)
+ })
+
+ command.on('end', (stdout, stderr) => {
+ logger.debug('FFmpeg command ended.', { stdout, stderr })
+
+ res()
+ })
+
+ if (job) {
+ command.on('progress', progress => {
+ if (!progress.percent) return
+
+ job.progress(Math.round(progress.percent))
+ .catch(err => logger.warn('Cannot set ffmpeg job progress.', { err }))
+ })
+ }
+
+ command.run()
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getLiveTranscodingCommand,
+ getLiveMuxingCommand,
+ buildStreamSuffix,
+ convertWebPToJPG,
+ processGIF,
+ generateImageFromVideoFile,
+ TranscodeOptions,
+ TranscodeOptionsType,
+ transcode,
+ runCommand,
+ getFFmpegVersion,
+
+ resetSupportedEncoders,
+
+ // builders
+ buildx264VODCommand
+}