]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/helpers/ffmpeg-utils.ts
Fix weblate conflicts
[github/Chocobozzz/PeerTube.git] / server / helpers / ffmpeg-utils.ts
index 9755dd67c7dbf87af7a2d2d90cd69fc1901cc7c9..7c997877cff112a07d4caad0b3cc06d771637164 100644 (file)
@@ -1,11 +1,11 @@
 import * as ffmpeg from 'fluent-ffmpeg'
 import { readFile, remove, writeFile } from 'fs-extra'
 import { dirname, join } from 'path'
-import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS, VIDEO_TRANSCODING_FPS } from '@server/initializers/constants'
+import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS } from '@server/initializers/constants'
 import { VideoResolution } from '../../shared/models/videos'
 import { checkFFmpegEncoders } from '../initializers/checker-before-init'
 import { CONFIG } from '../initializers/config'
-import { getAudioStream, getClosestFramerateStandard, getVideoFileFPS } from './ffprobe-utils'
+import { computeFPS, getAudioStream, getVideoFileFPS } from './ffprobe-utils'
 import { processImage } from './image-utils'
 import { logger } from './logger'
 
@@ -221,9 +221,21 @@ async function getLiveTranscodingCommand (options: {
 
   command.outputOption('-preset superfast')
 
+  addDefaultEncoderGlobalParams({ command })
+
   for (let i = 0; i < resolutions.length; i++) {
     const resolution = resolutions[i]
-    const baseEncoderBuilderParams = { input, availableEncoders, profile, fps, resolution, streamNum: i, videoType: 'live' as 'live' }
+    const resolutionFPS = computeFPS(fps, resolution)
+
+    const baseEncoderBuilderParams = {
+      input,
+      availableEncoders,
+      profile,
+      fps: resolutionFPS,
+      resolution,
+      streamNum: i,
+      videoType: 'live' as 'live'
+    }
 
     {
       const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType: 'VIDEO' }))
@@ -233,7 +245,7 @@ async function getLiveTranscodingCommand (options: {
 
       command.outputOption(`-map [vout${resolution}]`)
 
-      addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps, streamNum: i })
+      addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
 
       logger.debug('Apply ffmpeg live video params from %s.', builderResult.encoder, builderResult)
 
@@ -249,7 +261,7 @@ async function getLiveTranscodingCommand (options: {
 
       command.outputOption('-map a:0')
 
-      addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps, streamNum: i })
+      addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
 
       logger.debug('Apply ffmpeg live audio params from %s.', builderResult.encoder, builderResult)
 
@@ -335,6 +347,23 @@ export {
 // Default options
 // ---------------------------------------------------------------------------
 
+function addDefaultEncoderGlobalParams (options: {
+  command: ffmpeg.FfmpegCommand
+}) {
+  const { command } = options
+
+  // avoid issues when transcoding some files: https://trac.ffmpeg.org/ticket/6375
+  command.outputOption('-max_muxing_queue_size 1024')
+         // strip all metadata
+         .outputOption('-map_metadata -1')
+         // NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
+         .outputOption('-b_strategy 1')
+         // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16
+         .outputOption('-bf 16')
+         // allows import of source material with incompatible pixel formats (e.g. MJPEG video)
+         .outputOption('-pix_fmt yuv420p')
+}
+
 function addDefaultEncoderParams (options: {
   command: ffmpeg.FfmpegCommand
   encoder: 'libx264' | string
@@ -345,23 +374,13 @@ function addDefaultEncoderParams (options: {
 
   if (encoder === 'libx264') {
     // 3.1 is the minimal resource allocation for our highest supported resolution
-    command.outputOption('-level 3.1')
-        // NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
-        .outputOption('-b_strategy 1')
-        // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16
-        .outputOption('-bf 16')
-        // allows import of source material with incompatible pixel formats (e.g. MJPEG video)
-        .outputOption(buildStreamSuffix('-pix_fmt', streamNum) + ' yuv420p')
-        // strip all metadata
-        .outputOption('-map_metadata -1')
-        // avoid issues when transcoding some files: https://trac.ffmpeg.org/ticket/6375
-        .outputOption(buildStreamSuffix('-max_muxing_queue_size', streamNum) + ' 1024')
+    command.outputOption(buildStreamSuffix('-level:v', streamNum) + ' 3.1')
 
     if (fps) {
       // Keyframe interval of 2 seconds for faster seeking and resolution switching.
       // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html
       // https://superuser.com/a/908325
-      command.outputOption('-g ' + (fps * 2))
+      command.outputOption(buildStreamSuffix('-g:v', streamNum) + ' ' + (fps * 2))
     }
   }
 }
@@ -387,15 +406,7 @@ function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string
 
 async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) {
   let fps = await getVideoFileFPS(options.inputPath)
-  if (
-    // On small/medium resolutions, limit FPS
-    options.resolution !== undefined &&
-    options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
-    fps > VIDEO_TRANSCODING_FPS.AVERAGE
-  ) {
-    // Get closest standard framerate by modulo: downsampling has to be done to a divisor of the nominal fps value
-    fps = getClosestFramerateStandard(fps, 'STANDARD')
-  }
+  fps = computeFPS(fps, options.resolution)
 
   command = await presetVideo(command, options.inputPath, options, fps)
 
@@ -408,12 +419,6 @@ async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: Tran
     command = command.size(size)
   }
 
-  // Hard FPS limits
-  if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, 'HD_STANDARD')
-  else if (fps < VIDEO_TRANSCODING_FPS.MIN) fps = VIDEO_TRANSCODING_FPS.MIN
-
-  command = command.withFPS(fps)
-
   return command
 }
 
@@ -422,13 +427,6 @@ async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: M
 
   command = await presetVideo(command, options.audioPath, options)
 
-  /*
-  MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html
-  Our target situation is closer to a livestream than a stream,
-  since we want to reduce as much a possible the encoding burden,
-  although not to the point of a livestream where there is a hard
-  constraint on the frames per second to be encoded.
-  */
   command.outputOption('-preset:v veryfast')
 
   command = command.input(options.audioPath)
@@ -548,6 +546,8 @@ async function presetVideo (
     .format('mp4')
     .outputOption('-movflags faststart')
 
+  addDefaultEncoderGlobalParams({ command })
+
   // Audio encoder
   const parsedAudio = await getAudioStream(input)