From 777803c6c99424fabae96940cd36febc6f8f259a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Le=20Calvar?= Date: Sun, 4 Apr 2021 21:21:40 +0200 Subject: [PATCH] adadpt transcode profil for live --- main.js | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 7 deletions(-) diff --git a/main.js b/main.js index b98583d..f72b6b2 100644 --- a/main.js +++ b/main.js @@ -1,16 +1,19 @@ async function register ({ - transcodingManager + transcodingManager, + peertubeHelpers }) { - // Adapt bitrate when using libx264 encoder + const { logger } = peertubeHelpers + + // Add hardware encode through vaapi { - const builder = (options) => { + const VODBuilder = (options) => { const { input, resolution, fps, streamNum } = options + const targetBitrate = getTargetBitrate(resolution, fps) // You can also return a promise return { videoFilters: [ - 'hwupload' ], inputOptions: [ // enable hardware acceleration @@ -20,7 +23,41 @@ async function register ({ ], outputOptions: [ '-bf 8', // override hardcoded bf value which cause memory error - '-pix_fmt vaapi_vld' + '-pix_fmt vaapi_vld', + `-preset veryfast`, + `-b:v${streamSuffix} ${targetBitrate}`, + `-maxrate ${targetBitrate}`, + `-bufsize ${targetBitrate * 2}` + ] + } + } + + const liveBuilder = (options) => { + const { input, resolution, fps, streamNum } = options + const streamSuffix = streamNum == undefined ? '' : `:${streamNum}` + const targetBitrate = getTargetBitrate(resolution, fps) + + // You can also return a promise + return { + videoFilters: [ + ], + inputOptions: [ + // enable hardware acceleration + '-hwaccel vaapi', + '-hwaccel_output_format vaapi', + '-vaapi_device /dev/dri/renderD128' + ], + outputOptions: [ + '-bf 8', // override hardcoded bf value which cause memory error + '-pix_fmt vaapi_vld', + `-preset veryfast`, + `-r:v${streamSuffix} ${fps}`, + `-profile:v${streamSuffix} high`, + `-level:v${streamSuffix} 3.1`, + `-g:v${streamSuffix} ${fps*2}`, + `-b:v${streamSuffix} ${targetBitrate}`, + `-maxrate ${targetBitrate}`, + `-bufsize ${targetBitrate * 2}` ] } } @@ -29,11 +66,12 @@ async function register ({ const profileName = 'vaapi' // Support this profile for VOD transcoding - transcodingManager.addVODProfile(encoder, profileName, builder) + transcodingManager.addVODProfile(encoder, profileName, VODBuilder) transcodingManager.addVODEncoderPriority('video', encoder, 1000) // And/Or support this profile for live transcoding - transcodingManager.addLiveProfile(encoder, profileName, builder) + transcodingManager.addLiveProfile(encoder, profileName, liveBuilder) + transcodingManager.addLiveEncoderPriority('video', encoder, 1000) } } @@ -45,3 +83,85 @@ module.exports = { register, unregister } + + +// copied from Peertube, is it possible to import it instead of copying it ? +/** + * Bitrate targets for different resolutions, at VideoTranscodingFPS.AVERAGE. + * + * Sources for individual quality levels: + * Google Live Encoder: https://support.google.com/youtube/answer/2853702?hl=en + * YouTube Video Info: youtube-dl --list-formats, with sample videos + */ +function getBaseBitrate (resolution) { + if (resolution === 0) { + // audio-only + return 64 * 1000 + } + + if (resolution <= 240) { + // quality according to Google Live Encoder: 300 - 700 Kbps + // Quality according to YouTube Video Info: 285 Kbps + return 320 * 1000 + } + + if (resolution <= 360) { + // quality according to Google Live Encoder: 400 - 1,000 Kbps + // Quality according to YouTube Video Info: 700 Kbps + return 780 * 1000 + } + + if (resolution <= 480) { + // quality according to Google Live Encoder: 500 - 2,000 Kbps + // Quality according to YouTube Video Info: 1300 Kbps + return 1500 * 1000 + } + + if (resolution <= 720) { + // quality according to Google Live Encoder: 1,500 - 4,000 Kbps + // Quality according to YouTube Video Info: 2680 Kbps + return 2800 * 1000 + } + + if (resolution <= 1080) { + // quality according to Google Live Encoder: 3000 - 6000 Kbps + // Quality according to YouTube Video Info: 5081 Kbps + return 5200 * 1000 + } + + if (resolution <= 1440) { + // quality according to Google Live Encoder: 6000 - 13000 Kbps + // Quality according to YouTube Video Info: 8600 (av01) - 17000 (vp9.2) Kbps + return 10_000 * 1000 + } + + // 4K + // quality according to Google Live Encoder: 13000 - 34000 Kbps + return 22_000 * 1000 +} + +/** + * Calculate the target bitrate based on video resolution and FPS. + * + * The calculation is based on two values: + * Bitrate at VideoTranscodingFPS.AVERAGE is always the same as + * getBaseBitrate(). Bitrate at VideoTranscodingFPS.MAX is always + * getBaseBitrate() * 1.4. All other values are calculated linearly + * between these two points. + */ +function getTargetBitrate (resolution, fps) { + const baseBitrate = getBaseBitrate(resolution) + // The maximum bitrate, used when fps === VideoTranscodingFPS.MAX + // Based on numbers from Youtube, 60 fps bitrate divided by 30 fps bitrate: + // 720p: 2600 / 1750 = 1.49 + // 1080p: 4400 / 3300 = 1.33 + const maxBitrate = baseBitrate * 1.4 + const maxBitrateDifference = maxBitrate - baseBitrate + const maxFpsDifference = 60 - 30 + // For 1080p video with default settings, this results in the following formula: + // 3300 + (x - 30) * (1320/30) + // Example outputs: + // 1080p10: 2420 kbps, 1080p30: 3300 kbps, 1080p60: 4620 kbps + // 720p10: 1283 kbps, 720p30: 1750 kbps, 720p60: 2450 kbps + return Math.floor(baseBitrate + (fps - 30) * (maxBitrateDifference / maxFpsDifference)) +}