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<number>((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) {
}
function transcode (options: TranscodeOptions) {
- return new Promise<void>((res, rej) => {
+ return new Promise<void>(async (res, rej) => {
+ const fps = await getVideoFileFPS(options.inputPath)
+
let command = ffmpeg(options.inputPath)
.output(options.outputPath)
.videoCodec('libx264')
.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)
getVideoFileHeight,
getDurationFromVideoFile,
generateImageFromVideoFile,
- transcode
+ transcode,
+ getVideoFileFPS
+}
+
+// ---------------------------------------------------------------------------
+
+function getVideoFileStream (path: string) {
+ return new Promise<any>((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)
+ })
+ })
}
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
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)