From 679c12e69c9f3a2d003ee3abe8b8da49f25b2bd3 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 6 Aug 2021 13:35:25 +0200 Subject: Improve target bitrate calculation --- server/lib/job-queue/handlers/video-file-import.ts | 10 +++---- server/lib/job-queue/handlers/video-import.ts | 6 ++-- server/lib/job-queue/handlers/video-live-ending.ts | 4 +-- server/lib/job-queue/handlers/video-transcoding.ts | 4 +-- server/lib/live/live-manager.ts | 13 +++++---- server/lib/live/shared/muxing-session.ts | 9 +++++- .../lib/transcoding/video-transcoding-profiles.ts | 32 ++++++++++------------ 7 files changed, 42 insertions(+), 36 deletions(-) (limited to 'server/lib') 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) { const newResolutionPayload = { type: 'new-resolution-to-webtorrent' as 'new-resolution-to-webtorrent', videoUUID: video.uuid, - resolution: data.videoFileResolution, + resolution: data.resolution, isPortraitMode: data.isPortraitMode, copyCodecs: false, isNewVideo: false @@ -51,13 +51,13 @@ export { // --------------------------------------------------------------------------- async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { - const { videoFileResolution } = await getVideoFileResolution(inputFilePath) + const { resolution } = await getVideoFileResolution(inputFilePath) const { size } = await stat(inputFilePath) const fps = await getVideoFileFPS(inputFilePath) const fileExt = getLowercaseExtension(inputFilePath) - const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === videoFileResolution) + const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === resolution) if (currentVideoFile) { // Remove old file and old torrent @@ -69,9 +69,9 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { } const newVideoFile = new VideoFileModel({ - resolution: videoFileResolution, + resolution, extname: fileExt, - filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt), + filename: generateWebTorrentVideoFilename(resolution, fileExt), size, fps, 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, videoImport: MVid throw new Error('The user video quota is exceeded with this video to import.') } - const { videoFileResolution } = await getVideoFileResolution(tempVideoPath) + const { resolution } = await getVideoFileResolution(tempVideoPath) const fps = await getVideoFileFPS(tempVideoPath) const duration = await getDurationFromVideoFile(tempVideoPath) @@ -122,9 +122,9 @@ async function processFile (downloader: () => Promise, videoImport: MVid const fileExt = getLowercaseExtension(tempVideoPath) const videoFileData = { extname: fileExt, - resolution: videoFileResolution, + resolution, size: stats.size, - filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt), + filename: generateWebTorrentVideoFilename(resolution, fileExt), fps, videoId: videoImport.videoId } 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 const probe = await ffprobePromise(concatenatedTsFilePath) const { audioStream } = await getAudioStream(concatenatedTsFilePath, probe) - const { videoFileResolution, isPortraitMode } = await getVideoFileResolution(concatenatedTsFilePath, probe) + const { resolution, isPortraitMode } = await getVideoFileResolution(concatenatedTsFilePath, probe) const outputPath = await generateHlsPlaylistResolutionFromTS({ video: videoWithFiles, concatenatedTsFilePath, - resolution: videoFileResolution, + resolution, isPortraitMode, isAAC: audioStream?.codec_name === 'aac' }) 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 ( if (videoArg === undefined) return undefined // Outside the transaction (IO on disk) - const { videoFileResolution, isPortraitMode } = await videoArg.getMaxQualityResolution() + const { resolution, isPortraitMode } = await videoArg.getMaxQualityResolution() // Maybe the video changed in database, refresh it const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid) @@ -155,7 +155,7 @@ async function onVideoFileOptimizer ( }) const hasHls = await createHlsJobIfEnabled(user, originalFileHLSPayload) - const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, videoFileResolution, isPortraitMode, 'webtorrent') + const hasNewResolutions = await createLowerResolutionsJobs(videoDatabase, user, resolution, isPortraitMode, 'webtorrent') if (!hasHls && !hasNewResolutions) { // 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 { const now = Date.now() const probe = await ffprobePromise(rtmpUrl) - const [ { videoFileResolution }, fps, bitrate ] = await Promise.all([ + const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ getVideoFileResolution(rtmpUrl, probe), getVideoFileFPS(rtmpUrl, probe), getVideoFileBitrate(rtmpUrl, probe) @@ -210,13 +210,13 @@ class LiveManager { logger.info( '%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', - rtmpUrl, Date.now() - now, bitrate, fps, videoFileResolution, lTags(sessionId, video.uuid) + rtmpUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) ) - const allResolutions = this.buildAllResolutionsToTranscode(videoFileResolution) + const allResolutions = this.buildAllResolutionsToTranscode(resolution) logger.info( - 'Will mux/transcode live video of original resolution %d.', videoFileResolution, + 'Will mux/transcode live video of original resolution %d.', resolution, { allResolutions, ...lTags(sessionId, video.uuid) } ) @@ -229,6 +229,7 @@ class LiveManager { rtmpUrl, fps, bitrate, + ratio, allResolutions }) } @@ -240,9 +241,10 @@ class LiveManager { rtmpUrl: string fps: number bitrate: number + ratio: number allResolutions: number[] }) { - const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, rtmpUrl } = options + const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, rtmpUrl } = options const videoUUID = videoLive.Video.uuid const localLTags = lTags(sessionId, videoUUID) @@ -257,6 +259,7 @@ class LiveManager { streamingPlaylist, rtmpUrl, bitrate, + ratio, fps, allResolutions }) 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 { private readonly streamingPlaylist: MStreamingPlaylistVideo private readonly rtmpUrl: string private readonly fps: number - private readonly bitrate: number private readonly allResolutions: number[] + private readonly bitrate: number + private readonly ratio: number + private readonly videoId: number private readonly videoUUID: string private readonly saveReplay: boolean @@ -85,6 +87,7 @@ class MuxingSession extends EventEmitter { rtmpUrl: string fps: number bitrate: number + ratio: number allResolutions: number[] }) { super() @@ -96,7 +99,10 @@ class MuxingSession extends EventEmitter { this.streamingPlaylist = options.streamingPlaylist this.rtmpUrl = options.rtmpUrl this.fps = options.fps + this.bitrate = options.bitrate + this.ratio = options.bitrate + this.allResolutions = options.allResolutions this.videoId = this.videoLive.Video.id @@ -122,6 +128,7 @@ class MuxingSession extends EventEmitter { resolutions: this.allResolutions, fps: this.fps, bitrate: this.bitrate, + ratio: this.ratio, availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), 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 @@ import { logger } from '@server/helpers/logger' -import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../../shared/models/videos' +import { getAverageBitrate } from '@shared/core-utils' +import { AvailableEncoders, EncoderOptionsBuilder, EncoderOptionsBuilderParams } from '../../../shared/models/videos' import { buildStreamSuffix, resetSupportedEncoders } from '../../helpers/ffmpeg-utils' import { canDoQuickAudioTranscode, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '../../helpers/ffprobe-utils' -import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' /** * * Available encoders and profiles for the transcoding jobs * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile * + * Resources: + * * https://slhck.info/video/2017/03/01/rate-control.html + * * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate */ -// Resources: -// * https://slhck.info/video/2017/03/01/rate-control.html -// * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate +const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async (options: EncoderOptionsBuilderParams) => { + const { fps, inputRatio, inputBitrate } = options + if (!fps) return { outputOptions: [ ] } -const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ inputBitrate, resolution, fps }) => { - const targetBitrate = buildTargetBitrate({ inputBitrate, resolution, fps }) - if (!targetBitrate) return { outputOptions: [ ] } + const targetBitrate = capBitrate(inputBitrate, getAverageBitrate({ ...options, fps, ratio: inputRatio })) return { outputOptions: [ @@ -29,8 +30,10 @@ const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ inputBitrat } } -const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution, fps, inputBitrate, streamNum }) => { - const targetBitrate = buildTargetBitrate({ inputBitrate, resolution, fps }) +const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async (options: EncoderOptionsBuilderParams) => { + const { streamNum, fps, inputBitrate, inputRatio } = options + + const targetBitrate = capBitrate(inputBitrate, getAverageBitrate({ ...options, fps, ratio: inputRatio })) return { outputOptions: [ @@ -231,14 +234,7 @@ export { // --------------------------------------------------------------------------- -function buildTargetBitrate (options: { - inputBitrate: number - resolution: VideoResolution - fps: number -}) { - const { inputBitrate, resolution, fps } = options - - const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) +function capBitrate (inputBitrate: number, targetBitrate: number) { if (!inputBitrate) return targetBitrate return Math.min(targetBitrate, inputBitrate) -- cgit v1.2.3