mirror of https://github.com/Chocobozzz/PeerTube
				
				
				
			
		
			
				
	
	
		
			115 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			115 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
import { Job } from 'bull'
 | 
						|
import ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg'
 | 
						|
import { execPromise } from '@server/helpers/core-utils'
 | 
						|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
 | 
						|
import { CONFIG } from '@server/initializers/config'
 | 
						|
import { FFMPEG_NICE } from '@server/initializers/constants'
 | 
						|
import { EncoderOptions } from '@shared/models'
 | 
						|
 | 
						|
const lTags = loggerTagsFactory('ffmpeg')
 | 
						|
 | 
						|
type StreamType = 'audio' | 'video'
 | 
						|
 | 
						|
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: FfmpegCommand
 | 
						|
  silent?: boolean // false by default
 | 
						|
  job?: Job
 | 
						|
}) {
 | 
						|
  const { command, silent = false, job } = options
 | 
						|
 | 
						|
  return new Promise<void>((res, rej) => {
 | 
						|
    let shellCommand: string
 | 
						|
 | 
						|
    command.on('start', cmdline => { shellCommand = cmdline })
 | 
						|
 | 
						|
    command.on('error', (err, stdout, stderr) => {
 | 
						|
      if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr, shellCommand, ...lTags() })
 | 
						|
 | 
						|
      rej(err)
 | 
						|
    })
 | 
						|
 | 
						|
    command.on('end', (stdout, stderr) => {
 | 
						|
      logger.debug('FFmpeg command ended.', { stdout, stderr, shellCommand, ...lTags() })
 | 
						|
 | 
						|
      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, ...lTags() }))
 | 
						|
      })
 | 
						|
    }
 | 
						|
 | 
						|
    command.run()
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
function buildStreamSuffix (base: string, streamNum?: number) {
 | 
						|
  if (streamNum !== undefined) {
 | 
						|
    return `${base}:${streamNum}`
 | 
						|
  }
 | 
						|
 | 
						|
  return base
 | 
						|
}
 | 
						|
 | 
						|
function getScaleFilter (options: EncoderOptions): string {
 | 
						|
  if (options.scaleFilter) return options.scaleFilter.name
 | 
						|
 | 
						|
  return 'scale'
 | 
						|
}
 | 
						|
 | 
						|
export {
 | 
						|
  getFFmpeg,
 | 
						|
  getFFmpegVersion,
 | 
						|
  runCommand,
 | 
						|
  StreamType,
 | 
						|
  buildStreamSuffix,
 | 
						|
  getScaleFilter
 | 
						|
}
 |