diff options
author | Chocobozzz <me@florianbigard.com> | 2021-01-28 09:37:26 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-01-28 15:55:39 +0100 |
commit | 529b37527cff5203a0689a15ce73dcee6e1eece2 (patch) | |
tree | 8ca9cbb725b59a8dfadf70526a0e470349c1c720 /server/helpers/ffmpeg-utils.ts | |
parent | 923d3d5ad53cd23bf8107e936408e321f6175517 (diff) | |
download | PeerTube-529b37527cff5203a0689a15ce73dcee6e1eece2.tar.gz PeerTube-529b37527cff5203a0689a15ce73dcee6e1eece2.tar.zst PeerTube-529b37527cff5203a0689a15ce73dcee6e1eece2.zip |
Use a profile manager for transcoding
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 51 |
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' | |||
2 | import * as ffmpeg from 'fluent-ffmpeg' | 2 | import * as ffmpeg from 'fluent-ffmpeg' |
3 | import { readFile, remove, writeFile } from 'fs-extra' | 3 | import { readFile, remove, writeFile } from 'fs-extra' |
4 | import { dirname, join } from 'path' | 4 | import { dirname, join } from 'path' |
5 | import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_ENCODERS } from '@server/initializers/constants' | 5 | import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../shared/models/videos' |
7 | import { checkFFmpegEncoders } from '../initializers/checker-before-init' | 7 | import { checkFFmpegEncoders } from '../initializers/checker-before-init' |
8 | import { CONFIG } from '../initializers/config' | 8 | import { CONFIG } from '../initializers/config' |
@@ -46,11 +46,22 @@ export interface EncoderProfile <T> { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | export type AvailableEncoders = { | 48 | export 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 | ||
63 | type 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 | ||
483 | async function getEncoderBuilderResult (options: { | 501 | async 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 | ||