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/lib | |
parent | c826f34a45757b324a20f71665b44ed10e6953b5 (diff) | |
download | PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.gz PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.tar.zst PeerTube-679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3.zip |
Improve target bitrate calculation
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/job-queue/handlers/video-file-import.ts | 10 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-import.ts | 6 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-live-ending.ts | 4 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-transcoding.ts | 4 | ||||
-rw-r--r-- | server/lib/live/live-manager.ts | 13 | ||||
-rw-r--r-- | server/lib/live/shared/muxing-session.ts | 9 | ||||
-rw-r--r-- | server/lib/transcoding/video-transcoding-profiles.ts | 32 |
7 files changed, 42 insertions, 36 deletions
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 4d199f247..2f4abf730 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -32,7 +32,7 @@ async function processVideoFileImport (job: Bull.Job) { | |||
32 | const newResolutionPayload = { | 32 | const newResolutionPayload = { |
33 | type: 'new-resolution-to-webtorrent' as 'new-resolution-to-webtorrent', | 33 | type: 'new-resolution-to-webtorrent' as 'new-resolution-to-webtorrent', |
34 | videoUUID: video.uuid, | 34 | videoUUID: video.uuid, |
35 | resolution: data.videoFileResolution, | 35 | resolution: data.resolution, |
36 | isPortraitMode: data.isPortraitMode, | 36 | isPortraitMode: data.isPortraitMode, |
37 | copyCodecs: false, | 37 | copyCodecs: false, |
38 | isNewVideo: false | 38 | isNewVideo: false |
@@ -51,13 +51,13 @@ export { | |||
51 | // --------------------------------------------------------------------------- | 51 | // --------------------------------------------------------------------------- |
52 | 52 | ||
53 | async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { | 53 | async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { |
54 | const { videoFileResolution } = await getVideoFileResolution(inputFilePath) | 54 | const { resolution } = await getVideoFileResolution(inputFilePath) |
55 | const { size } = await stat(inputFilePath) | 55 | const { size } = await stat(inputFilePath) |
56 | const fps = await getVideoFileFPS(inputFilePath) | 56 | const fps = await getVideoFileFPS(inputFilePath) |
57 | 57 | ||
58 | const fileExt = getLowercaseExtension(inputFilePath) | 58 | const fileExt = getLowercaseExtension(inputFilePath) |
59 | 59 | ||
60 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === videoFileResolution) | 60 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === resolution) |
61 | 61 | ||
62 | if (currentVideoFile) { | 62 | if (currentVideoFile) { |
63 | // Remove old file and old torrent | 63 | // Remove old file and old torrent |
@@ -69,9 +69,9 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { | |||
69 | } | 69 | } |
70 | 70 | ||
71 | const newVideoFile = new VideoFileModel({ | 71 | const newVideoFile = new VideoFileModel({ |
72 | resolution: videoFileResolution, | 72 | resolution, |
73 | extname: fileExt, | 73 | extname: fileExt, |
74 | filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt), | 74 | filename: generateWebTorrentVideoFilename(resolution, fileExt), |
75 | size, | 75 | size, |
76 | fps, | 76 | fps, |
77 | videoId: video.id | 77 | videoId: video.id |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 5fd2039b1..fec553f2b 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -114,7 +114,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid | |||
114 | throw new Error('The user video quota is exceeded with this video to import.') | 114 | throw new Error('The user video quota is exceeded with this video to import.') |
115 | } | 115 | } |
116 | 116 | ||
117 | const { videoFileResolution } = await getVideoFileResolution(tempVideoPath) | 117 | const { resolution } = await getVideoFileResolution(tempVideoPath) |
118 | const fps = await getVideoFileFPS(tempVideoPath) | 118 | const fps = await getVideoFileFPS(tempVideoPath) |
119 | const duration = await getDurationFromVideoFile(tempVideoPath) | 119 | const duration = await getDurationFromVideoFile(tempVideoPath) |
120 | 120 | ||
@@ -122,9 +122,9 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid | |||
122 | const fileExt = getLowercaseExtension(tempVideoPath) | 122 | const fileExt = getLowercaseExtension(tempVideoPath) |
123 | const videoFileData = { | 123 | const videoFileData = { |
124 | extname: fileExt, | 124 | extname: fileExt, |
125 | resolution: videoFileResolution, | 125 | resolution, |
126 | size: stats.size, | 126 | size: stats.size, |
127 | filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt), | 127 | filename: generateWebTorrentVideoFilename(resolution, fileExt), |
128 | fps, | 128 | fps, |
129 | videoId: videoImport.videoId | 129 | videoId: videoImport.videoId |
130 | } | 130 | } |
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index 386ccdc7b..aa5bd573a 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts | |||
@@ -96,12 +96,12 @@ async function saveLive (video: MVideo, live: MVideoLive, streamingPlaylist: MSt | |||
96 | const probe = await ffprobePromise(concatenatedTsFilePath) | 96 | const probe = await ffprobePromise(concatenatedTsFilePath) |
97 | const { audioStream } = await getAudioStream(concatenatedTsFilePath, probe) | 97 | const { audioStream } = await getAudioStream(concatenatedTsFilePath, probe) |
98 | 98 | ||
99 | const { videoFileResolution, isPortraitMode } = await getVideoFileResolution(concatenatedTsFilePath, probe) | 99 | const { resolution, isPortraitMode } = await getVideoFileResolution(concatenatedTsFilePath, probe) |
100 | 100 | ||
101 | const outputPath = await generateHlsPlaylistResolutionFromTS({ | 101 | const outputPath = await generateHlsPlaylistResolutionFromTS({ |
102 | video: videoWithFiles, | 102 | video: videoWithFiles, |
103 | concatenatedTsFilePath, | 103 | concatenatedTsFilePath, |
104 | resolution: videoFileResolution, | 104 | resolution, |
105 | isPortraitMode, | 105 | isPortraitMode, |
106 | isAAC: audioStream?.codec_name === 'aac' | 106 | isAAC: audioStream?.codec_name === 'aac' |
107 | }) | 107 | }) |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 2abb351ce..876d1460c 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -136,7 +136,7 @@ async function onVideoFileOptimizer ( | |||
136 | if (videoArg === undefined) return undefined | 136 | if (videoArg === undefined) return undefined |
137 | 137 | ||
138 | // Outside the transaction (IO on disk) | 138 | // Outside the transaction (IO on disk) |
139 | const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution() | 139 | const { resolution, isPortraitMode } = await videoArg.getMaxQualityResolution() |
140 | 140 | ||
141 | // Maybe the video changed in database, refresh it | 141 | // Maybe the video changed in database, refresh it |
142 | const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid) | 142 | const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid) |
@@ -155,7 +155,7 @@ async function onVideoFileOptimizer ( | |||
155 | }) | 155 | }) |
156 | const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload) | 156 | const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload) |
157 | 157 | ||
158 | const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent') | 158 | const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, resolution, isPortraitMode, 'webtorrent') |
159 | 159 | ||
160 | if (!hasHls && !hasNewResolutions) { | 160 | if (!hasHls && !hasNewResolutions) { |
161 | // No transcoding to do, it's now published | 161 | // No transcoding to do, it's now published |
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts index b19ecef6f..2a429fb33 100644 --- a/server/lib/live/live-manager.ts +++ b/server/lib/live/live-manager.ts | |||
@@ -202,7 +202,7 @@ class LiveManager { | |||
202 | const now = Date.now() | 202 | const now = Date.now() |
203 | const probe = await ffprobePromise(rtmpUrl) | 203 | const probe = await ffprobePromise(rtmpUrl) |
204 | 204 | ||
205 | const [ { videoFileResolution }, fps, bitrate ] = await Promise.all([ | 205 | const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ |
206 | getVideoFileResolution(rtmpUrl, probe), | 206 | getVideoFileResolution(rtmpUrl, probe), |
207 | getVideoFileFPS(rtmpUrl, probe), | 207 | getVideoFileFPS(rtmpUrl, probe), |
208 | getVideoFileBitrate(rtmpUrl, probe) | 208 | getVideoFileBitrate(rtmpUrl, probe) |
@@ -210,13 +210,13 @@ class LiveManager { | |||
210 | 210 | ||
211 | logger.info( | 211 | logger.info( |
212 | '%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', | 212 | '%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', |
213 | rtmpUrl, Date.now() - now, bitrate, fps, videoFileResolution, lTags(sessionId, video.uuid) | 213 | rtmpUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) |
214 | ) | 214 | ) |
215 | 215 | ||
216 | const allResolutions = this.buildAllResolutionsToTranscode(videoFileResolution) | 216 | const allResolutions = this.buildAllResolutionsToTranscode(resolution) |
217 | 217 | ||
218 | logger.info( | 218 | logger.info( |
219 | 'Will mux/transcode live video of original resolution %d.', videoFileResolution, | 219 | 'Will mux/transcode live video of original resolution %d.', resolution, |
220 | { allResolutions, ...lTags(sessionId, video.uuid) } | 220 | { allResolutions, ...lTags(sessionId, video.uuid) } |
221 | ) | 221 | ) |
222 | 222 | ||
@@ -229,6 +229,7 @@ class LiveManager { | |||
229 | rtmpUrl, | 229 | rtmpUrl, |
230 | fps, | 230 | fps, |
231 | bitrate, | 231 | bitrate, |
232 | ratio, | ||
232 | allResolutions | 233 | allResolutions |
233 | }) | 234 | }) |
234 | } | 235 | } |
@@ -240,9 +241,10 @@ class LiveManager { | |||
240 | rtmpUrl: string | 241 | rtmpUrl: string |
241 | fps: number | 242 | fps: number |
242 | bitrate: number | 243 | bitrate: number |
244 | ratio: number | ||
243 | allResolutions: number[] | 245 | allResolutions: number[] |
244 | }) { | 246 | }) { |
245 | const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, rtmpUrl } = options | 247 | const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, rtmpUrl } = options |
246 | const videoUUID = videoLive.Video.uuid | 248 | const videoUUID = videoLive.Video.uuid |
247 | const localLTags = lTags(sessionId, videoUUID) | 249 | const localLTags = lTags(sessionId, videoUUID) |
248 | 250 | ||
@@ -257,6 +259,7 @@ class LiveManager { | |||
257 | streamingPlaylist, | 259 | streamingPlaylist, |
258 | rtmpUrl, | 260 | rtmpUrl, |
259 | bitrate, | 261 | bitrate, |
262 | ratio, | ||
260 | fps, | 263 | fps, |
261 | allResolutions | 264 | allResolutions |
262 | }) | 265 | }) |
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts index 62708b14b..a80abc843 100644 --- a/server/lib/live/shared/muxing-session.ts +++ b/server/lib/live/shared/muxing-session.ts | |||
@@ -54,9 +54,11 @@ class MuxingSession extends EventEmitter { | |||
54 | private readonly streamingPlaylist: MStreamingPlaylistVideo | 54 | private readonly streamingPlaylist: MStreamingPlaylistVideo |
55 | private readonly rtmpUrl: string | 55 | private readonly rtmpUrl: string |
56 | private readonly fps: number | 56 | private readonly fps: number |
57 | private readonly bitrate: number | ||
58 | private readonly allResolutions: number[] | 57 | private readonly allResolutions: number[] |
59 | 58 | ||
59 | private readonly bitrate: number | ||
60 | private readonly ratio: number | ||
61 | |||
60 | private readonly videoId: number | 62 | private readonly videoId: number |
61 | private readonly videoUUID: string | 63 | private readonly videoUUID: string |
62 | private readonly saveReplay: boolean | 64 | private readonly saveReplay: boolean |
@@ -85,6 +87,7 @@ class MuxingSession extends EventEmitter { | |||
85 | rtmpUrl: string | 87 | rtmpUrl: string |
86 | fps: number | 88 | fps: number |
87 | bitrate: number | 89 | bitrate: number |
90 | ratio: number | ||
88 | allResolutions: number[] | 91 | allResolutions: number[] |
89 | }) { | 92 | }) { |
90 | super() | 93 | super() |
@@ -96,7 +99,10 @@ class MuxingSession extends EventEmitter { | |||
96 | this.streamingPlaylist = options.streamingPlaylist | 99 | this.streamingPlaylist = options.streamingPlaylist |
97 | this.rtmpUrl = options.rtmpUrl | 100 | this.rtmpUrl = options.rtmpUrl |
98 | this.fps = options.fps | 101 | this.fps = options.fps |
102 | |||
99 | this.bitrate = options.bitrate | 103 | this.bitrate = options.bitrate |
104 | this.ratio = options.bitrate | ||
105 | |||
100 | this.allResolutions = options.allResolutions | 106 | this.allResolutions = options.allResolutions |
101 | 107 | ||
102 | this.videoId = this.videoLive.Video.id | 108 | this.videoId = this.videoLive.Video.id |
@@ -122,6 +128,7 @@ class MuxingSession extends EventEmitter { | |||
122 | resolutions: this.allResolutions, | 128 | resolutions: this.allResolutions, |
123 | fps: this.fps, | 129 | fps: this.fps, |
124 | bitrate: this.bitrate, | 130 | bitrate: this.bitrate, |
131 | ratio: this.ratio, | ||
125 | 132 | ||
126 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 133 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
127 | profile: CONFIG.LIVE.TRANSCODING.PROFILE | 134 | profile: CONFIG.LIVE.TRANSCODING.PROFILE |
diff --git a/server/lib/transcoding/video-transcoding-profiles.ts b/server/lib/transcoding/video-transcoding-profiles.ts index 2309f38d4..bca6dfccd 100644 --- a/server/lib/transcoding/video-transcoding-profiles.ts +++ b/server/lib/transcoding/video-transcoding-profiles.ts | |||
@@ -1,23 +1,24 @@ | |||
1 | import { logger } from '@server/helpers/logger' | 1 | import { logger } from '@server/helpers/logger' |
2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../../shared/models/videos' | 2 | import { getAverageBitrate } from '@shared/core-utils' |
3 | import { AvailableEncoders, EncoderOptionsBuilder, EncoderOptionsBuilderParams } from '../../../shared/models/videos' | ||
3 | import { buildStreamSuffix, resetSupportedEncoders } from '../../helpers/ffmpeg-utils' | 4 | import { buildStreamSuffix, resetSupportedEncoders } from '../../helpers/ffmpeg-utils' |
4 | import { canDoQuickAudioTranscode, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '../../helpers/ffprobe-utils' | 5 | import { canDoQuickAudioTranscode, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '../../helpers/ffprobe-utils' |
5 | import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' | ||
6 | 6 | ||
7 | /** | 7 | /** |
8 | * | 8 | * |
9 | * Available encoders and profiles for the transcoding jobs | 9 | * Available encoders and profiles for the transcoding jobs |
10 | * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile | 10 | * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile |
11 | * | 11 | * |
12 | * Resources: | ||
13 | * * https://slhck.info/video/2017/03/01/rate-control.html | ||
14 | * * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate | ||
12 | */ | 15 | */ |
13 | 16 | ||
14 | // Resources: | 17 | const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async (options: EncoderOptionsBuilderParams) => { |
15 | // * https://slhck.info/video/2017/03/01/rate-control.html | 18 | const { fps, inputRatio, inputBitrate } = options |
16 | // * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate | 19 | if (!fps) return { outputOptions: [ ] } |
17 | 20 | ||
18 | const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ inputBitrate, resolution, fps }) => { | 21 | const targetBitrate = capBitrate(inputBitrate, getAverageBitrate({ ...options, fps, ratio: inputRatio })) |
19 | const targetBitrate = buildTargetBitrate({ inputBitrate, resolution, fps }) | ||
20 | if (!targetBitrate) return { outputOptions: [ ] } | ||
21 | 22 | ||
22 | return { | 23 | return { |
23 | outputOptions: [ | 24 | outputOptions: [ |
@@ -29,8 +30,10 @@ const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ inputBitrat | |||
29 | } | 30 | } |
30 | } | 31 | } |
31 | 32 | ||
32 | const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution, fps, inputBitrate, streamNum }) => { | 33 | const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async (options: EncoderOptionsBuilderParams) => { |
33 | const targetBitrate = buildTargetBitrate({ inputBitrate, resolution, fps }) | 34 | const { streamNum, fps, inputBitrate, inputRatio } = options |
35 | |||
36 | const targetBitrate = capBitrate(inputBitrate, getAverageBitrate({ ...options, fps, ratio: inputRatio })) | ||
34 | 37 | ||
35 | return { | 38 | return { |
36 | outputOptions: [ | 39 | outputOptions: [ |
@@ -231,14 +234,7 @@ export { | |||
231 | 234 | ||
232 | // --------------------------------------------------------------------------- | 235 | // --------------------------------------------------------------------------- |
233 | 236 | ||
234 | function buildTargetBitrate (options: { | 237 | function capBitrate (inputBitrate: number, targetBitrate: number) { |
235 | inputBitrate: number | ||
236 | resolution: VideoResolution | ||
237 | fps: number | ||
238 | }) { | ||
239 | const { inputBitrate, resolution, fps } = options | ||
240 | |||
241 | const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) | ||
242 | if (!inputBitrate) return targetBitrate | 238 | if (!inputBitrate) return targetBitrate |
243 | 239 | ||
244 | return Math.min(targetBitrate, inputBitrate) | 240 | return Math.min(targetBitrate, inputBitrate) |