diff options
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 43 |
1 files changed, 16 insertions, 27 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 9755dd67c..3cc062b8c 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import * as ffmpeg from 'fluent-ffmpeg' | 1 | import * as ffmpeg from 'fluent-ffmpeg' |
2 | import { readFile, remove, writeFile } from 'fs-extra' | 2 | import { readFile, remove, writeFile } from 'fs-extra' |
3 | import { dirname, join } from 'path' | 3 | import { dirname, join } from 'path' |
4 | import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS, VIDEO_TRANSCODING_FPS } from '@server/initializers/constants' | 4 | import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS } from '@server/initializers/constants' |
5 | import { VideoResolution } from '../../shared/models/videos' | 5 | import { VideoResolution } from '../../shared/models/videos' |
6 | import { checkFFmpegEncoders } from '../initializers/checker-before-init' | 6 | import { checkFFmpegEncoders } from '../initializers/checker-before-init' |
7 | import { CONFIG } from '../initializers/config' | 7 | import { CONFIG } from '../initializers/config' |
8 | import { getAudioStream, getClosestFramerateStandard, getVideoFileFPS } from './ffprobe-utils' | 8 | import { computeFPS, getAudioStream, getVideoFileFPS } from './ffprobe-utils' |
9 | import { processImage } from './image-utils' | 9 | import { processImage } from './image-utils' |
10 | import { logger } from './logger' | 10 | import { logger } from './logger' |
11 | 11 | ||
@@ -223,7 +223,17 @@ async function getLiveTranscodingCommand (options: { | |||
223 | 223 | ||
224 | for (let i = 0; i < resolutions.length; i++) { | 224 | for (let i = 0; i < resolutions.length; i++) { |
225 | const resolution = resolutions[i] | 225 | const resolution = resolutions[i] |
226 | const baseEncoderBuilderParams = { input, availableEncoders, profile, fps, resolution, streamNum: i, videoType: 'live' as 'live' } | 226 | const resolutionFPS = computeFPS(fps, resolution) |
227 | |||
228 | const baseEncoderBuilderParams = { | ||
229 | input, | ||
230 | availableEncoders, | ||
231 | profile, | ||
232 | fps: resolutionFPS, | ||
233 | resolution, | ||
234 | streamNum: i, | ||
235 | videoType: 'live' as 'live' | ||
236 | } | ||
227 | 237 | ||
228 | { | 238 | { |
229 | const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType: 'VIDEO' })) | 239 | const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType: 'VIDEO' })) |
@@ -233,7 +243,7 @@ async function getLiveTranscodingCommand (options: { | |||
233 | 243 | ||
234 | command.outputOption(`-map [vout${resolution}]`) | 244 | command.outputOption(`-map [vout${resolution}]`) |
235 | 245 | ||
236 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps, streamNum: i }) | 246 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) |
237 | 247 | ||
238 | logger.debug('Apply ffmpeg live video params from %s.', builderResult.encoder, builderResult) | 248 | logger.debug('Apply ffmpeg live video params from %s.', builderResult.encoder, builderResult) |
239 | 249 | ||
@@ -249,7 +259,7 @@ async function getLiveTranscodingCommand (options: { | |||
249 | 259 | ||
250 | command.outputOption('-map a:0') | 260 | command.outputOption('-map a:0') |
251 | 261 | ||
252 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps, streamNum: i }) | 262 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) |
253 | 263 | ||
254 | logger.debug('Apply ffmpeg live audio params from %s.', builderResult.encoder, builderResult) | 264 | logger.debug('Apply ffmpeg live audio params from %s.', builderResult.encoder, builderResult) |
255 | 265 | ||
@@ -387,15 +397,7 @@ function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string | |||
387 | 397 | ||
388 | async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { | 398 | async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { |
389 | let fps = await getVideoFileFPS(options.inputPath) | 399 | let fps = await getVideoFileFPS(options.inputPath) |
390 | if ( | 400 | fps = computeFPS(fps, options.resolution) |
391 | // On small/medium resolutions, limit FPS | ||
392 | options.resolution !== undefined && | ||
393 | options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN && | ||
394 | fps > VIDEO_TRANSCODING_FPS.AVERAGE | ||
395 | ) { | ||
396 | // Get closest standard framerate by modulo: downsampling has to be done to a divisor of the nominal fps value | ||
397 | fps = getClosestFramerateStandard(fps, 'STANDARD') | ||
398 | } | ||
399 | 401 | ||
400 | command = await presetVideo(command, options.inputPath, options, fps) | 402 | command = await presetVideo(command, options.inputPath, options, fps) |
401 | 403 | ||
@@ -408,12 +410,6 @@ async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: Tran | |||
408 | command = command.size(size) | 410 | command = command.size(size) |
409 | } | 411 | } |
410 | 412 | ||
411 | // Hard FPS limits | ||
412 | if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, 'HD_STANDARD') | ||
413 | else if (fps < VIDEO_TRANSCODING_FPS.MIN) fps = VIDEO_TRANSCODING_FPS.MIN | ||
414 | |||
415 | command = command.withFPS(fps) | ||
416 | |||
417 | return command | 413 | return command |
418 | } | 414 | } |
419 | 415 | ||
@@ -422,13 +418,6 @@ async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: M | |||
422 | 418 | ||
423 | command = await presetVideo(command, options.audioPath, options) | 419 | command = await presetVideo(command, options.audioPath, options) |
424 | 420 | ||
425 | /* | ||
426 | MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html | ||
427 | Our target situation is closer to a livestream than a stream, | ||
428 | since we want to reduce as much a possible the encoding burden, | ||
429 | although not to the point of a livestream where there is a hard | ||
430 | constraint on the frames per second to be encoded. | ||
431 | */ | ||
432 | command.outputOption('-preset:v veryfast') | 421 | command.outputOption('-preset:v veryfast') |
433 | 422 | ||
434 | command = command.input(options.audioPath) | 423 | command = command.input(options.audioPath) |