1
0
Fork 0

Set keyframe interval for transcoding (fixes #1147) (#1231)

* Set keyframe interval for transcoding (fixes #1147)

* remove -maxrate and old bitrate setter

* pass fps as parameter

* set type for ffmpeg param

* assign ffmpeg object
This commit is contained in:
Felix Ableitner 2018-10-17 03:15:38 -05:00 committed by Chocobozzz
parent c197877971
commit bcf21a376f

View file

@ -116,28 +116,27 @@ type TranscodeOptions = {
function transcode (options: TranscodeOptions) { function transcode (options: TranscodeOptions) {
return new Promise<void>(async (res, rej) => { return new Promise<void>(async (res, rej) => {
let fps = await getVideoFileFPS(options.inputPath)
// On small/medium resolutions, limit FPS
if (options.resolution !== undefined &&
options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
fps > VIDEO_TRANSCODING_FPS.AVERAGE) {
fps = VIDEO_TRANSCODING_FPS.AVERAGE
}
let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING })
.output(options.outputPath) .output(options.outputPath)
.preset(standard) command = await presetH264(command, options.resolution, fps)
if (CONFIG.TRANSCODING.THREADS > 0) { if (CONFIG.TRANSCODING.THREADS > 0) {
// if we don't set any threads ffmpeg will chose automatically // if we don't set any threads ffmpeg will chose automatically
command = command.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) command = command.outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
} }
let fps = await getVideoFileFPS(options.inputPath)
if (options.resolution !== undefined) { if (options.resolution !== undefined) {
// '?x720' or '720x?' for example // '?x720' or '720x?' for example
const size = options.isPortraitMode === true ? `${options.resolution}x?` : `?x${options.resolution}` const size = options.isPortraitMode === true ? `${options.resolution}x?` : `?x${options.resolution}`
command = command.size(size) command = command.size(size)
// On small/medium resolutions, limit FPS
if (
options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
fps > VIDEO_TRANSCODING_FPS.AVERAGE
) {
fps = VIDEO_TRANSCODING_FPS.AVERAGE
}
} }
if (fps) { if (fps) {
@ -148,12 +147,6 @@ function transcode (options: TranscodeOptions) {
command = command.withFPS(fps) command = command.withFPS(fps)
} }
// Constrained Encoding (VBV)
// https://slhck.info/video/2017/03/01/rate-control.html
// https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
const targetBitrate = getTargetBitrate(options.resolution, fps, VIDEO_TRANSCODING_FPS)
command.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`])
command command
.on('error', (err, stdout, stderr) => { .on('error', (err, stdout, stderr) => {
logger.error('Error in transcoding job.', { stdout, stderr }) logger.error('Error in transcoding job.', { stdout, stderr })
@ -199,9 +192,9 @@ function getVideoFileStream (path: string) {
* and quality. Superfast and ultrafast will give you better * and quality. Superfast and ultrafast will give you better
* performance, but then quality is noticeably worse. * performance, but then quality is noticeably worse.
*/ */
function veryfast (_ffmpeg) { async function presetH264VeryFast (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
_ffmpeg const localFfmpeg = await presetH264(ffmpeg, resolution, fps)
.preset(standard) localFfmpeg
.outputOption('-preset:v veryfast') .outputOption('-preset:v veryfast')
.outputOption(['--aq-mode=2', '--aq-strength=1.3']) .outputOption(['--aq-mode=2', '--aq-strength=1.3'])
/* /*
@ -220,9 +213,9 @@ function veryfast (_ffmpeg) {
/** /**
* A preset optimised for a stillimage audio video * A preset optimised for a stillimage audio video
*/ */
function audio (_ffmpeg) { async function presetStillImageWithAudio (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
_ffmpeg const localFfmpeg = await presetH264VeryFast(ffmpeg, resolution, fps)
.preset(veryfast) localFfmpeg
.outputOption('-tune stillimage') .outputOption('-tune stillimage')
} }
@ -290,8 +283,8 @@ namespace audio {
* As for the audio, quality '5' is the highest and ensures 96-112kbps/channel * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel
* See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr
*/ */
async function standard (_ffmpeg) { async function presetH264 (ffmpeg: ffmpeg, resolution: VideoResolution, fps: number): ffmpeg {
let localFfmpeg = _ffmpeg let localFfmpeg = ffmpeg
.format('mp4') .format('mp4')
.videoCodec('libx264') .videoCodec('libx264')
.outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution
@ -324,5 +317,16 @@ async function standard (_ffmpeg) {
if (bitrate !== undefined) return localFfmpeg.audioBitrate(bitrate) if (bitrate !== undefined) return localFfmpeg.audioBitrate(bitrate)
// Constrained Encoding (VBV)
// https://slhck.info/video/2017/03/01/rate-control.html
// https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
localFfmpeg.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`])
// Keyframe interval of 2 seconds for faster seeking and resolution switching.
// https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html
// https://superuser.com/a/908325
localFfmpeg.outputOption(`-g ${ fps * 2 }`)
return localFfmpeg return localFfmpeg
} }