1 import { FfmpegCommand } from 'fluent-ffmpeg'
2 import { getFFmpegCommandWrapperOptions } from '@server/helpers/ffmpeg'
3 import { logger } from '@server/helpers/logger'
4 import { CONFIG } from '@server/initializers/config'
5 import { VIDEO_LIVE } from '@server/initializers/constants'
6 import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/default-transcoding-profiles'
7 import { FFmpegLive } from '@shared/ffmpeg'
8 import { getLiveSegmentTime } from '../../live-utils'
9 import { AbstractTranscodingWrapper } from './abstract-transcoding-wrapper'
11 export class FFmpegTranscodingWrapper extends AbstractTranscodingWrapper {
12 private ffmpegCommand: FfmpegCommand
14 private aborted = false
15 private errored = false
19 this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED
20 ? await this.buildFFmpegLive().getLiveTranscodingCommand({
21 inputUrl: this.inputLocalUrl,
23 outPath: this.outDirectory,
24 masterPlaylistName: this.streamingPlaylist.playlistFilename,
26 segmentListSize: this.segmentListSize,
27 segmentDuration: this.segmentDuration,
29 toTranscode: this.toTranscode,
31 bitrate: this.bitrate,
34 hasAudio: this.hasAudio
36 : this.buildFFmpegLive().getLiveMuxingCommand({
37 inputUrl: this.inputLocalUrl,
38 outPath: this.outDirectory,
40 masterPlaylistName: this.streamingPlaylist.playlistFilename,
42 segmentListSize: VIDEO_LIVE.SEGMENTS_LIST_SIZE,
43 segmentDuration: getLiveSegmentTime(this.videoLive.latencyMode)
46 logger.info('Running local live muxing/transcoding for %s.', this.videoUUID, this.lTags())
48 let ffmpegShellCommand: string
49 this.ffmpegCommand.on('start', cmdline => {
50 ffmpegShellCommand = cmdline
52 logger.debug('Running ffmpeg command for live', { ffmpegShellCommand, ...this.lTags() })
55 this.ffmpegCommand.on('error', (err, stdout, stderr) => {
56 this.onFFmpegError({ err, stdout, stderr, ffmpegShellCommand })
59 this.ffmpegCommand.on('end', () => {
63 this.ffmpegCommand.run()
67 if (this.ended || this.errored || this.aborted) return
69 logger.debug('Killing ffmpeg after live abort of ' + this.videoUUID, this.lTags())
71 this.ffmpegCommand.kill('SIGINT')
77 private onFFmpegError (options: {
81 ffmpegShellCommand: string
83 const { err, stdout, stderr, ffmpegShellCommand } = options
85 // Don't care that we killed the ffmpeg process
86 if (err?.message?.includes('Exiting normally')) return
87 if (this.ended || this.errored || this.aborted) return
89 logger.error('FFmpeg transcoding error.', { err, stdout, stderr, ffmpegShellCommand, ...this.lTags() })
92 this.emit('error', { err })
95 private onFFmpegEnded () {
96 if (this.ended || this.errored || this.aborted) return
98 logger.debug('Live ffmpeg transcoding ended for ' + this.videoUUID, this.lTags())
104 private buildFFmpegLive () {
105 return new FFmpegLive(getFFmpegCommandWrapperOptions('live', VideoTranscodingProfilesManager.Instance.getAvailableEncoders()))