From 92a89a87bb494a6945bb9503c5bc1a21c8f1758d Mon Sep 17 00:00:00 2001 From: SimplyBoo Date: Fri, 23 Apr 2021 09:18:41 +0100 Subject: [PATCH] Perform caches operations with lower priority than streaming --- server/src/cache/import-utils.ts | 35 +++++++++++++++++--------------- server/src/cache/transcoder.ts | 22 +++++++++++++++++--- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/server/src/cache/import-utils.ts b/server/src/cache/import-utils.ts index 825661e..cf3d529 100755 --- a/server/src/cache/import-utils.ts +++ b/server/src/cache/import-utils.ts @@ -23,6 +23,14 @@ export interface LoadedImage { // Nice level for low priority tasks const LOW_PRIORITY = 15; +export interface TranscoderOptions { + input: string; + output: string | Stream.Writable; + outputOptions: string[]; + inputOptions?: string[]; + important?: boolean; +} + export class ImportUtils { public static async getFileCreationTime(path: string): Promise { const stat = await Util.promisify(FS.stat)(path); @@ -181,29 +189,24 @@ export class ImportUtils { // You would think we'd just use fluent-ffmpeg's streaming functionality. // However, there appears to be a bug in streaming I can't track down // that corrupts that output stream even if piped to a file. - public static async transcode( - input: string, - output: string | Stream.Writable, - outputOptions: string[], - inputOptions?: string[], - ): Promise { + public static async transcode(options: TranscoderOptions): Promise { return new Promise((resolve, reject) => { const args = []; - if (inputOptions) { - args.push(...inputOptions); + if (options.inputOptions) { + args.push(...options.inputOptions); } - args.push(...['-i', input]); - args.push(...outputOptions); + args.push(...['-i', options.input]); + args.push(...options.outputOptions); - if (typeof output === 'string') { - args.push(output); + if (typeof options.output === 'string') { + args.push(options.output); } else { args.push('pipe:1'); } const proc = ChildProcess.spawn('ffmpeg', args); - if (typeof output === 'string') { + if (!options.important) { ImportUtils.setNice(proc.pid, LOW_PRIORITY); } @@ -229,15 +232,15 @@ export class ImportUtils { } }); - if (typeof output !== 'string') { - output.on('close', () => { + if (typeof options.output !== 'string') { + options.output.on('close', () => { // Wait slightly to avoid race condition under load. setTimeout(() => { proc.kill('SIGKILL'); }, 20); }); - proc.stdout.pipe(output, { end: true }); + proc.stdout.pipe(options.output, { end: true }); } }); } diff --git a/server/src/cache/transcoder.ts b/server/src/cache/transcoder.ts index 3806614..84892c9 100755 --- a/server/src/cache/transcoder.ts +++ b/server/src/cache/transcoder.ts @@ -37,7 +37,12 @@ export class Transcoder { inputOptions.push('-ss'); inputOptions.push(`00:00:${offset >= 60 ? 59 : offset.toFixed(2)}`); } - await ImportUtils.transcode(media.absolutePath, path, args, inputOptions); + await ImportUtils.transcode({ + input: media.absolutePath, + output: path, + outputOptions: args, + inputOptions, + }); } public async createVideoPreview(media: Media): Promise { @@ -69,7 +74,11 @@ export class Transcoder { '1', ]; const path = `${Config.get().cachePath}/previews/${media.hash}.png`; - await ImportUtils.transcode(media.absolutePath, path, args); + await ImportUtils.transcode({ + input: media.absolutePath, + output: path, + outputOptions: args, + }); } public getThumbnailPath(media: Media): string { @@ -169,7 +178,14 @@ export class Transcoder { const args = ['-y', ...audioCodec, ...scale, ...videoCodec, '-f', 'mpegts', '-muxdelay', '0']; - await ImportUtils.transcode(media.absolutePath, stream, args, inputOptions); + // If not realtime then transcode as low priority. + await ImportUtils.transcode({ + input: media.absolutePath, + output: stream, + outputOptions: args, + inputOptions, + important: realtime, + }); } public async transcodeMedia(media: Media): Promise {