diff options
author | Chocobozzz <me@florianbigard.com> | 2020-11-26 11:29:50 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2020-11-26 11:30:18 +0100 |
commit | 884d2c39ae23b44d0d037aaff0f66ad9ae0807ba (patch) | |
tree | 1e06fa6954a987b08894c574e31db7e8c106bafe /server/helpers | |
parent | 0151c41c65e0e3e94288314afe295358b9d698f7 (diff) | |
download | PeerTube-884d2c39ae23b44d0d037aaff0f66ad9ae0807ba.tar.gz PeerTube-884d2c39ae23b44d0d037aaff0f66ad9ae0807ba.tar.zst PeerTube-884d2c39ae23b44d0d037aaff0f66ad9ae0807ba.zip |
Fix live FPS limit
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 43 | ||||
-rw-r--r-- | server/helpers/ffprobe-utils.ts | 21 |
2 files changed, 37 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) |
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index 16b295bbd..1cf397767 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts | |||
@@ -247,6 +247,26 @@ function getClosestFramerateStandard (fps: number, type: 'HD_STANDARD' | 'STANDA | |||
247 | .sort((a, b) => fps % a - fps % b)[0] | 247 | .sort((a, b) => fps % a - fps % b)[0] |
248 | } | 248 | } |
249 | 249 | ||
250 | function computeFPS (fpsArg: number, resolution: VideoResolution) { | ||
251 | let fps = fpsArg | ||
252 | |||
253 | if ( | ||
254 | // On small/medium resolutions, limit FPS | ||
255 | resolution !== undefined && | ||
256 | resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN && | ||
257 | fps > VIDEO_TRANSCODING_FPS.AVERAGE | ||
258 | ) { | ||
259 | // Get closest standard framerate by modulo: downsampling has to be done to a divisor of the nominal fps value | ||
260 | fps = getClosestFramerateStandard(fps, 'STANDARD') | ||
261 | } | ||
262 | |||
263 | // Hard FPS limits | ||
264 | if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, 'HD_STANDARD') | ||
265 | else if (fps < VIDEO_TRANSCODING_FPS.MIN) fps = VIDEO_TRANSCODING_FPS.MIN | ||
266 | |||
267 | return fps | ||
268 | } | ||
269 | |||
250 | // --------------------------------------------------------------------------- | 270 | // --------------------------------------------------------------------------- |
251 | 271 | ||
252 | export { | 272 | export { |
@@ -259,6 +279,7 @@ export { | |||
259 | getVideoStreamFromFile, | 279 | getVideoStreamFromFile, |
260 | getDurationFromVideoFile, | 280 | getDurationFromVideoFile, |
261 | getAudioStream, | 281 | getAudioStream, |
282 | computeFPS, | ||
262 | getVideoFileFPS, | 283 | getVideoFileFPS, |
263 | ffprobePromise, | 284 | ffprobePromise, |
264 | getClosestFramerateStandard, | 285 | getClosestFramerateStandard, |