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 {
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)
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)
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
doubleFollow,
flushAndRunMultipleServers,
generateHighBitrateVideo,
+ generateVideoWithFramerate,
getMyVideos,
getVideo,
getVideosList,
}
})
+ 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)
})
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<string>(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 {
testImage,
buildAbsoluteFixturePath,
root,
- generateHighBitrateVideo
+ generateHighBitrateVideo,
+ generateVideoWithFramerate
}