From 837666fe48f9ed786db75c96e2025cbcf20a1e3b Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Mon, 20 Jan 2020 20:40:30 +0100 Subject: [PATCH] Add tests for video downscale framerate matching --- server/helpers/ffmpeg-utils.ts | 12 +++++--- server/helpers/logger.ts | 2 +- server/tests/api/videos/video-transcoder.ts | 34 +++++++++++++++++++++ shared/extra-utils/miscs/miscs.ts | 25 ++++++++++++++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 78f9ba07c..63dc5b6a3 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -263,6 +263,10 @@ async function canDoQuickTranscode (path: string): Promise { return true } +function getClosestFramerateStandard (fps: number, hd = false): number { + return VIDEO_TRANSCODING_FPS[hd ? 'HD_STANDARD' : 'STANDARD'].slice(0).sort((a, b) => fps % a - fps % b)[0] +} + // --------------------------------------------------------------------------- export { @@ -291,11 +295,11 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco options.resolution !== undefined && options.resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN && fps > VIDEO_TRANSCODING_FPS.AVERAGE || - // If the video is doesn't match had standard - !VIDEO_TRANSCODING_FPS.HD_STANDARD.map(value => fps % value).includes(0) + // If the video is doesn't match hd standard + !VIDEO_TRANSCODING_FPS.HD_STANDARD.some(value => fps % value === 0) ) { // Get closest standard framerate by modulo: downsampling has to be done to a divisor of the nominal fps value - fps = VIDEO_TRANSCODING_FPS.STANDARD.sort((a, b) => fps % a - fps % b)[0] + fps = getClosestFramerateStandard(fps) } command = await presetH264(command, options.inputPath, options.resolution, fps) @@ -308,7 +312,7 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco if (fps) { // Hard FPS limits - if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = VIDEO_TRANSCODING_FPS.HD_STANDARD.sort((a, b) => fps % a - fps % b)[0] + if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, true) else if (fps < VIDEO_TRANSCODING_FPS.MIN) fps = VIDEO_TRANSCODING_FPS.MIN command = command.withFPS(fps) diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts index 395417612..fd2988ad0 100644 --- a/server/helpers/logger.ts +++ b/server/helpers/logger.ts @@ -5,7 +5,7 @@ import * as winston from 'winston' import { FileTransportOptions } from 'winston/lib/winston/transports' import { CONFIG } from '../initializers/config' import { omit } from 'lodash' -import { LOG_FILENAME } from '@server/initializers/constants' +import { LOG_FILENAME } from '../initializers/constants' const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 4be74901a..0104c94fc 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts @@ -11,6 +11,7 @@ import { doubleFollow, flushAndRunMultipleServers, generateHighBitrateVideo, + generateVideoWithFramerate, getMyVideos, getVideo, getVideosList, @@ -416,6 +417,39 @@ describe('Test video transcoding', function () { } }) + it('Should downscale to the closest divisor standard framerate', async function () { + this.timeout(160000) + + let tempFixturePath: string + + { + tempFixturePath = await generateVideoWithFramerate() + + const fps = await getVideoFileFPS(tempFixturePath) + expect(fps).to.be.equal(59) + } + + const videoAttributes = { + name: '59fps video', + description: '59fps video', + fixture: tempFixturePath + } + + await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + + const video = res.body.data.find(v => v.name === videoAttributes.name) + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') + const fps = await getVideoFileFPS(path) + + expect(fps).to.be.equal(25) + } + }) + after(async function () { await cleanupTests(servers) }) diff --git a/shared/extra-utils/miscs/miscs.ts b/shared/extra-utils/miscs/miscs.ts index 6b0f6d990..c957a6abe 100644 --- a/shared/extra-utils/miscs/miscs.ts +++ b/shared/extra-utils/miscs/miscs.ts @@ -104,6 +104,28 @@ async function generateHighBitrateVideo () { return tempFixturePath } +async function generateVideoWithFramerate (fps = 60) { + const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true) + + await ensureDir(dirname(tempFixturePath)) + + const exists = await pathExists(tempFixturePath) + if (!exists) { + return new Promise(async (res, rej) => { + ffmpeg() + .outputOptions([ '-f rawvideo', '-video_size 320x240', '-i /dev/urandom' ]) + .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ]) + .outputOptions([ `-r ${fps}` ]) + .output(tempFixturePath) + .on('error', rej) + .on('end', () => res(tempFixturePath)) + .run() + }) + } + + return tempFixturePath +} + // --------------------------------------------------------------------------- export { @@ -115,5 +137,6 @@ export { testImage, buildAbsoluteFixturePath, root, - generateHighBitrateVideo + generateHighBitrateVideo, + generateVideoWithFramerate }