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.ts52
1 files changed, 46 insertions, 6 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 76b744de8..13bb2e894 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,17 @@ 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 command
143 .format('mp4')
144 .addOption('-c:v copy')
145 .addOption('-c:a copy')
146 .outputOption('-map_metadata -1') // strip all metadata
147 .outputOption('-movflags faststart')
148 } else if (options.hlsPlaylist) {
138 command = await buildHLSCommand(command, options) 149 command = await buildHLSCommand(command, options)
139 } else { 150 } else {
140 command = await buildx264Command(command, options) 151 command = await buildx264Command(command, options)
@@ -162,6 +173,34 @@ function transcode (options: TranscodeOptions) {
162 }) 173 })
163} 174}
164 175
176async function canDoQuickTranscode (path: string) {
177 // NOTE: This could be optimized by running ffprobe only once (but it runs fast anyway)
178 const videoStream = await getVideoStreamFromFile(path)
179 const parsedAudio = await audio.get(path)
180 const fps = await getVideoFileFPS(path)
181 const bitRate = await getVideoFileBitrate(path)
182 const resolution = await getVideoFileResolution(path)
183
184 // check video params
185 if (videoStream[ 'codec_name' ] !== 'h264')
186 return false
187 if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX)
188 return false
189 if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS))
190 return false
191
192 // check audio params (if audio stream exists)
193 if (parsedAudio.audioStream) {
194 if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac')
195 return false
196 const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ])
197 if (maxAudioBitrate != -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate)
198 return false
199 }
200
201 return true
202}
203
165// --------------------------------------------------------------------------- 204// ---------------------------------------------------------------------------
166 205
167export { 206export {
@@ -173,7 +212,8 @@ export {
173 getVideoFileFPS, 212 getVideoFileFPS,
174 computeResolutionsToTranscode, 213 computeResolutionsToTranscode,
175 audio, 214 audio,
176 getVideoFileBitrate 215 getVideoFileBitrate,
216 canDoQuickTranscode
177} 217}
178 218
179// --------------------------------------------------------------------------- 219// ---------------------------------------------------------------------------
@@ -243,7 +283,7 @@ async function onTranscodingSuccess (options: TranscodeOptions) {
243 await writeFile(options.outputPath, newContent) 283 await writeFile(options.outputPath, newContent)
244} 284}
245 285
246function getVideoFileStream (path: string) { 286function getVideoStreamFromFile (path: string) {
247 return new Promise<any>((res, rej) => { 287 return new Promise<any>((res, rej) => {
248 ffmpeg.ffprobe(path, (err, metadata) => { 288 ffmpeg.ffprobe(path, (err, metadata) => {
249 if (err) return rej(err) 289 if (err) return rej(err)