diff options
author | Chocobozzz <me@florianbigard.com> | 2021-08-06 13:35:25 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-08-06 14:13:26 +0200 |
commit | 679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3 (patch) | |
tree | 03abf589275db05e5b1fa1c89f57049cd807324a /server/helpers | |
parent | c826f34a45757b324a20f71665b44ed10e6953b5 (diff) | |
download | PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.gz PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.zst PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.zip |
Improve target bitrate calculation
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 35 | ||||
-rw-r--r-- | server/helpers/ffprobe-utils.ts | 16 |
2 files changed, 33 insertions, 18 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 7f84a049f..830625cc6 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -3,10 +3,18 @@ import * as ffmpeg from 'fluent-ffmpeg' | |||
3 | import { readFile, remove, writeFile } from 'fs-extra' | 3 | import { readFile, remove, writeFile } from 'fs-extra' |
4 | import { dirname, join } from 'path' | 4 | import { dirname, join } from 'path' |
5 | import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants' | 5 | import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants' |
6 | import { AvailableEncoders, EncoderOptions, EncoderOptionsBuilder, EncoderProfile, VideoResolution } from '../../shared/models/videos' | 6 | import { pick } from '@shared/core-utils' |
7 | import { | ||
8 | AvailableEncoders, | ||
9 | EncoderOptions, | ||
10 | EncoderOptionsBuilder, | ||
11 | EncoderOptionsBuilderParams, | ||
12 | EncoderProfile, | ||
13 | VideoResolution | ||
14 | } from '../../shared/models/videos' | ||
7 | import { CONFIG } from '../initializers/config' | 15 | import { CONFIG } from '../initializers/config' |
8 | import { execPromise, promisify0 } from './core-utils' | 16 | import { execPromise, promisify0 } from './core-utils' |
9 | import { computeFPS, ffprobePromise, getAudioStream, getVideoFileBitrate, getVideoFileFPS } from './ffprobe-utils' | 17 | import { computeFPS, ffprobePromise, getAudioStream, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from './ffprobe-utils' |
10 | import { processImage } from './image-utils' | 18 | import { processImage } from './image-utils' |
11 | import { logger } from './logger' | 19 | import { logger } from './logger' |
12 | 20 | ||
@@ -217,13 +225,16 @@ async function getLiveTranscodingCommand (options: { | |||
217 | masterPlaylistName: string | 225 | masterPlaylistName: string |
218 | 226 | ||
219 | resolutions: number[] | 227 | resolutions: number[] |
228 | |||
229 | // Input information | ||
220 | fps: number | 230 | fps: number |
221 | bitrate: number | 231 | bitrate: number |
232 | ratio: number | ||
222 | 233 | ||
223 | availableEncoders: AvailableEncoders | 234 | availableEncoders: AvailableEncoders |
224 | profile: string | 235 | profile: string |
225 | }) { | 236 | }) { |
226 | const { rtmpUrl, outPath, resolutions, fps, bitrate, availableEncoders, profile, masterPlaylistName } = options | 237 | const { rtmpUrl, outPath, resolutions, fps, bitrate, availableEncoders, profile, masterPlaylistName, ratio } = options |
227 | const input = rtmpUrl | 238 | const input = rtmpUrl |
228 | 239 | ||
229 | const command = getFFmpeg(input, 'live') | 240 | const command = getFFmpeg(input, 'live') |
@@ -253,9 +264,12 @@ async function getLiveTranscodingCommand (options: { | |||
253 | availableEncoders, | 264 | availableEncoders, |
254 | profile, | 265 | profile, |
255 | 266 | ||
256 | fps: resolutionFPS, | ||
257 | inputBitrate: bitrate, | 267 | inputBitrate: bitrate, |
268 | inputRatio: ratio, | ||
269 | |||
258 | resolution, | 270 | resolution, |
271 | fps: resolutionFPS, | ||
272 | |||
259 | streamNum: i, | 273 | streamNum: i, |
260 | videoType: 'live' as 'live' | 274 | videoType: 'live' as 'live' |
261 | } | 275 | } |
@@ -502,7 +516,7 @@ function getHLSVideoPath (options: HLSTranscodeOptions | HLSFromTSTranscodeOptio | |||
502 | // Run encoder builder depending on available encoders | 516 | // Run encoder builder depending on available encoders |
503 | // Try encoders by priority: if the encoder is available, run the chosen profile or fallback to the default one | 517 | // Try encoders by priority: if the encoder is available, run the chosen profile or fallback to the default one |
504 | // If the default one does not exist, check the next encoder | 518 | // If the default one does not exist, check the next encoder |
505 | async function getEncoderBuilderResult (options: { | 519 | async function getEncoderBuilderResult (options: EncoderOptionsBuilderParams & { |
506 | streamType: 'video' | 'audio' | 520 | streamType: 'video' | 'audio' |
507 | input: string | 521 | input: string |
508 | 522 | ||
@@ -510,13 +524,8 @@ async function getEncoderBuilderResult (options: { | |||
510 | profile: string | 524 | profile: string |
511 | 525 | ||
512 | videoType: 'vod' | 'live' | 526 | videoType: 'vod' | 'live' |
513 | |||
514 | resolution: number | ||
515 | inputBitrate: number | ||
516 | fps?: number | ||
517 | streamNum?: number | ||
518 | }) { | 527 | }) { |
519 | const { availableEncoders, input, profile, resolution, streamType, fps, inputBitrate, streamNum, videoType } = options | 528 | const { availableEncoders, profile, streamType, videoType } = options |
520 | 529 | ||
521 | const encodersToTry = availableEncoders.encodersToTry[videoType][streamType] | 530 | const encodersToTry = availableEncoders.encodersToTry[videoType][streamType] |
522 | const encoders = availableEncoders.available[videoType] | 531 | const encoders = availableEncoders.available[videoType] |
@@ -546,7 +555,7 @@ async function getEncoderBuilderResult (options: { | |||
546 | } | 555 | } |
547 | } | 556 | } |
548 | 557 | ||
549 | const result = await builder({ input, resolution, inputBitrate, fps, streamNum }) | 558 | const result = await builder(pick(options, [ 'input', 'resolution', 'inputBitrate', 'fps', 'inputRatio', 'streamNum' ])) |
550 | 559 | ||
551 | return { | 560 | return { |
552 | result, | 561 | result, |
@@ -581,6 +590,7 @@ async function presetVideo (options: { | |||
581 | // Audio encoder | 590 | // Audio encoder |
582 | const parsedAudio = await getAudioStream(input, probe) | 591 | const parsedAudio = await getAudioStream(input, probe) |
583 | const bitrate = await getVideoFileBitrate(input, probe) | 592 | const bitrate = await getVideoFileBitrate(input, probe) |
593 | const { ratio } = await getVideoFileResolution(input, probe) | ||
584 | 594 | ||
585 | let streamsToProcess: StreamType[] = [ 'audio', 'video' ] | 595 | let streamsToProcess: StreamType[] = [ 'audio', 'video' ] |
586 | 596 | ||
@@ -600,6 +610,7 @@ async function presetVideo (options: { | |||
600 | profile, | 610 | profile, |
601 | fps, | 611 | fps, |
602 | inputBitrate: bitrate, | 612 | inputBitrate: bitrate, |
613 | inputRatio: ratio, | ||
603 | videoType: 'vod' as 'vod' | 614 | videoType: 'vod' as 'vod' |
604 | }) | 615 | }) |
605 | 616 | ||
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index bc87e49b1..e58444b07 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import * as ffmpeg from 'fluent-ffmpeg' | 1 | import * as ffmpeg from 'fluent-ffmpeg' |
2 | import { getMaxBitrate, VideoFileMetadata, VideoResolution } from '../../shared/models/videos' | 2 | import { getMaxBitrate } from '@shared/core-utils' |
3 | import { VideoFileMetadata, VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos' | ||
3 | import { CONFIG } from '../initializers/config' | 4 | import { CONFIG } from '../initializers/config' |
4 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 5 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
5 | import { logger } from './logger' | 6 | import { logger } from './logger' |
@@ -75,7 +76,7 @@ function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) { | |||
75 | } | 76 | } |
76 | } | 77 | } |
77 | 78 | ||
78 | async function getVideoStreamSize (path: string, existingProbe?: ffmpeg.FfprobeData) { | 79 | async function getVideoStreamSize (path: string, existingProbe?: ffmpeg.FfprobeData): Promise<{ width: number, height: number }> { |
79 | const videoStream = await getVideoStreamFromFile(path, existingProbe) | 80 | const videoStream = await getVideoStreamFromFile(path, existingProbe) |
80 | 81 | ||
81 | return videoStream === null | 82 | return videoStream === null |
@@ -146,7 +147,10 @@ async function getVideoFileResolution (path: string, existingProbe?: ffmpeg.Ffpr | |||
146 | const size = await getVideoStreamSize(path, existingProbe) | 147 | const size = await getVideoStreamSize(path, existingProbe) |
147 | 148 | ||
148 | return { | 149 | return { |
149 | videoFileResolution: Math.min(size.height, size.width), | 150 | width: size.width, |
151 | height: size.height, | ||
152 | ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width), | ||
153 | resolution: Math.min(size.height, size.width), | ||
150 | isPortraitMode: size.height > size.width | 154 | isPortraitMode: size.height > size.width |
151 | } | 155 | } |
152 | } | 156 | } |
@@ -243,7 +247,7 @@ async function canDoQuickVideoTranscode (path: string, probe?: ffmpeg.FfprobeDat | |||
243 | const videoStream = await getVideoStreamFromFile(path, probe) | 247 | const videoStream = await getVideoStreamFromFile(path, probe) |
244 | const fps = await getVideoFileFPS(path, probe) | 248 | const fps = await getVideoFileFPS(path, probe) |
245 | const bitRate = await getVideoFileBitrate(path, probe) | 249 | const bitRate = await getVideoFileBitrate(path, probe) |
246 | const resolution = await getVideoFileResolution(path, probe) | 250 | const resolutionData = await getVideoFileResolution(path, probe) |
247 | 251 | ||
248 | // If ffprobe did not manage to guess the bitrate | 252 | // If ffprobe did not manage to guess the bitrate |
249 | if (!bitRate) return false | 253 | if (!bitRate) return false |
@@ -253,7 +257,7 @@ async function canDoQuickVideoTranscode (path: string, probe?: ffmpeg.FfprobeDat | |||
253 | if (videoStream['codec_name'] !== 'h264') return false | 257 | if (videoStream['codec_name'] !== 'h264') return false |
254 | if (videoStream['pix_fmt'] !== 'yuv420p') return false | 258 | if (videoStream['pix_fmt'] !== 'yuv420p') return false |
255 | if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false | 259 | if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false |
256 | if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false | 260 | if (bitRate > getMaxBitrate({ ...resolutionData, fps })) return false |
257 | 261 | ||
258 | return true | 262 | return true |
259 | } | 263 | } |
@@ -278,7 +282,7 @@ async function canDoQuickAudioTranscode (path: string, probe?: ffmpeg.FfprobeDat | |||
278 | return true | 282 | return true |
279 | } | 283 | } |
280 | 284 | ||
281 | function getClosestFramerateStandard (fps: number, type: 'HD_STANDARD' | 'STANDARD'): number { | 285 | function getClosestFramerateStandard <K extends keyof Pick<VideoTranscodingFPS, 'HD_STANDARD' | 'STANDARD'>> (fps: number, type: K) { |
282 | return VIDEO_TRANSCODING_FPS[type].slice(0) | 286 | return VIDEO_TRANSCODING_FPS[type].slice(0) |
283 | .sort((a, b) => fps % a - fps % b)[0] | 287 | .sort((a, b) => fps % a - fps % b)[0] |
284 | } | 288 | } |