diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2018-05-21 13:14:29 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-08-01 10:57:13 +0200 |
commit | 4176e227cb18c40e13f30f4634d128cc3169e9d4 (patch) | |
tree | bf31f4abb7352d309a4d19311b78013162699d6f /server/helpers/ffmpeg-utils.ts | |
parent | e80687c458065500e5f1d72fdedd2906c0d58b1a (diff) | |
download | PeerTube-4176e227cb18c40e13f30f4634d128cc3169e9d4.tar.gz PeerTube-4176e227cb18c40e13f30f4634d128cc3169e9d4.tar.zst PeerTube-4176e227cb18c40e13f30f4634d128cc3169e9d4.zip |
Fixing #626 with ffmpeg's low default audio bitrate
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 121 |
1 files changed, 116 insertions, 5 deletions
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' | |||
5 | import { unlinkPromise } from './core-utils' | 5 | import { unlinkPromise } from './core-utils' |
6 | import { processImage } from './image-utils' | 6 | import { processImage } from './image-utils' |
7 | import { logger } from './logger' | 7 | import { logger } from './logger' |
8 | import { checkFFmpegEncoders } from '../initializers/checker' | ||
8 | 9 | ||
9 | async function getVideoFileResolution (path: string) { | 10 | async function getVideoFileResolution (path: string) { |
10 | const videoStream = await getVideoFileStream(path) | 11 | const videoStream = await getVideoFileStream(path) |
@@ -85,12 +86,8 @@ function transcode (options: TranscodeOptions) { | |||
85 | return new Promise<void>(async (res, rej) => { | 86 | return new Promise<void>(async (res, rej) => { |
86 | let command = ffmpeg(options.inputPath) | 87 | let command = ffmpeg(options.inputPath) |
87 | .output(options.outputPath) | 88 | .output(options.outputPath) |
88 | .videoCodec('libx264') | ||
89 | .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) | 89 | .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) |
90 | .outputOption('-movflags faststart') | 90 | .preset(standard) |
91 | .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it | ||
92 | .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 | ||
93 | // .outputOption('-crf 18') | ||
94 | 91 | ||
95 | let fps = await getVideoFileFPS(options.inputPath) | 92 | let fps = await getVideoFileFPS(options.inputPath) |
96 | if (options.resolution !== undefined) { | 93 | if (options.resolution !== undefined) { |
@@ -149,3 +146,117 @@ function getVideoFileStream (path: string) { | |||
149 | }) | 146 | }) |
150 | }) | 147 | }) |
151 | } | 148 | } |
149 | |||
150 | /** | ||
151 | * A slightly customised version of the 'veryfast' x264 preset | ||
152 | * | ||
153 | * The veryfast preset is right in the sweet spot of performance | ||
154 | * and quality. Superfast and ultrafast will give you better | ||
155 | * performance, but then quality is noticeably worse. | ||
156 | */ | ||
157 | function veryfast (ffmpeg) { | ||
158 | ffmpeg | ||
159 | .preset(standard) | ||
160 | .outputOption('-preset:v veryfast') | ||
161 | .outputOption(['--aq-mode=2', '--aq-strength=1.3']) | ||
162 | /* | ||
163 | MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html | ||
164 | Our target situation is closer to a livestream than a stream, | ||
165 | since we want to reduce as much a possible the encoding burden, | ||
166 | altough not to the point of a livestream where there is a hard | ||
167 | constraint on the frames per second to be encoded. | ||
168 | |||
169 | why '--aq-mode=2 --aq-strength=1.3' instead of '-profile:v main'? | ||
170 | Make up for most of the loss of grain and macroblocking | ||
171 | with less computing power. | ||
172 | */ | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * A preset optimised for a stillimage audio video | ||
177 | */ | ||
178 | function audio (ffmpeg) { | ||
179 | ffmpeg | ||
180 | .preset(veryfast) | ||
181 | .outputOption('-tune stillimage') | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * A toolbox to play with audio | ||
186 | */ | ||
187 | namespace audio { | ||
188 | export const get = (ffmpeg, pos = 0) => { | ||
189 | // without position, ffprobe considers the last input only | ||
190 | // we make it consider the first input only | ||
191 | ffmpeg | ||
192 | .ffprobe(pos, (_,data) => { | ||
193 | return data['streams'].find(stream => { | ||
194 | return stream['codec_type'] === 'audio' | ||
195 | }) | ||
196 | }) | ||
197 | } | ||
198 | |||
199 | export namespace bitrate { | ||
200 | export const baseKbitrate = 384 | ||
201 | |||
202 | const toBits = (kbits: number): number => { return kbits * 8000 } | ||
203 | |||
204 | export const aac = (bitrate: number): number => { | ||
205 | switch (true) { | ||
206 | case bitrate > toBits(384): | ||
207 | return baseKbitrate | ||
208 | default: | ||
209 | return -1 // we interpret it as a signal to copy the audio stream as is | ||
210 | } | ||
211 | } | ||
212 | |||
213 | export const mp3 = (bitrate: number): number => { | ||
214 | switch (true) { | ||
215 | case bitrate <= toBits(192): | ||
216 | return 128 | ||
217 | case bitrate <= toBits(384): | ||
218 | return 256 | ||
219 | default: | ||
220 | return baseKbitrate | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * Standard profile, with variable bitrate audio and faststart. | ||
228 | * | ||
229 | * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel | ||
230 | * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr | ||
231 | */ | ||
232 | async function standard (ffmpeg) { | ||
233 | let _bitrate = audio.bitrate.baseKbitrate | ||
234 | let _ffmpeg = ffmpeg | ||
235 | .format('mp4') | ||
236 | .videoCodec('libx264') | ||
237 | .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution | ||
238 | .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it | ||
239 | .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 | ||
240 | .outputOption('-movflags faststart') | ||
241 | let _audio = audio.get(_ffmpeg) | ||
242 | |||
243 | if (!_audio) return _ffmpeg.noAudio() | ||
244 | |||
245 | // we try to reduce the ceiling bitrate by making rough correspondances of bitrates | ||
246 | // of course this is far from perfect, but it might save some space in the end | ||
247 | if (audio.bitrate[_audio['codec_name']]) { | ||
248 | _bitrate = audio.bitrate[_audio['codec_name']](_audio['bit_rate']) | ||
249 | if (_bitrate === -1) { | ||
250 | return _ffmpeg.audioCodec('copy') | ||
251 | } | ||
252 | } | ||
253 | |||
254 | // we favor VBR, if a good AAC encoder is available | ||
255 | if ((await checkFFmpegEncoders()).get('libfdk_aac')) { | ||
256 | return _ffmpeg | ||
257 | .audioCodec('libfdk_aac') | ||
258 | .audioQuality(5) | ||
259 | } | ||
260 | |||
261 | return _ffmpeg.audioBitrate(_bitrate) | ||
262 | } | ||