aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/helpers/ffmpeg-utils.ts52
1 files changed, 28 insertions, 24 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index a964abdd4..17f35fe8d 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -116,28 +116,27 @@ type TranscodeOptions = {
116 116
117function transcode (options: TranscodeOptions) { 117function transcode (options: TranscodeOptions) {
118 return new Promise<void>(async (res, rej) => { 118 return new Promise<void>(async (res, rej) => {
119 let fps = await getVideoFileFPS(options.inputPath)
120 // On small/medium resolutions, limit FPS
121 if (options.resolution !== undefined &&
122 options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
123 fps > VIDEO_TRANSCODING_FPS.AVERAGE) {
124 fps = VIDEO_TRANSCODING_FPS.AVERAGE
125 }
126
119 let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) 127 let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING })
120 .output(options.outputPath) 128 .output(options.outputPath)
121 .preset(standard) 129 command = await presetH264(command, options.resolution, fps)
122 130
123 if (CONFIG.TRANSCODING.THREADS > 0) { 131 if (CONFIG.TRANSCODING.THREADS > 0) {
124 // if we don't set any threads ffmpeg will chose automatically 132 // if we don't set any threads ffmpeg will chose automatically
125 command = command.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) 133 command = command.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
126 } 134 }
127 135
128 let fps = await getVideoFileFPS(options.inputPath)
129 if (options.resolution !== undefined) { 136 if (options.resolution !== undefined) {
130 // '?x720' or '720x?' for example 137 // '?x720' or '720x?' for example
131 const size = options.isPortraitMode === true ? `${options.resolution}x?` : `?x${options.resolution}` 138 const size = options.isPortraitMode === true ? `${options.resolution}x?` : `?x${options.resolution}`
132 command = command.size(size) 139 command = command.size(size)
133
134 // On small/medium resolutions, limit FPS
135 if (
136 options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
137 fps > VIDEO_TRANSCODING_FPS.AVERAGE
138 ) {
139 fps = VIDEO_TRANSCODING_FPS.AVERAGE
140 }
141 } 140 }
142 141
143 if (fps) { 142 if (fps) {
@@ -148,12 +147,6 @@ function transcode (options: TranscodeOptions) {
148 command = command.withFPS(fps) 147 command = command.withFPS(fps)
149 } 148 }
150 149
151 // Constrained Encoding (VBV)
152 // https://slhck.info/video/2017/03/01/rate-control.html
153 // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
154 const targetBitrate = getTargetBitrate(options.resolution, fps, VIDEO_TRANSCODING_FPS)
155 command.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`])
156
157 command 150 command
158 .on('error', (err, stdout, stderr) => { 151 .on('error', (err, stdout, stderr) => {
159 logger.error('Error in transcoding job.', { stdout, stderr }) 152 logger.error('Error in transcoding job.', { stdout, stderr })
@@ -199,9 +192,9 @@ function getVideoFileStream (path: string) {
199 * and quality. Superfast and ultrafast will give you better 192 * and quality. Superfast and ultrafast will give you better
200 * performance, but then quality is noticeably worse. 193 * performance, but then quality is noticeably worse.
201 */ 194 */
202function veryfast (_ffmpeg) { 195async function presetH264VeryFast (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
203 _ffmpeg 196 const localFfmpeg = await presetH264(ffmpeg, resolution, fps)
204 .preset(standard) 197 localFfmpeg
205 .outputOption('-preset:v veryfast') 198 .outputOption('-preset:v veryfast')
206 .outputOption(['--aq-mode=2', '--aq-strength=1.3']) 199 .outputOption(['--aq-mode=2', '--aq-strength=1.3'])
207 /* 200 /*
@@ -220,9 +213,9 @@ function veryfast (_ffmpeg) {
220/** 213/**
221 * A preset optimised for a stillimage audio video 214 * A preset optimised for a stillimage audio video
222 */ 215 */
223function audio (_ffmpeg) { 216async function presetStillImageWithAudio (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
224 _ffmpeg 217 const localFfmpeg = await presetH264VeryFast(ffmpeg, resolution, fps)
225 .preset(veryfast) 218 localFfmpeg
226 .outputOption('-tune stillimage') 219 .outputOption('-tune stillimage')
227} 220}
228 221
@@ -290,8 +283,8 @@ namespace audio {
290 * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel 283 * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel
291 * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr 284 * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr
292 */ 285 */
293async function standard (_ffmpeg) { 286async function presetH264 (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
294 let localFfmpeg = _ffmpeg 287 let localFfmpeg = ffmpeg
295 .format('mp4') 288 .format('mp4')
296 .videoCodec('libx264') 289 .videoCodec('libx264')
297 .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution 290 .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution
@@ -324,5 +317,16 @@ async function standard (_ffmpeg) {
324 317
325 if (bitrate !== undefined) return localFfmpeg.audioBitrate(bitrate) 318 if (bitrate !== undefined) return localFfmpeg.audioBitrate(bitrate)
326 319
320 // Constrained Encoding (VBV)
321 // https://slhck.info/video/2017/03/01/rate-control.html
322 // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
323 const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
324 localFfmpeg.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`])
325
326 // Keyframe interval of 2 seconds for faster seeking and resolution switching.
327 // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html
328 // https://superuser.com/a/908325
329 localFfmpeg.outputOption(`-g ${ fps * 2 }`)
330
327 return localFfmpeg 331 return localFfmpeg
328} 332}