From 73c695919c6569bfb667c36fc5a6b9b862130a0d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 26 Feb 2018 10:48:53 +0100 Subject: Add 30 fps limit in transcoding --- server/helpers/ffmpeg-utils.ts | 53 +++++++++++++++++++----- server/initializers/constants.ts | 2 + server/tests/api/fixtures/video_60fps_short.mp4 | Bin 0 -> 33968 bytes server/tests/api/videos/video-transcoder.ts | 33 ++++++++++++++- 4 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 server/tests/api/fixtures/video_60fps_short.mp4 (limited to 'server') diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index c2581f460..ad6f2f867 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -1,16 +1,27 @@ import * as ffmpeg from 'fluent-ffmpeg' import { VideoResolution } from '../../shared/models/videos' -import { CONFIG } from '../initializers' +import { CONFIG, MAX_VIDEO_TRANSCODING_FPS } from '../initializers' -function getVideoFileHeight (path: string) { - return new Promise((res, rej) => { - ffmpeg.ffprobe(path, (err, metadata) => { - if (err) return rej(err) +async function getVideoFileHeight (path: string) { + const videoStream = await getVideoFileStream(path) + return videoStream.height +} - const videoStream = metadata.streams.find(s => s.codec_type === 'video') - return res(videoStream.height) - }) - }) +async function getVideoFileFPS (path: string) { + const videoStream = await getVideoFileStream(path) + + for (const key of [ 'r_frame_rate' , 'avg_frame_rate' ]) { + const valuesText: string = videoStream[key] + if (!valuesText) continue + + const [ frames, seconds ] = valuesText.split('/') + if (!frames || !seconds) continue + + const result = parseInt(frames, 10) / parseInt(seconds, 10) + if (result > 0) return result + } + + return 0 } function getDurationFromVideoFile (path: string) { @@ -49,7 +60,9 @@ type TranscodeOptions = { } function transcode (options: TranscodeOptions) { - return new Promise((res, rej) => { + return new Promise(async (res, rej) => { + const fps = await getVideoFileFPS(options.inputPath) + let command = ffmpeg(options.inputPath) .output(options.outputPath) .videoCodec('libx264') @@ -57,6 +70,8 @@ function transcode (options: TranscodeOptions) { .outputOption('-movflags faststart') // .outputOption('-crf 18') + if (fps > MAX_VIDEO_TRANSCODING_FPS) command = command.withFPS(MAX_VIDEO_TRANSCODING_FPS) + if (options.resolution !== undefined) { const size = `?x${options.resolution}` // '?x720' for example command = command.size(size) @@ -74,5 +89,21 @@ export { getVideoFileHeight, getDurationFromVideoFile, generateImageFromVideoFile, - transcode + transcode, + getVideoFileFPS +} + +// --------------------------------------------------------------------------- + +function getVideoFileStream (path: string) { + return new Promise((res, rej) => { + ffmpeg.ffprobe(path, (err, metadata) => { + if (err) return rej(err) + + const videoStream = metadata.streams.find(s => s.codec_type === 'video') + if (!videoStream) throw new Error('Cannot find video stream of ' + path) + + return res(videoStream) + }) + }) } diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 2dc73770d..318df48bf 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -232,6 +232,7 @@ const CONSTRAINTS_FIELDS = { } let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour +const MAX_VIDEO_TRANSCODING_FPS = 30 const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = { LIKE: 'like', @@ -442,6 +443,7 @@ export { VIDEO_LICENCES, VIDEO_RATE_TYPES, VIDEO_MIMETYPE_EXT, + MAX_VIDEO_TRANSCODING_FPS, USER_PASSWORD_RESET_LIFETIME, IMAGE_MIMETYPE_EXT, SCHEDULER_INTERVAL, diff --git a/server/tests/api/fixtures/video_60fps_short.mp4 b/server/tests/api/fixtures/video_60fps_short.mp4 new file mode 100644 index 000000000..ff0593cf3 Binary files /dev/null and b/server/tests/api/fixtures/video_60fps_short.mp4 differ diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index c494e7f67..ef929960d 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts @@ -2,10 +2,13 @@ import * as chai from 'chai' import 'mocha' +import { VideoDetails } from '../../../../shared/models/videos' +import { getVideoFileFPS } from '../../../helpers/ffmpeg-utils' import { - flushAndRunMultipleServers, flushTests, getVideo, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo, + flushAndRunMultipleServers, flushTests, getVideo, getVideosList, killallServers, root, ServerInfo, setAccessTokensToServers, uploadVideo, wait, webtorrentAdd } from '../../utils' +import { join } from 'path' const expect = chai.expect @@ -78,6 +81,34 @@ describe('Test video transcoding', function () { expect(torrent.files[0].path).match(/\.mp4$/) }) + it('Should transcode to 30 FPS', async function () { + this.timeout(60000) + + const videoAttributes = { + name: 'my super 30fps name for server 2', + description: 'my super 30fps description for server 2', + fixture: 'video_60fps_short.mp4' + } + await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) + + await wait(20000) + + const res = await getVideosList(servers[1].url) + + const video = res.body.data[0] + const res2 = await getVideo(servers[1].url, video.id) + const videoDetails: VideoDetails = res2.body + + expect(videoDetails.files).to.have.lengthOf(1) + + for (const resolution of [ '240' ]) { + const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4') + const fps = await getVideoFileFPS(path) + + expect(fps).to.be.below(31) + } + }) + after(async function () { killallServers(servers) -- cgit v1.2.3