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.ts51
1 files changed, 38 insertions, 13 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index f85b9f316..7d46130ec 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -2,7 +2,7 @@ import { Job } from 'bull'
2import * as ffmpeg from 'fluent-ffmpeg' 2import * as ffmpeg from 'fluent-ffmpeg'
3import { readFile, remove, writeFile } from 'fs-extra' 3import { readFile, remove, writeFile } from 'fs-extra'
4import { dirname, join } from 'path' 4import { dirname, join } from 'path'
5import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS } from '@server/initializers/constants' 5import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants'
6import { VideoResolution } from '../../shared/models/videos' 6import { VideoResolution } from '../../shared/models/videos'
7import { checkFFmpegEncoders } from '../initializers/checker-before-init' 7import { checkFFmpegEncoders } from '../initializers/checker-before-init'
8import { CONFIG } from '../initializers/config' 8import { CONFIG } from '../initializers/config'
@@ -46,11 +46,22 @@ export interface EncoderProfile <T> {
46} 46}
47 47
48export type AvailableEncoders = { 48export type AvailableEncoders = {
49 [ id in 'live' | 'vod' ]: { 49 live: {
50 [ encoder in 'libx264' | 'aac' | 'libfdk_aac' ]?: EncoderProfile<EncoderOptionsBuilder> 50 [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
51 }
52
53 vod: {
54 [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
55 }
56
57 encodersToTry: {
58 video: string[]
59 audio: string[]
51 } 60 }
52} 61}
53 62
63type StreamType = 'audio' | 'video'
64
54// --------------------------------------------------------------------------- 65// ---------------------------------------------------------------------------
55// Image manipulation 66// Image manipulation
56// --------------------------------------------------------------------------- 67// ---------------------------------------------------------------------------
@@ -243,8 +254,10 @@ async function getLiveTranscodingCommand (options: {
243 254
244 const baseEncoderBuilderParams = { 255 const baseEncoderBuilderParams = {
245 input, 256 input,
257
246 availableEncoders, 258 availableEncoders,
247 profile, 259 profile,
260
248 fps: resolutionFPS, 261 fps: resolutionFPS,
249 resolution, 262 resolution,
250 streamNum: i, 263 streamNum: i,
@@ -252,7 +265,8 @@ async function getLiveTranscodingCommand (options: {
252 } 265 }
253 266
254 { 267 {
255 const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType: 'VIDEO' })) 268 const streamType: StreamType = 'video'
269 const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType }))
256 if (!builderResult) { 270 if (!builderResult) {
257 throw new Error('No available live video encoder found') 271 throw new Error('No available live video encoder found')
258 } 272 }
@@ -268,7 +282,8 @@ async function getLiveTranscodingCommand (options: {
268 } 282 }
269 283
270 { 284 {
271 const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType: 'AUDIO' })) 285 const streamType: StreamType = 'audio'
286 const builderResult = await getEncoderBuilderResult(Object.assign({}, baseEncoderBuilderParams, { streamType }))
272 if (!builderResult) { 287 if (!builderResult) {
273 throw new Error('No available live audio encoder found') 288 throw new Error('No available live audio encoder found')
274 } 289 }
@@ -480,8 +495,11 @@ function getHLSVideoPath (options: HLSTranscodeOptions | HLSFromTSTranscodeOptio
480// Transcoding presets 495// Transcoding presets
481// --------------------------------------------------------------------------- 496// ---------------------------------------------------------------------------
482 497
498// Run encoder builder depending on available encoders
499// Try encoders by priority: if the encoder is available, run the chosen profile or fallback to the default one
500// If the default one does not exist, check the next encoder
483async function getEncoderBuilderResult (options: { 501async function getEncoderBuilderResult (options: {
484 streamType: string 502 streamType: 'video' | 'audio'
485 input: string 503 input: string
486 504
487 availableEncoders: AvailableEncoders 505 availableEncoders: AvailableEncoders
@@ -495,17 +513,24 @@ async function getEncoderBuilderResult (options: {
495}) { 513}) {
496 const { availableEncoders, input, profile, resolution, streamType, fps, streamNum, videoType } = options 514 const { availableEncoders, input, profile, resolution, streamType, fps, streamNum, videoType } = options
497 515
498 const encodersToTry: string[] = VIDEO_TRANSCODING_ENCODERS[streamType] 516 const encodersToTry = availableEncoders.encodersToTry[streamType]
517 const encoders = availableEncoders[videoType]
499 518
500 for (const encoder of encodersToTry) { 519 for (const encoder of encodersToTry) {
501 if (!(await checkFFmpegEncoders()).get(encoder) || !availableEncoders[videoType][encoder]) continue 520 if (!(await checkFFmpegEncoders()).get(encoder) || !encoders[encoder]) continue
502 521
503 const builderProfiles: EncoderProfile<EncoderOptionsBuilder> = availableEncoders[videoType][encoder] 522 // An object containing available profiles for this encoder
523 const builderProfiles: EncoderProfile<EncoderOptionsBuilder> = encoders[encoder]
504 let builder = builderProfiles[profile] 524 let builder = builderProfiles[profile]
505 525
506 if (!builder) { 526 if (!builder) {
507 logger.debug('Profile %s for encoder %s not available. Fallback to default.', profile, encoder) 527 logger.debug('Profile %s for encoder %s not available. Fallback to default.', profile, encoder)
508 builder = builderProfiles.default 528 builder = builderProfiles.default
529
530 if (!builder) {
531 logger.debug('Default profile for encoder %s not available. Try next available encoder.', encoder)
532 continue
533 }
509 } 534 }
510 535
511 const result = await builder({ input, resolution: resolution, fps, streamNum }) 536 const result = await builder({ input, resolution: resolution, fps, streamNum })
@@ -538,11 +563,11 @@ async function presetVideo (
538 // Audio encoder 563 // Audio encoder
539 const parsedAudio = await getAudioStream(input) 564 const parsedAudio = await getAudioStream(input)
540 565
541 let streamsToProcess = [ 'AUDIO', 'VIDEO' ] 566 let streamsToProcess: StreamType[] = [ 'audio', 'video' ]
542 567
543 if (!parsedAudio.audioStream) { 568 if (!parsedAudio.audioStream) {
544 localCommand = localCommand.noAudio() 569 localCommand = localCommand.noAudio()
545 streamsToProcess = [ 'VIDEO' ] 570 streamsToProcess = [ 'audio' ]
546 } 571 }
547 572
548 for (const streamType of streamsToProcess) { 573 for (const streamType of streamsToProcess) {
@@ -564,9 +589,9 @@ async function presetVideo (
564 589
565 logger.debug('Apply ffmpeg params from %s.', builderResult.encoder, builderResult) 590 logger.debug('Apply ffmpeg params from %s.', builderResult.encoder, builderResult)
566 591
567 if (streamType === 'VIDEO') { 592 if (streamType === 'video') {
568 localCommand.videoCodec(builderResult.encoder) 593 localCommand.videoCodec(builderResult.encoder)
569 } else if (streamType === 'AUDIO') { 594 } else if (streamType === 'audio') {
570 localCommand.audioCodec(builderResult.encoder) 595 localCommand.audioCodec(builderResult.encoder)
571 } 596 }
572 597