diff options
author | Chocobozzz <me@florianbigard.com> | 2021-01-28 15:52:44 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-01-28 15:55:39 +0100 |
commit | 1896bca09e088b0da9d5e845407ecebae330618c (patch) | |
tree | 56041c445c0cd49aca536d0fd6b586730f4d341e /server/lib | |
parent | 529b37527cff5203a0689a15ce73dcee6e1eece2 (diff) | |
download | PeerTube-1896bca09e088b0da9d5e845407ecebae330618c.tar.gz PeerTube-1896bca09e088b0da9d5e845407ecebae330618c.tar.zst PeerTube-1896bca09e088b0da9d5e845407ecebae330618c.zip |
Support transcoding options/encoders by plugins
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/live-manager.ts | 2 | ||||
-rw-r--r-- | server/lib/plugins/plugin-helpers-builder.ts (renamed from server/lib/plugins/plugin-helpers.ts) | 0 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 35 | ||||
-rw-r--r-- | server/lib/plugins/register-helpers.ts (renamed from server/lib/plugins/register-helpers-store.ts) | 91 | ||||
-rw-r--r-- | server/lib/video-transcoding-profiles.ts | 119 | ||||
-rw-r--r-- | server/lib/video-transcoding.ts | 10 |
6 files changed, 211 insertions, 46 deletions
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts index c8e5bcb77..9f17b8820 100644 --- a/server/lib/live-manager.ts +++ b/server/lib/live-manager.ts | |||
@@ -338,7 +338,7 @@ class LiveManager { | |||
338 | resolutions: allResolutions, | 338 | resolutions: allResolutions, |
339 | fps, | 339 | fps, |
340 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 340 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
341 | profile: 'default' | 341 | profile: CONFIG.LIVE.TRANSCODING.PROFILE |
342 | }) | 342 | }) |
343 | : getLiveMuxingCommand(rtmpUrl, outPath) | 343 | : getLiveMuxingCommand(rtmpUrl, outPath) |
344 | 344 | ||
diff --git a/server/lib/plugins/plugin-helpers.ts b/server/lib/plugins/plugin-helpers-builder.ts index 39773f693..39773f693 100644 --- a/server/lib/plugins/plugin-helpers.ts +++ b/server/lib/plugins/plugin-helpers-builder.ts | |||
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index 8e7491257..c19b40135 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -20,7 +20,7 @@ import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants' | |||
20 | import { PluginModel } from '../../models/server/plugin' | 20 | import { PluginModel } from '../../models/server/plugin' |
21 | import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPassOptions, RegisterServerOptions } from '../../types/plugins' | 21 | import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPassOptions, RegisterServerOptions } from '../../types/plugins' |
22 | import { ClientHtml } from '../client-html' | 22 | import { ClientHtml } from '../client-html' |
23 | import { RegisterHelpersStore } from './register-helpers-store' | 23 | import { RegisterHelpers } from './register-helpers' |
24 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' | 24 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' |
25 | 25 | ||
26 | export interface RegisteredPlugin { | 26 | export interface RegisteredPlugin { |
@@ -40,7 +40,7 @@ export interface RegisteredPlugin { | |||
40 | css: string[] | 40 | css: string[] |
41 | 41 | ||
42 | // Only if this is a plugin | 42 | // Only if this is a plugin |
43 | registerHelpersStore?: RegisterHelpersStore | 43 | registerHelpers?: RegisterHelpers |
44 | unregister?: Function | 44 | unregister?: Function |
45 | } | 45 | } |
46 | 46 | ||
@@ -109,7 +109,7 @@ export class PluginManager implements ServerHook { | |||
109 | npmName: p.npmName, | 109 | npmName: p.npmName, |
110 | name: p.name, | 110 | name: p.name, |
111 | version: p.version, | 111 | version: p.version, |
112 | idAndPassAuths: p.registerHelpersStore.getIdAndPassAuths() | 112 | idAndPassAuths: p.registerHelpers.getIdAndPassAuths() |
113 | })) | 113 | })) |
114 | .filter(v => v.idAndPassAuths.length !== 0) | 114 | .filter(v => v.idAndPassAuths.length !== 0) |
115 | } | 115 | } |
@@ -120,7 +120,7 @@ export class PluginManager implements ServerHook { | |||
120 | npmName: p.npmName, | 120 | npmName: p.npmName, |
121 | name: p.name, | 121 | name: p.name, |
122 | version: p.version, | 122 | version: p.version, |
123 | externalAuths: p.registerHelpersStore.getExternalAuths() | 123 | externalAuths: p.registerHelpers.getExternalAuths() |
124 | })) | 124 | })) |
125 | .filter(v => v.externalAuths.length !== 0) | 125 | .filter(v => v.externalAuths.length !== 0) |
126 | } | 126 | } |
@@ -129,14 +129,14 @@ export class PluginManager implements ServerHook { | |||
129 | const result = this.getRegisteredPluginOrTheme(npmName) | 129 | const result = this.getRegisteredPluginOrTheme(npmName) |
130 | if (!result || result.type !== PluginType.PLUGIN) return [] | 130 | if (!result || result.type !== PluginType.PLUGIN) return [] |
131 | 131 | ||
132 | return result.registerHelpersStore.getSettings() | 132 | return result.registerHelpers.getSettings() |
133 | } | 133 | } |
134 | 134 | ||
135 | getRouter (npmName: string) { | 135 | getRouter (npmName: string) { |
136 | const result = this.getRegisteredPluginOrTheme(npmName) | 136 | const result = this.getRegisteredPluginOrTheme(npmName) |
137 | if (!result || result.type !== PluginType.PLUGIN) return null | 137 | if (!result || result.type !== PluginType.PLUGIN) return null |
138 | 138 | ||
139 | return result.registerHelpersStore.getRouter() | 139 | return result.registerHelpers.getRouter() |
140 | } | 140 | } |
141 | 141 | ||
142 | getTranslations (locale: string) { | 142 | getTranslations (locale: string) { |
@@ -194,7 +194,7 @@ export class PluginManager implements ServerHook { | |||
194 | logger.error('Cannot find plugin %s to call on settings changed.', name) | 194 | logger.error('Cannot find plugin %s to call on settings changed.', name) |
195 | } | 195 | } |
196 | 196 | ||
197 | for (const cb of registered.registerHelpersStore.getOnSettingsChangedCallbacks()) { | 197 | for (const cb of registered.registerHelpers.getOnSettingsChangedCallbacks()) { |
198 | try { | 198 | try { |
199 | cb(settings) | 199 | cb(settings) |
200 | } catch (err) { | 200 | } catch (err) { |
@@ -268,8 +268,9 @@ export class PluginManager implements ServerHook { | |||
268 | this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName) | 268 | this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName) |
269 | } | 269 | } |
270 | 270 | ||
271 | const store = plugin.registerHelpersStore | 271 | const store = plugin.registerHelpers |
272 | store.reinitVideoConstants(plugin.npmName) | 272 | store.reinitVideoConstants(plugin.npmName) |
273 | store.reinitTranscodingProfilesAndEncoders(plugin.npmName) | ||
273 | 274 | ||
274 | logger.info('Regenerating registered plugin CSS to global file.') | 275 | logger.info('Regenerating registered plugin CSS to global file.') |
275 | await this.regeneratePluginGlobalCSS() | 276 | await this.regeneratePluginGlobalCSS() |
@@ -375,11 +376,11 @@ export class PluginManager implements ServerHook { | |||
375 | this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, plugin.type) | 376 | this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, plugin.type) |
376 | 377 | ||
377 | let library: PluginLibrary | 378 | let library: PluginLibrary |
378 | let registerHelpersStore: RegisterHelpersStore | 379 | let registerHelpers: RegisterHelpers |
379 | if (plugin.type === PluginType.PLUGIN) { | 380 | if (plugin.type === PluginType.PLUGIN) { |
380 | const result = await this.registerPlugin(plugin, pluginPath, packageJSON) | 381 | const result = await this.registerPlugin(plugin, pluginPath, packageJSON) |
381 | library = result.library | 382 | library = result.library |
382 | registerHelpersStore = result.registerStore | 383 | registerHelpers = result.registerStore |
383 | } | 384 | } |
384 | 385 | ||
385 | const clientScripts: { [id: string]: ClientScript } = {} | 386 | const clientScripts: { [id: string]: ClientScript } = {} |
@@ -398,7 +399,7 @@ export class PluginManager implements ServerHook { | |||
398 | staticDirs: packageJSON.staticDirs, | 399 | staticDirs: packageJSON.staticDirs, |
399 | clientScripts, | 400 | clientScripts, |
400 | css: packageJSON.css, | 401 | css: packageJSON.css, |
401 | registerHelpersStore: registerHelpersStore || undefined, | 402 | registerHelpers: registerHelpers || undefined, |
402 | unregister: library ? library.unregister : undefined | 403 | unregister: library ? library.unregister : undefined |
403 | } | 404 | } |
404 | 405 | ||
@@ -512,8 +513,8 @@ export class PluginManager implements ServerHook { | |||
512 | const plugin = this.getRegisteredPluginOrTheme(npmName) | 513 | const plugin = this.getRegisteredPluginOrTheme(npmName) |
513 | if (!plugin || plugin.type !== PluginType.PLUGIN) return null | 514 | if (!plugin || plugin.type !== PluginType.PLUGIN) return null |
514 | 515 | ||
515 | let auths: (RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions)[] = plugin.registerHelpersStore.getIdAndPassAuths() | 516 | let auths: (RegisterServerAuthPassOptions | RegisterServerAuthExternalOptions)[] = plugin.registerHelpers.getIdAndPassAuths() |
516 | auths = auths.concat(plugin.registerHelpersStore.getExternalAuths()) | 517 | auths = auths.concat(plugin.registerHelpers.getExternalAuths()) |
517 | 518 | ||
518 | return auths.find(a => a.authName === authName) | 519 | return auths.find(a => a.authName === authName) |
519 | } | 520 | } |
@@ -538,7 +539,7 @@ export class PluginManager implements ServerHook { | |||
538 | private getRegisterHelpers ( | 539 | private getRegisterHelpers ( |
539 | npmName: string, | 540 | npmName: string, |
540 | plugin: PluginModel | 541 | plugin: PluginModel |
541 | ): { registerStore: RegisterHelpersStore, registerOptions: RegisterServerOptions } { | 542 | ): { registerStore: RegisterHelpers, registerOptions: RegisterServerOptions } { |
542 | const onHookAdded = (options: RegisterServerHookOptions) => { | 543 | const onHookAdded = (options: RegisterServerHookOptions) => { |
543 | if (!this.hooks[options.target]) this.hooks[options.target] = [] | 544 | if (!this.hooks[options.target]) this.hooks[options.target] = [] |
544 | 545 | ||
@@ -550,11 +551,11 @@ export class PluginManager implements ServerHook { | |||
550 | }) | 551 | }) |
551 | } | 552 | } |
552 | 553 | ||
553 | const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this)) | 554 | const registerHelpers = new RegisterHelpers(npmName, plugin, onHookAdded.bind(this)) |
554 | 555 | ||
555 | return { | 556 | return { |
556 | registerStore: registerHelpersStore, | 557 | registerStore: registerHelpers, |
557 | registerOptions: registerHelpersStore.buildRegisterHelpers() | 558 | registerOptions: registerHelpers.buildRegisterHelpers() |
558 | } | 559 | } |
559 | } | 560 | } |
560 | 561 | ||
diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers.ts index c73079302..3a38a4835 100644 --- a/server/lib/plugins/register-helpers-store.ts +++ b/server/lib/plugins/register-helpers.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | RegisterServerOptions | 17 | RegisterServerOptions |
18 | } from '@server/types/plugins' | 18 | } from '@server/types/plugins' |
19 | import { | 19 | import { |
20 | EncoderOptionsBuilder, | ||
20 | PluginPlaylistPrivacyManager, | 21 | PluginPlaylistPrivacyManager, |
21 | PluginSettingsManager, | 22 | PluginSettingsManager, |
22 | PluginStorageManager, | 23 | PluginStorageManager, |
@@ -28,7 +29,8 @@ import { | |||
28 | RegisterServerSettingOptions | 29 | RegisterServerSettingOptions |
29 | } from '@shared/models' | 30 | } from '@shared/models' |
30 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' | 31 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' |
31 | import { buildPluginHelpers } from './plugin-helpers' | 32 | import { VideoTranscodingProfilesManager } from '../video-transcoding-profiles' |
33 | import { buildPluginHelpers } from './plugin-helpers-builder' | ||
32 | 34 | ||
33 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' | 35 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' |
34 | type VideoConstant = { [key in number | string]: string } | 36 | type VideoConstant = { [key in number | string]: string } |
@@ -40,7 +42,7 @@ type UpdatedVideoConstant = { | |||
40 | } | 42 | } |
41 | } | 43 | } |
42 | 44 | ||
43 | export class RegisterHelpersStore { | 45 | export class RegisterHelpers { |
44 | private readonly updatedVideoConstants: UpdatedVideoConstant = { | 46 | private readonly updatedVideoConstants: UpdatedVideoConstant = { |
45 | playlistPrivacy: { added: [], deleted: [] }, | 47 | playlistPrivacy: { added: [], deleted: [] }, |
46 | privacy: { added: [], deleted: [] }, | 48 | privacy: { added: [], deleted: [] }, |
@@ -49,6 +51,23 @@ export class RegisterHelpersStore { | |||
49 | category: { added: [], deleted: [] } | 51 | category: { added: [], deleted: [] } |
50 | } | 52 | } |
51 | 53 | ||
54 | private readonly transcodingProfiles: { | ||
55 | [ npmName: string ]: { | ||
56 | type: 'vod' | 'live' | ||
57 | encoder: string | ||
58 | profile: string | ||
59 | }[] | ||
60 | } = {} | ||
61 | |||
62 | private readonly transcodingEncoders: { | ||
63 | [ npmName: string ]: { | ||
64 | type: 'vod' | 'live' | ||
65 | streamType: 'audio' | 'video' | ||
66 | encoder: string | ||
67 | priority: number | ||
68 | }[] | ||
69 | } = {} | ||
70 | |||
52 | private readonly settings: RegisterServerSettingOptions[] = [] | 71 | private readonly settings: RegisterServerSettingOptions[] = [] |
53 | 72 | ||
54 | private idAndPassAuths: RegisterServerAuthPassOptions[] = [] | 73 | private idAndPassAuths: RegisterServerAuthPassOptions[] = [] |
@@ -83,6 +102,8 @@ export class RegisterHelpersStore { | |||
83 | const videoPrivacyManager = this.buildVideoPrivacyManager() | 102 | const videoPrivacyManager = this.buildVideoPrivacyManager() |
84 | const playlistPrivacyManager = this.buildPlaylistPrivacyManager() | 103 | const playlistPrivacyManager = this.buildPlaylistPrivacyManager() |
85 | 104 | ||
105 | const transcodingManager = this.buildTranscodingManager() | ||
106 | |||
86 | const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth() | 107 | const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth() |
87 | const registerExternalAuth = this.buildRegisterExternalAuth() | 108 | const registerExternalAuth = this.buildRegisterExternalAuth() |
88 | const unregisterIdAndPassAuth = this.buildUnregisterIdAndPassAuth() | 109 | const unregisterIdAndPassAuth = this.buildUnregisterIdAndPassAuth() |
@@ -106,6 +127,8 @@ export class RegisterHelpersStore { | |||
106 | videoPrivacyManager, | 127 | videoPrivacyManager, |
107 | playlistPrivacyManager, | 128 | playlistPrivacyManager, |
108 | 129 | ||
130 | transcodingManager, | ||
131 | |||
109 | registerIdAndPassAuth, | 132 | registerIdAndPassAuth, |
110 | registerExternalAuth, | 133 | registerExternalAuth, |
111 | unregisterIdAndPassAuth, | 134 | unregisterIdAndPassAuth, |
@@ -141,6 +164,22 @@ export class RegisterHelpersStore { | |||
141 | } | 164 | } |
142 | } | 165 | } |
143 | 166 | ||
167 | reinitTranscodingProfilesAndEncoders (npmName: string) { | ||
168 | const profiles = this.transcodingProfiles[npmName] | ||
169 | if (Array.isArray(profiles)) { | ||
170 | for (const profile of profiles) { | ||
171 | VideoTranscodingProfilesManager.Instance.removeProfile(profile) | ||
172 | } | ||
173 | } | ||
174 | |||
175 | const encoders = this.transcodingEncoders[npmName] | ||
176 | if (Array.isArray(encoders)) { | ||
177 | for (const o of encoders) { | ||
178 | VideoTranscodingProfilesManager.Instance.removeEncoderPriority(o.type, o.streamType, o.encoder, o.priority) | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
144 | getSettings () { | 183 | getSettings () { |
145 | return this.settings | 184 | return this.settings |
146 | } | 185 | } |
@@ -354,4 +393,52 @@ export class RegisterHelpersStore { | |||
354 | 393 | ||
355 | return true | 394 | return true |
356 | } | 395 | } |
396 | |||
397 | private buildTranscodingManager () { | ||
398 | const self = this | ||
399 | |||
400 | function addProfile (type: 'live' | 'vod', encoder: string, profile: string, builder: EncoderOptionsBuilder) { | ||
401 | if (profile === 'default') { | ||
402 | logger.error('A plugin cannot add a default live transcoding profile') | ||
403 | return false | ||
404 | } | ||
405 | |||
406 | VideoTranscodingProfilesManager.Instance.addProfile({ | ||
407 | type, | ||
408 | encoder, | ||
409 | profile, | ||
410 | builder | ||
411 | }) | ||
412 | |||
413 | if (!self.transcodingProfiles[self.npmName]) self.transcodingProfiles[self.npmName] = [] | ||
414 | self.transcodingProfiles[self.npmName].push({ type, encoder, profile }) | ||
415 | |||
416 | return true | ||
417 | } | ||
418 | |||
419 | function addEncoderPriority (type: 'live' | 'vod', streamType: 'audio' | 'video', encoder: string, priority: number) { | ||
420 | VideoTranscodingProfilesManager.Instance.addEncoderPriority(type, streamType, encoder, priority) | ||
421 | |||
422 | if (!self.transcodingEncoders[self.npmName]) self.transcodingEncoders[self.npmName] = [] | ||
423 | self.transcodingEncoders[self.npmName].push({ type, streamType, encoder, priority }) | ||
424 | } | ||
425 | |||
426 | return { | ||
427 | addLiveProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder) { | ||
428 | return addProfile('live', encoder, profile, builder) | ||
429 | }, | ||
430 | |||
431 | addVODProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder) { | ||
432 | return addProfile('vod', encoder, profile, builder) | ||
433 | }, | ||
434 | |||
435 | addLiveEncoderPriority (streamType: 'audio' | 'video', encoder: string, priority: number) { | ||
436 | return addEncoderPriority('live', streamType, encoder, priority) | ||
437 | }, | ||
438 | |||
439 | addVODEncoderPriority (streamType: 'audio' | 'video', encoder: string, priority: number) { | ||
440 | return addEncoderPriority('vod', streamType, encoder, priority) | ||
441 | } | ||
442 | } | ||
443 | } | ||
357 | } | 444 | } |
diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/video-transcoding-profiles.ts index bbe556e75..76d38b6ca 100644 --- a/server/lib/video-transcoding-profiles.ts +++ b/server/lib/video-transcoding-profiles.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { logger } from '@server/helpers/logger' | 1 | import { logger } from '@server/helpers/logger' |
2 | import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' | 2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../shared/models/videos' |
3 | import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' | 3 | import { buildStreamSuffix, resetSupportedEncoders } from '../helpers/ffmpeg-utils' |
4 | import { | 4 | import { |
5 | canDoQuickAudioTranscode, | 5 | canDoQuickAudioTranscode, |
6 | ffprobePromise, | 6 | ffprobePromise, |
@@ -84,16 +84,8 @@ class VideoTranscodingProfilesManager { | |||
84 | 84 | ||
85 | // 1 === less priority | 85 | // 1 === less priority |
86 | private readonly encodersPriorities = { | 86 | private readonly encodersPriorities = { |
87 | video: [ | 87 | vod: this.buildDefaultEncodersPriorities(), |
88 | { name: 'libx264', priority: 100 } | 88 | live: this.buildDefaultEncodersPriorities() |
89 | ], | ||
90 | |||
91 | // Try the first one, if not available try the second one etc | ||
92 | audio: [ | ||
93 | // we favor VBR, if a good AAC encoder is available | ||
94 | { name: 'libfdk_aac', priority: 200 }, | ||
95 | { name: 'aac', priority: 100 } | ||
96 | ] | ||
97 | } | 89 | } |
98 | 90 | ||
99 | private readonly availableEncoders = { | 91 | private readonly availableEncoders = { |
@@ -118,25 +110,77 @@ class VideoTranscodingProfilesManager { | |||
118 | } | 110 | } |
119 | } | 111 | } |
120 | 112 | ||
121 | private constructor () { | 113 | private availableProfiles = { |
114 | vod: [] as string[], | ||
115 | live: [] as string[] | ||
116 | } | ||
122 | 117 | ||
118 | private constructor () { | ||
119 | this.buildAvailableProfiles() | ||
123 | } | 120 | } |
124 | 121 | ||
125 | getAvailableEncoders (): AvailableEncoders { | 122 | getAvailableEncoders (): AvailableEncoders { |
126 | const encodersToTry = { | 123 | return { |
127 | video: this.getEncodersByPriority('video'), | 124 | available: this.availableEncoders, |
128 | audio: this.getEncodersByPriority('audio') | 125 | encodersToTry: { |
126 | vod: { | ||
127 | video: this.getEncodersByPriority('vod', 'video'), | ||
128 | audio: this.getEncodersByPriority('vod', 'audio') | ||
129 | }, | ||
130 | live: { | ||
131 | video: this.getEncodersByPriority('live', 'video'), | ||
132 | audio: this.getEncodersByPriority('live', 'audio') | ||
133 | } | ||
134 | } | ||
129 | } | 135 | } |
130 | |||
131 | return Object.assign({}, this.availableEncoders, { encodersToTry }) | ||
132 | } | 136 | } |
133 | 137 | ||
134 | getAvailableProfiles (type: 'vod' | 'live') { | 138 | getAvailableProfiles (type: 'vod' | 'live') { |
135 | return this.availableEncoders[type] | 139 | return this.availableProfiles[type] |
140 | } | ||
141 | |||
142 | addProfile (options: { | ||
143 | type: 'vod' | 'live' | ||
144 | encoder: string | ||
145 | profile: string | ||
146 | builder: EncoderOptionsBuilder | ||
147 | }) { | ||
148 | const { type, encoder, profile, builder } = options | ||
149 | |||
150 | const encoders = this.availableEncoders[type] | ||
151 | |||
152 | if (!encoders[encoder]) encoders[encoder] = {} | ||
153 | encoders[encoder][profile] = builder | ||
154 | |||
155 | this.buildAvailableProfiles() | ||
156 | } | ||
157 | |||
158 | removeProfile (options: { | ||
159 | type: 'vod' | 'live' | ||
160 | encoder: string | ||
161 | profile: string | ||
162 | }) { | ||
163 | const { type, encoder, profile } = options | ||
164 | |||
165 | delete this.availableEncoders[type][encoder][profile] | ||
166 | this.buildAvailableProfiles() | ||
136 | } | 167 | } |
137 | 168 | ||
138 | private getEncodersByPriority (type: 'video' | 'audio') { | 169 | addEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) { |
139 | return this.encodersPriorities[type] | 170 | this.encodersPriorities[type][streamType].push({ name: encoder, priority }) |
171 | |||
172 | resetSupportedEncoders() | ||
173 | } | ||
174 | |||
175 | removeEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) { | ||
176 | this.encodersPriorities[type][streamType] = this.encodersPriorities[type][streamType] | ||
177 | .filter(o => o.name !== encoder && o.priority !== priority) | ||
178 | |||
179 | resetSupportedEncoders() | ||
180 | } | ||
181 | |||
182 | private getEncodersByPriority (type: 'vod' | 'live', streamType: 'audio' | 'video') { | ||
183 | return this.encodersPriorities[type][streamType] | ||
140 | .sort((e1, e2) => { | 184 | .sort((e1, e2) => { |
141 | if (e1.priority > e2.priority) return -1 | 185 | if (e1.priority > e2.priority) return -1 |
142 | else if (e1.priority === e2.priority) return 0 | 186 | else if (e1.priority === e2.priority) return 0 |
@@ -146,6 +190,39 @@ class VideoTranscodingProfilesManager { | |||
146 | .map(e => e.name) | 190 | .map(e => e.name) |
147 | } | 191 | } |
148 | 192 | ||
193 | private buildAvailableProfiles () { | ||
194 | for (const type of [ 'vod', 'live' ]) { | ||
195 | const result = new Set() | ||
196 | |||
197 | const encoders = this.availableEncoders[type] | ||
198 | |||
199 | for (const encoderName of Object.keys(encoders)) { | ||
200 | for (const profile of Object.keys(encoders[encoderName])) { | ||
201 | result.add(profile) | ||
202 | } | ||
203 | } | ||
204 | |||
205 | this.availableProfiles[type] = Array.from(result) | ||
206 | } | ||
207 | |||
208 | logger.debug('Available transcoding profiles built.', { availableProfiles: this.availableProfiles }) | ||
209 | } | ||
210 | |||
211 | private buildDefaultEncodersPriorities () { | ||
212 | return { | ||
213 | video: [ | ||
214 | { name: 'libx264', priority: 100 } | ||
215 | ], | ||
216 | |||
217 | // Try the first one, if not available try the second one etc | ||
218 | audio: [ | ||
219 | // we favor VBR, if a good AAC encoder is available | ||
220 | { name: 'libfdk_aac', priority: 200 }, | ||
221 | { name: 'aac', priority: 100 } | ||
222 | ] | ||
223 | } | ||
224 | } | ||
225 | |||
149 | static get Instance () { | 226 | static get Instance () { |
150 | return this.instance || (this.instance = new this()) | 227 | return this.instance || (this.instance = new this()) |
151 | } | 228 | } |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index c4b3425d1..37a4f3019 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -42,7 +42,7 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFile: | |||
42 | outputPath: videoTranscodedPath, | 42 | outputPath: videoTranscodedPath, |
43 | 43 | ||
44 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 44 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
45 | profile: 'default', | 45 | profile: CONFIG.TRANSCODING.PROFILE, |
46 | 46 | ||
47 | resolution: inputVideoFile.resolution, | 47 | resolution: inputVideoFile.resolution, |
48 | 48 | ||
@@ -96,7 +96,7 @@ async function transcodeNewWebTorrentResolution (video: MVideoWithFile, resoluti | |||
96 | outputPath: videoTranscodedPath, | 96 | outputPath: videoTranscodedPath, |
97 | 97 | ||
98 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 98 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
99 | profile: 'default', | 99 | profile: CONFIG.TRANSCODING.PROFILE, |
100 | 100 | ||
101 | resolution, | 101 | resolution, |
102 | 102 | ||
@@ -108,7 +108,7 @@ async function transcodeNewWebTorrentResolution (video: MVideoWithFile, resoluti | |||
108 | outputPath: videoTranscodedPath, | 108 | outputPath: videoTranscodedPath, |
109 | 109 | ||
110 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 110 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
111 | profile: 'default', | 111 | profile: CONFIG.TRANSCODING.PROFILE, |
112 | 112 | ||
113 | resolution, | 113 | resolution, |
114 | isPortraitMode: isPortrait, | 114 | isPortraitMode: isPortrait, |
@@ -143,7 +143,7 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video | |||
143 | outputPath: videoTranscodedPath, | 143 | outputPath: videoTranscodedPath, |
144 | 144 | ||
145 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 145 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
146 | profile: 'default', | 146 | profile: CONFIG.TRANSCODING.PROFILE, |
147 | 147 | ||
148 | audioPath: audioInputPath, | 148 | audioPath: audioInputPath, |
149 | resolution, | 149 | resolution, |
@@ -284,7 +284,7 @@ async function generateHlsPlaylistCommon (options: { | |||
284 | outputPath, | 284 | outputPath, |
285 | 285 | ||
286 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 286 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
287 | profile: 'default', | 287 | profile: CONFIG.TRANSCODING.PROFILE, |
288 | 288 | ||
289 | resolution, | 289 | resolution, |
290 | copyCodecs, | 290 | copyCodecs, |