X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fffmpeg-utils.ts;h=bfc942fa3c82440cf9ed41b311b945498c57d055;hb=b81eb8fdc6fabbe517d5731c26da773206ebba62;hp=ad6f2f867f657ab916e7f506893ceef4d43589f6;hpb=73c695919c6569bfb667c36fc5a6b9b862130a0d;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index ad6f2f867..bfc942fa3 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -1,10 +1,18 @@ import * as ffmpeg from 'fluent-ffmpeg' +import { join } from 'path' import { VideoResolution } from '../../shared/models/videos' -import { CONFIG, MAX_VIDEO_TRANSCODING_FPS } from '../initializers' +import { CONFIG, VIDEO_TRANSCODING_FPS } from '../initializers' +import { unlinkPromise } from './core-utils' +import { processImage } from './image-utils' +import { logger } from './logger' -async function getVideoFileHeight (path: string) { +async function getVideoFileResolution (path: string) { const videoStream = await getVideoFileStream(path) - return videoStream.height + + return { + videoFileResolution: Math.min(videoStream.height, videoStream.width), + isPortraitMode: videoStream.height > videoStream.width + } } async function getVideoFileFPS (path: string) { @@ -34,29 +42,43 @@ function getDurationFromVideoFile (path: string) { }) } -function generateImageFromVideoFile (fromPath: string, folder: string, imageName: string, size: string) { +async function generateImageFromVideoFile (fromPath: string, folder: string, imageName: string, size: { width: number, height: number }) { + const pendingImageName = 'pending-' + imageName + const options = { - filename: imageName, + filename: pendingImageName, count: 1, folder } - if (size !== undefined) { - options['size'] = size - } + const pendingImagePath = join(folder, pendingImageName) - return new Promise((res, rej) => { - ffmpeg(fromPath) - .on('error', rej) - .on('end', () => res(imageName)) - .thumbnail(options) - }) + try { + await new Promise((res, rej) => { + ffmpeg(fromPath) + .on('error', rej) + .on('end', () => res(imageName)) + .thumbnail(options) + }) + + const destination = join(folder, imageName) + await processImage({ path: pendingImagePath }, destination, size) + } catch (err) { + logger.error('Cannot generate image from video %s.', fromPath, { err }) + + try { + await unlinkPromise(pendingImagePath) + } catch (err) { + logger.debug('Cannot remove pending image path after generation error.', { err }) + } + } } type TranscodeOptions = { inputPath: string outputPath: string resolution?: VideoResolution + isPortraitMode?: boolean } function transcode (options: TranscodeOptions) { @@ -70,23 +92,30 @@ function transcode (options: TranscodeOptions) { .outputOption('-movflags faststart') // .outputOption('-crf 18') - if (fps > MAX_VIDEO_TRANSCODING_FPS) command = command.withFPS(MAX_VIDEO_TRANSCODING_FPS) + // Our player has some FPS limits + if (fps > VIDEO_TRANSCODING_FPS.MAX) command = command.withFPS(VIDEO_TRANSCODING_FPS.MAX) + else if (fps < VIDEO_TRANSCODING_FPS.MIN) command = command.withFPS(VIDEO_TRANSCODING_FPS.MIN) if (options.resolution !== undefined) { - const size = `?x${options.resolution}` // '?x720' for example + // '?x720' or '720x?' for example + const size = options.isPortraitMode === true ? `${options.resolution}x?` : `?x${options.resolution}` command = command.size(size) } - command.on('error', rej) - .on('end', res) - .run() + command + .on('error', (err, stdout, stderr) => { + logger.error('Error in transcoding job.', { stdout, stderr }) + return rej(err) + }) + .on('end', res) + .run() }) } // --------------------------------------------------------------------------- export { - getVideoFileHeight, + getVideoFileResolution, getDurationFromVideoFile, generateImageFromVideoFile, transcode,