import * as ffmpeg from 'fluent-ffmpeg'
import { dirname, join } from 'path'
-import { getTargetBitrate, VideoResolution } from '../../shared/models/videos'
+import { getTargetBitrate, getMaxBitrate, VideoResolution } from '../../shared/models/videos'
import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants'
import { processImage } from './image-utils'
import { logger } from './logger'
}
async function getVideoFileSize (path: string) {
- const videoStream = await getVideoFileStream(path)
+ const videoStream = await getVideoStreamFromFile(path)
return {
width: videoStream.width,
}
async function getVideoFileFPS (path: string) {
- const videoStream = await getVideoFileStream(path)
+ const videoStream = await getVideoStreamFromFile(path)
for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
const valuesText: string = videoStream[key]
outputPath: string
resolution: VideoResolution
isPortraitMode?: boolean
+ doQuickTranscode?: Boolean
hlsPlaylist?: {
videoFilename: string
let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING })
.output(options.outputPath)
- if (options.hlsPlaylist) {
+ if (options.doQuickTranscode) {
+ if (options.hlsPlaylist) {
+ throw(Error("Quick transcode and HLS can't be used at the same time"))
+ }
+
+ command
+ .format('mp4')
+ .addOption('-c:v copy')
+ .addOption('-c:a copy')
+ .outputOption('-map_metadata -1') // strip all metadata
+ .outputOption('-movflags faststart')
+ } else if (options.hlsPlaylist) {
command = await buildHLSCommand(command, options)
} else {
command = await buildx264Command(command, options)
})
}
+async function canDoQuickTranscode (path: string): Promise<boolean> {
+ // NOTE: This could be optimized by running ffprobe only once (but it runs fast anyway)
+ const videoStream = await getVideoStreamFromFile(path)
+ const parsedAudio = await audio.get(path)
+ const fps = await getVideoFileFPS(path)
+ const bitRate = await getVideoFileBitrate(path)
+ const resolution = await getVideoFileResolution(path)
+
+ // check video params
+ if (videoStream[ 'codec_name' ] !== 'h264') return false
+ if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false
+ if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false
+
+ // check audio params (if audio stream exists)
+ if (parsedAudio.audioStream) {
+ if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac') return false
+
+ const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ])
+ if (maxAudioBitrate !== -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate) return false
+ }
+
+ return true
+}
+
// ---------------------------------------------------------------------------
export {
getVideoFileFPS,
computeResolutionsToTranscode,
audio,
- getVideoFileBitrate
+ getVideoFileBitrate,
+ canDoQuickTranscode
}
// ---------------------------------------------------------------------------
await writeFile(options.outputPath, newContent)
}
-function getVideoFileStream (path: string) {
+function getVideoStreamFromFile (path: string) {
return new Promise<any>((res, rej) => {
ffmpeg.ffprobe(path, (err, metadata) => {
if (err) return rej(err)
import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants'
import { join } from 'path'
-import { getVideoFileFPS, transcode } from '../helpers/ffmpeg-utils'
+import { getVideoFileFPS, transcode, canDoQuickTranscode } from '../helpers/ffmpeg-utils'
import { ensureDir, move, remove, stat } from 'fs-extra'
import { logger } from '../helpers/logger'
import { VideoResolution } from '../../shared/models/videos'
import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
import { CONFIG } from '../initializers/config'
+/**
+ * Optimize the original video file and replace it. The resolution is not changed.
+ */
async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) {
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const newExtname = '.mp4'
const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile))
const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
+ const doQuickTranscode = await(canDoQuickTranscode(videoInputPath))
+
const transcodeOptions = {
inputPath: videoInputPath,
outputPath: videoTranscodedPath,
- resolution: inputVideoFile.resolution
+ resolution: inputVideoFile.resolution,
+ doQuickTranscode
}
// Could be very long!
}
}
+/**
+ * Transcode the original video file to a lower resolution.
+ */
async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) {
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const extname = '.mp4'
import 'mocha'
import { omit } from 'lodash'
import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos'
-import { audio, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
+import { audio, canDoQuickTranscode, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
import {
buildAbsoluteFixturePath,
cleanupTests,
ServerInfo,
setAccessTokensToServers,
uploadVideo,
+ waitJobs,
webtorrentAdd
} from '../../../../shared/extra-utils'
import { join } from 'path'
-import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
const expect = chai.expect
it('Should accept and transcode additional extensions', async function () {
this.timeout(300000)
+ let tempFixturePath: string
+
+ {
+ tempFixturePath = await generateHighBitrateVideo()
+
+ const bitrate = await getVideoFileBitrate(tempFixturePath)
+ expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 60, VIDEO_TRANSCODING_FPS))
+ }
+
for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) {
const videoAttributes = {
name: fixture,
}
})
+ it('Should correctly detect if quick transcode is possible', async function () {
+ this.timeout(10000)
+
+ expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.mp4'))).to.be.true
+ expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.webm'))).to.be.false
+ })
+
after(async function () {
await cleanupTests(servers)
})