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 /shared/models/videos | |
parent | c826f34a45757b324a20f71665b44ed10e6953b5 (diff) | |
download | PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.gz PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.zst PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.zip |
Improve target bitrate calculation
Diffstat (limited to 'shared/models/videos')
-rw-r--r-- | shared/models/videos/video-resolution.enum.ts | 89 | ||||
-rw-r--r-- | shared/models/videos/video-transcoding.model.ts | 16 |
2 files changed, 13 insertions, 92 deletions
diff --git a/shared/models/videos/video-resolution.enum.ts b/shared/models/videos/video-resolution.enum.ts index a5d2ac7fa..24cd2d04d 100644 --- a/shared/models/videos/video-resolution.enum.ts +++ b/shared/models/videos/video-resolution.enum.ts | |||
@@ -1,5 +1,3 @@ | |||
1 | import { VideoTranscodingFPS } from './video-transcoding-fps.model' | ||
2 | |||
3 | export const enum VideoResolution { | 1 | export const enum VideoResolution { |
4 | H_NOVIDEO = 0, | 2 | H_NOVIDEO = 0, |
5 | H_240P = 240, | 3 | H_240P = 240, |
@@ -10,90 +8,3 @@ export const enum VideoResolution { | |||
10 | H_1440P = 1440, | 8 | H_1440P = 1440, |
11 | H_4K = 2160 | 9 | H_4K = 2160 |
12 | } | 10 | } |
13 | |||
14 | /** | ||
15 | * Bitrate targets for different resolutions, at VideoTranscodingFPS.AVERAGE. | ||
16 | * | ||
17 | * Sources for individual quality levels: | ||
18 | * Google Live Encoder: https://support.google.com/youtube/answer/2853702?hl=en | ||
19 | * YouTube Video Info: youtube-dl --list-formats, with sample videos | ||
20 | */ | ||
21 | function getBaseBitrate (resolution: number) { | ||
22 | if (resolution === VideoResolution.H_NOVIDEO) { | ||
23 | // audio-only | ||
24 | return 64 * 1000 | ||
25 | } | ||
26 | |||
27 | if (resolution <= VideoResolution.H_240P) { | ||
28 | // quality according to Google Live Encoder: 300 - 700 Kbps | ||
29 | // Quality according to YouTube Video Info: 285 Kbps | ||
30 | return 320 * 1000 | ||
31 | } | ||
32 | |||
33 | if (resolution <= VideoResolution.H_360P) { | ||
34 | // quality according to Google Live Encoder: 400 - 1,000 Kbps | ||
35 | // Quality according to YouTube Video Info: 700 Kbps | ||
36 | return 780 * 1000 | ||
37 | } | ||
38 | |||
39 | if (resolution <= VideoResolution.H_480P) { | ||
40 | // quality according to Google Live Encoder: 500 - 2,000 Kbps | ||
41 | // Quality according to YouTube Video Info: 1300 Kbps | ||
42 | return 1500 * 1000 | ||
43 | } | ||
44 | |||
45 | if (resolution <= VideoResolution.H_720P) { | ||
46 | // quality according to Google Live Encoder: 1,500 - 4,000 Kbps | ||
47 | // Quality according to YouTube Video Info: 2680 Kbps | ||
48 | return 2800 * 1000 | ||
49 | } | ||
50 | |||
51 | if (resolution <= VideoResolution.H_1080P) { | ||
52 | // quality according to Google Live Encoder: 3000 - 6000 Kbps | ||
53 | // Quality according to YouTube Video Info: 5081 Kbps | ||
54 | return 5200 * 1000 | ||
55 | } | ||
56 | |||
57 | if (resolution <= VideoResolution.H_1440P) { | ||
58 | // quality according to Google Live Encoder: 6000 - 13000 Kbps | ||
59 | // Quality according to YouTube Video Info: 8600 (av01) - 17000 (vp9.2) Kbps | ||
60 | return 10_000 * 1000 | ||
61 | } | ||
62 | |||
63 | // 4K | ||
64 | // quality according to Google Live Encoder: 13000 - 34000 Kbps | ||
65 | return 22_000 * 1000 | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Calculate the target bitrate based on video resolution and FPS. | ||
70 | * | ||
71 | * The calculation is based on two values: | ||
72 | * Bitrate at VideoTranscodingFPS.AVERAGE is always the same as | ||
73 | * getBaseBitrate(). Bitrate at VideoTranscodingFPS.MAX is always | ||
74 | * getBaseBitrate() * 1.4. All other values are calculated linearly | ||
75 | * between these two points. | ||
76 | */ | ||
77 | export function getTargetBitrate (resolution: number, fps: number, fpsTranscodingConstants: VideoTranscodingFPS) { | ||
78 | const baseBitrate = getBaseBitrate(resolution) | ||
79 | // The maximum bitrate, used when fps === VideoTranscodingFPS.MAX | ||
80 | // Based on numbers from Youtube, 60 fps bitrate divided by 30 fps bitrate: | ||
81 | // 720p: 2600 / 1750 = 1.49 | ||
82 | // 1080p: 4400 / 3300 = 1.33 | ||
83 | const maxBitrate = baseBitrate * 1.4 | ||
84 | const maxBitrateDifference = maxBitrate - baseBitrate | ||
85 | const maxFpsDifference = fpsTranscodingConstants.MAX - fpsTranscodingConstants.AVERAGE | ||
86 | // For 1080p video with default settings, this results in the following formula: | ||
87 | // 3300 + (x - 30) * (1320/30) | ||
88 | // Example outputs: | ||
89 | // 1080p10: 2420 kbps, 1080p30: 3300 kbps, 1080p60: 4620 kbps | ||
90 | // 720p10: 1283 kbps, 720p30: 1750 kbps, 720p60: 2450 kbps | ||
91 | return Math.floor(baseBitrate + (fps - fpsTranscodingConstants.AVERAGE) * (maxBitrateDifference / maxFpsDifference)) | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * The maximum bitrate we expect to see on a transcoded video in bytes per second. | ||
96 | */ | ||
97 | export function getMaxBitrate (resolution: VideoResolution, fps: number, fpsTranscodingConstants: VideoTranscodingFPS) { | ||
98 | return getTargetBitrate(resolution, fps, fpsTranscodingConstants) * 2 | ||
99 | } | ||
diff --git a/shared/models/videos/video-transcoding.model.ts b/shared/models/videos/video-transcoding.model.ts index f1fe4609b..83b8e98a0 100644 --- a/shared/models/videos/video-transcoding.model.ts +++ b/shared/models/videos/video-transcoding.model.ts | |||
@@ -2,13 +2,23 @@ import { VideoResolution } from './video-resolution.enum' | |||
2 | 2 | ||
3 | // Types used by plugins and ffmpeg-utils | 3 | // Types used by plugins and ffmpeg-utils |
4 | 4 | ||
5 | export type EncoderOptionsBuilder = (params: { | 5 | export type EncoderOptionsBuilderParams = { |
6 | input: string | 6 | input: string |
7 | |||
7 | resolution: VideoResolution | 8 | resolution: VideoResolution |
8 | inputBitrate: number | 9 | |
10 | // Could be null for "merge audio" transcoding | ||
9 | fps?: number | 11 | fps?: number |
12 | |||
13 | // Could be undefined if we could not get input bitrate (some RTMP streams for example) | ||
14 | inputBitrate: number | ||
15 | inputRatio: number | ||
16 | |||
17 | // For lives | ||
10 | streamNum?: number | 18 | streamNum?: number |
11 | }) => Promise<EncoderOptions> | EncoderOptions | 19 | } |
20 | |||
21 | export type EncoderOptionsBuilder = (params: EncoderOptionsBuilderParams) => Promise<EncoderOptions> | EncoderOptions | ||
12 | 22 | ||
13 | export interface EncoderOptions { | 23 | export interface EncoderOptions { |
14 | copy?: boolean // Copy stream? Default to false | 24 | copy?: boolean // Copy stream? Default to false |