From 4176e227cb18c40e13f30f4634d128cc3169e9d4 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Mon, 21 May 2018 13:14:29 +0200 Subject: Fixing #626 with ffmpeg's low default audio bitrate --- server/helpers/ffmpeg-utils.ts | 121 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 5 deletions(-) (limited to 'server/helpers/ffmpeg-utils.ts') diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index f0623c88b..eb1c86ab9 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -5,6 +5,7 @@ import { CONFIG, VIDEO_TRANSCODING_FPS } from '../initializers' import { unlinkPromise } from './core-utils' import { processImage } from './image-utils' import { logger } from './logger' +import { checkFFmpegEncoders } from '../initializers/checker' async function getVideoFileResolution (path: string) { const videoStream = await getVideoFileStream(path) @@ -85,12 +86,8 @@ function transcode (options: TranscodeOptions) { return new Promise(async (res, rej) => { let command = ffmpeg(options.inputPath) .output(options.outputPath) - .videoCodec('libx264') .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) - .outputOption('-movflags faststart') - .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it - .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 - // .outputOption('-crf 18') + .preset(standard) let fps = await getVideoFileFPS(options.inputPath) if (options.resolution !== undefined) { @@ -149,3 +146,117 @@ function getVideoFileStream (path: string) { }) }) } + +/** + * A slightly customised version of the 'veryfast' x264 preset + * + * The veryfast preset is right in the sweet spot of performance + * and quality. Superfast and ultrafast will give you better + * performance, but then quality is noticeably worse. + */ +function veryfast (ffmpeg) { + ffmpeg + .preset(standard) + .outputOption('-preset:v veryfast') + .outputOption(['--aq-mode=2', '--aq-strength=1.3']) + /* + MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html + Our target situation is closer to a livestream than a stream, + since we want to reduce as much a possible the encoding burden, + altough not to the point of a livestream where there is a hard + constraint on the frames per second to be encoded. + + why '--aq-mode=2 --aq-strength=1.3' instead of '-profile:v main'? + Make up for most of the loss of grain and macroblocking + with less computing power. + */ +} + +/** + * A preset optimised for a stillimage audio video + */ +function audio (ffmpeg) { + ffmpeg + .preset(veryfast) + .outputOption('-tune stillimage') +} + +/** + * A toolbox to play with audio + */ +namespace audio { + export const get = (ffmpeg, pos = 0) => { + // without position, ffprobe considers the last input only + // we make it consider the first input only + ffmpeg + .ffprobe(pos, (_,data) => { + return data['streams'].find(stream => { + return stream['codec_type'] === 'audio' + }) + }) + } + + export namespace bitrate { + export const baseKbitrate = 384 + + const toBits = (kbits: number): number => { return kbits * 8000 } + + export const aac = (bitrate: number): number => { + switch (true) { + case bitrate > toBits(384): + return baseKbitrate + default: + return -1 // we interpret it as a signal to copy the audio stream as is + } + } + + export const mp3 = (bitrate: number): number => { + switch (true) { + case bitrate <= toBits(192): + return 128 + case bitrate <= toBits(384): + return 256 + default: + return baseKbitrate + } + } + } +} + +/** + * Standard profile, with variable bitrate audio and faststart. + * + * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel + * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr + */ +async function standard (ffmpeg) { + let _bitrate = audio.bitrate.baseKbitrate + let _ffmpeg = ffmpeg + .format('mp4') + .videoCodec('libx264') + .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution + .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it + .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 + .outputOption('-movflags faststart') + let _audio = audio.get(_ffmpeg) + + if (!_audio) return _ffmpeg.noAudio() + + // we try to reduce the ceiling bitrate by making rough correspondances of bitrates + // of course this is far from perfect, but it might save some space in the end + if (audio.bitrate[_audio['codec_name']]) { + _bitrate = audio.bitrate[_audio['codec_name']](_audio['bit_rate']) + if (_bitrate === -1) { + return _ffmpeg.audioCodec('copy') + } + } + + // we favor VBR, if a good AAC encoder is available + if ((await checkFFmpegEncoders()).get('libfdk_aac')) { + return _ffmpeg + .audioCodec('libfdk_aac') + .audioQuality(5) + } + + return _ffmpeg.audioBitrate(_bitrate) +} -- cgit v1.2.3