aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/ffmpeg-utils.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-01-28 15:52:44 +0100
committerChocobozzz <me@florianbigard.com>2021-01-28 15:55:39 +0100
commit1896bca09e088b0da9d5e845407ecebae330618c (patch)
tree56041c445c0cd49aca536d0fd6b586730f4d341e /server/helpers/ffmpeg-utils.ts
parent529b37527cff5203a0689a15ce73dcee6e1eece2 (diff)
downloadPeerTube-1896bca09e088b0da9d5e845407ecebae330618c.tar.gz
PeerTube-1896bca09e088b0da9d5e845407ecebae330618c.tar.zst
PeerTube-1896bca09e088b0da9d5e845407ecebae330618c.zip
Support transcoding options/encoders by plugins
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r--server/helpers/ffmpeg-utils.ts88
1 files changed, 50 insertions, 38 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 7d46130ec..33c625c9e 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -3,9 +3,9 @@ import * 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 } from '@server/initializers/constants' 5import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants'
6import { VideoResolution } from '../../shared/models/videos' 6import { AvailableEncoders, EncoderOptionsBuilder, EncoderProfile, VideoResolution } from '../../shared/models/videos'
7import { checkFFmpegEncoders } from '../initializers/checker-before-init'
8import { CONFIG } from '../initializers/config' 7import { CONFIG } from '../initializers/config'
8import { promisify0 } from './core-utils'
9import { computeFPS, getAudioStream, getVideoFileFPS } from './ffprobe-utils' 9import { computeFPS, getAudioStream, getVideoFileFPS } from './ffprobe-utils'
10import { processImage } from './image-utils' 10import { processImage } from './image-utils'
11import { logger } from './logger' 11import { logger } from './logger'
@@ -21,46 +21,45 @@ import { logger } from './logger'
21// Encoder options 21// Encoder options
22// --------------------------------------------------------------------------- 22// ---------------------------------------------------------------------------
23 23
24// Options builders 24type StreamType = 'audio' | 'video'
25
26export type EncoderOptionsBuilder = (params: {
27 input: string
28 resolution: VideoResolution
29 fps?: number
30 streamNum?: number
31}) => Promise<EncoderOptions> | EncoderOptions
32 25
33// Options types 26// ---------------------------------------------------------------------------
27// Encoders support
28// ---------------------------------------------------------------------------
34 29
35export interface EncoderOptions { 30// Detect supported encoders by ffmpeg
36 copy?: boolean 31let supportedEncoders: Map<string, boolean>
37 outputOptions: string[] 32async function checkFFmpegEncoders (peertubeAvailableEncoders: AvailableEncoders): Promise<Map<string, boolean>> {
38} 33 if (supportedEncoders !== undefined) {
34 return supportedEncoders
35 }
39 36
40// All our encoders 37 const getAvailableEncodersPromise = promisify0(ffmpeg.getAvailableEncoders)
38 const availableFFmpegEncoders = await getAvailableEncodersPromise()
41 39
42export interface EncoderProfile <T> { 40 const searchEncoders = new Set<string>()
43 [ profile: string ]: T 41 for (const type of [ 'live', 'vod' ]) {
42 for (const streamType of [ 'audio', 'video' ]) {
43 for (const encoder of peertubeAvailableEncoders.encodersToTry[type][streamType]) {
44 searchEncoders.add(encoder)
45 }
46 }
47 }
44 48
45 default: T 49 supportedEncoders = new Map<string, boolean>()
46}
47 50
48export type AvailableEncoders = { 51 for (const searchEncoder of searchEncoders) {
49 live: { 52 supportedEncoders.set(searchEncoder, availableFFmpegEncoders[searchEncoder] !== undefined)
50 [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
51 } 53 }
52 54
53 vod: { 55 logger.info('Built supported ffmpeg encoders.', { supportedEncoders, searchEncoders })
54 [ encoder: string ]: EncoderProfile<EncoderOptionsBuilder>
55 }
56 56
57 encodersToTry: { 57 return supportedEncoders
58 video: string[]
59 audio: string[]
60 }
61} 58}
62 59
63type StreamType = 'audio' | 'video' 60function resetSupportedEncoders () {
61 supportedEncoders = undefined
62}
64 63
65// --------------------------------------------------------------------------- 64// ---------------------------------------------------------------------------
66// Image manipulation 65// Image manipulation
@@ -275,7 +274,7 @@ async function getLiveTranscodingCommand (options: {
275 274
276 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) 275 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
277 276
278 logger.debug('Apply ffmpeg live video params from %s.', builderResult.encoder, builderResult) 277 logger.debug('Apply ffmpeg live video params from %s using %s profile.', builderResult.encoder, profile, builderResult)
279 278
280 command.outputOption(`${buildStreamSuffix('-c:v', i)} ${builderResult.encoder}`) 279 command.outputOption(`${buildStreamSuffix('-c:v', i)} ${builderResult.encoder}`)
281 command.addOutputOptions(builderResult.result.outputOptions) 280 command.addOutputOptions(builderResult.result.outputOptions)
@@ -292,7 +291,7 @@ async function getLiveTranscodingCommand (options: {
292 291
293 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) 292 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
294 293
295 logger.debug('Apply ffmpeg live audio params from %s.', builderResult.encoder, builderResult) 294 logger.debug('Apply ffmpeg live audio params from %s using %s profile.', builderResult.encoder, profile, builderResult)
296 295
297 command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`) 296 command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`)
298 command.addOutputOptions(builderResult.result.outputOptions) 297 command.addOutputOptions(builderResult.result.outputOptions)
@@ -513,11 +512,19 @@ async function getEncoderBuilderResult (options: {
513}) { 512}) {
514 const { availableEncoders, input, profile, resolution, streamType, fps, streamNum, videoType } = options 513 const { availableEncoders, input, profile, resolution, streamType, fps, streamNum, videoType } = options
515 514
516 const encodersToTry = availableEncoders.encodersToTry[streamType] 515 const encodersToTry = availableEncoders.encodersToTry[videoType][streamType]
517 const encoders = availableEncoders[videoType] 516 const encoders = availableEncoders.available[videoType]
518 517
519 for (const encoder of encodersToTry) { 518 for (const encoder of encodersToTry) {
520 if (!(await checkFFmpegEncoders()).get(encoder) || !encoders[encoder]) continue 519 if (!(await checkFFmpegEncoders(availableEncoders)).get(encoder)) {
520 logger.debug('Encoder %s not available in ffmpeg, skipping.', encoder)
521 continue
522 }
523
524 if (!encoders[encoder]) {
525 logger.debug('Encoder %s not available in peertube encoders, skipping.', encoder)
526 continue
527 }
521 528
522 // An object containing available profiles for this encoder 529 // An object containing available profiles for this encoder
523 const builderProfiles: EncoderProfile<EncoderOptionsBuilder> = encoders[encoder] 530 const builderProfiles: EncoderProfile<EncoderOptionsBuilder> = encoders[encoder]
@@ -567,7 +574,7 @@ async function presetVideo (
567 574
568 if (!parsedAudio.audioStream) { 575 if (!parsedAudio.audioStream) {
569 localCommand = localCommand.noAudio() 576 localCommand = localCommand.noAudio()
570 streamsToProcess = [ 'audio' ] 577 streamsToProcess = [ 'video' ]
571 } 578 }
572 579
573 for (const streamType of streamsToProcess) { 580 for (const streamType of streamsToProcess) {
@@ -587,7 +594,10 @@ async function presetVideo (
587 throw new Error('No available encoder found for stream ' + streamType) 594 throw new Error('No available encoder found for stream ' + streamType)
588 } 595 }
589 596
590 logger.debug('Apply ffmpeg params from %s.', builderResult.encoder, builderResult) 597 logger.debug(
598 'Apply ffmpeg params from %s for %s stream of input %s using %s profile.',
599 builderResult.encoder, streamType, input, profile, builderResult
600 )
591 601
592 if (streamType === 'video') { 602 if (streamType === 'video') {
593 localCommand.videoCodec(builderResult.encoder) 603 localCommand.videoCodec(builderResult.encoder)
@@ -679,6 +689,8 @@ export {
679 transcode, 689 transcode,
680 runCommand, 690 runCommand,
681 691
692 resetSupportedEncoders,
693
682 // builders 694 // builders
683 buildx264VODCommand 695 buildx264VODCommand
684} 696}