* 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:
parent
c197877971
commit
bcf21a376f
1 changed files with 28 additions and 24 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue