aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/ffmpeg-utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r--server/helpers/ffmpeg-utils.ts49
1 files changed, 43 insertions, 6 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 76b744de8..2fdf34cb7 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -1,6 +1,6 @@
1import * as ffmpeg from 'fluent-ffmpeg' 1import * as ffmpeg from 'fluent-ffmpeg'
2import { dirname, join } from 'path' 2import { dirname, join } from 'path'
3import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' 3import { getTargetBitrate, getMaxBitrate, VideoResolution } from '../../shared/models/videos'
4import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' 4import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants'
5import { processImage } from './image-utils' 5import { processImage } from './image-utils'
6import { logger } from './logger' 6import { logger } from './logger'
@@ -31,7 +31,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
31} 31}
32 32
33async function getVideoFileSize (path: string) { 33async function getVideoFileSize (path: string) {
34 const videoStream = await getVideoFileStream(path) 34 const videoStream = await getVideoStreamFromFile(path)
35 35
36 return { 36 return {
37 width: videoStream.width, 37 width: videoStream.width,
@@ -49,7 +49,7 @@ async function getVideoFileResolution (path: string) {
49} 49}
50 50
51async function getVideoFileFPS (path: string) { 51async function getVideoFileFPS (path: string) {
52 const videoStream = await getVideoFileStream(path) 52 const videoStream = await getVideoStreamFromFile(path)
53 53
54 for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { 54 for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
55 const valuesText: string = videoStream[key] 55 const valuesText: string = videoStream[key]
@@ -122,6 +122,7 @@ type TranscodeOptions = {
122 outputPath: string 122 outputPath: string
123 resolution: VideoResolution 123 resolution: VideoResolution
124 isPortraitMode?: boolean 124 isPortraitMode?: boolean
125 doQuickTranscode?: Boolean
125 126
126 hlsPlaylist?: { 127 hlsPlaylist?: {
127 videoFilename: string 128 videoFilename: string
@@ -134,7 +135,18 @@ function transcode (options: TranscodeOptions) {
134 let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) 135 let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING })
135 .output(options.outputPath) 136 .output(options.outputPath)
136 137
137 if (options.hlsPlaylist) { 138 if (options.doQuickTranscode) {
139 if (options.hlsPlaylist) {
140 throw(Error("Quick transcode and HLS can't be used at the same time"))
141 }
142
143 command
144 .format('mp4')
145 .addOption('-c:v copy')
146 .addOption('-c:a copy')
147 .outputOption('-map_metadata -1') // strip all metadata
148 .outputOption('-movflags faststart')
149 } else if (options.hlsPlaylist) {
138 command = await buildHLSCommand(command, options) 150 command = await buildHLSCommand(command, options)
139 } else { 151 } else {
140 command = await buildx264Command(command, options) 152 command = await buildx264Command(command, options)
@@ -162,6 +174,30 @@ function transcode (options: TranscodeOptions) {
162 }) 174 })
163} 175}
164 176
177async function canDoQuickTranscode (path: string): Promise<boolean> {
178 // NOTE: This could be optimized by running ffprobe only once (but it runs fast anyway)
179 const videoStream = await getVideoStreamFromFile(path)
180 const parsedAudio = await audio.get(path)
181 const fps = await getVideoFileFPS(path)
182 const bitRate = await getVideoFileBitrate(path)
183 const resolution = await getVideoFileResolution(path)
184
185 // check video params
186 if (videoStream[ 'codec_name' ] !== 'h264') return false
187 if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false
188 if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false
189
190 // check audio params (if audio stream exists)
191 if (parsedAudio.audioStream) {
192 if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac') return false
193
194 const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ])
195 if (maxAudioBitrate !== -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate) return false
196 }
197
198 return true
199}
200
165// --------------------------------------------------------------------------- 201// ---------------------------------------------------------------------------
166 202
167export { 203export {
@@ -173,7 +209,8 @@ export {
173 getVideoFileFPS, 209 getVideoFileFPS,
174 computeResolutionsToTranscode, 210 computeResolutionsToTranscode,
175 audio, 211 audio,
176 getVideoFileBitrate 212 getVideoFileBitrate,
213 canDoQuickTranscode
177} 214}
178 215
179// --------------------------------------------------------------------------- 216// ---------------------------------------------------------------------------
@@ -243,7 +280,7 @@ async function onTranscodingSuccess (options: TranscodeOptions) {
243 await writeFile(options.outputPath, newContent) 280 await writeFile(options.outputPath, newContent)
244} 281}
245 282
246function getVideoFileStream (path: string) { 283function getVideoStreamFromFile (path: string) {
247 return new Promise<any>((res, rej) => { 284 return new Promise<any>((res, rej) => {
248 ffmpeg.ffprobe(path, (err, metadata) => { 285 ffmpeg.ffprobe(path, (err, metadata) => {
249 if (err) return rej(err) 286 if (err) return rej(err)