]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | import { logger } from '@server/helpers/logger' | |
3 | import { FFmpegCommandWrapper, getDefaultAvailableEncoders } from '@shared/ffmpeg' | |
4 | import { AvailableEncoders, EncoderOptionsBuilder } from '@shared/models' | |
5 | ||
6 | // --------------------------------------------------------------------------- | |
7 | // Profile manager to get and change default profiles | |
8 | // --------------------------------------------------------------------------- | |
9 | ||
10 | class VideoTranscodingProfilesManager { | |
11 | private static instance: VideoTranscodingProfilesManager | |
12 | ||
13 | // 1 === less priority | |
14 | private readonly encodersPriorities = { | |
15 | vod: this.buildDefaultEncodersPriorities(), | |
16 | live: this.buildDefaultEncodersPriorities() | |
17 | } | |
18 | ||
19 | private readonly availableEncoders = getDefaultAvailableEncoders() | |
20 | ||
21 | private availableProfiles = { | |
22 | vod: [] as string[], | |
23 | live: [] as string[] | |
24 | } | |
25 | ||
26 | private constructor () { | |
27 | this.buildAvailableProfiles() | |
28 | } | |
29 | ||
30 | getAvailableEncoders (): AvailableEncoders { | |
31 | return { | |
32 | available: this.availableEncoders, | |
33 | encodersToTry: { | |
34 | vod: { | |
35 | video: this.getEncodersByPriority('vod', 'video'), | |
36 | audio: this.getEncodersByPriority('vod', 'audio') | |
37 | }, | |
38 | live: { | |
39 | video: this.getEncodersByPriority('live', 'video'), | |
40 | audio: this.getEncodersByPriority('live', 'audio') | |
41 | } | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | getAvailableProfiles (type: 'vod' | 'live') { | |
47 | return this.availableProfiles[type] | |
48 | } | |
49 | ||
50 | addProfile (options: { | |
51 | type: 'vod' | 'live' | |
52 | encoder: string | |
53 | profile: string | |
54 | builder: EncoderOptionsBuilder | |
55 | }) { | |
56 | const { type, encoder, profile, builder } = options | |
57 | ||
58 | const encoders = this.availableEncoders[type] | |
59 | ||
60 | if (!encoders[encoder]) encoders[encoder] = {} | |
61 | encoders[encoder][profile] = builder | |
62 | ||
63 | this.buildAvailableProfiles() | |
64 | } | |
65 | ||
66 | removeProfile (options: { | |
67 | type: 'vod' | 'live' | |
68 | encoder: string | |
69 | profile: string | |
70 | }) { | |
71 | const { type, encoder, profile } = options | |
72 | ||
73 | delete this.availableEncoders[type][encoder][profile] | |
74 | this.buildAvailableProfiles() | |
75 | } | |
76 | ||
77 | addEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) { | |
78 | this.encodersPriorities[type][streamType].push({ name: encoder, priority }) | |
79 | ||
80 | FFmpegCommandWrapper.resetSupportedEncoders() | |
81 | } | |
82 | ||
83 | removeEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) { | |
84 | this.encodersPriorities[type][streamType] = this.encodersPriorities[type][streamType] | |
85 | .filter(o => o.name !== encoder && o.priority !== priority) | |
86 | ||
87 | FFmpegCommandWrapper.resetSupportedEncoders() | |
88 | } | |
89 | ||
90 | private getEncodersByPriority (type: 'vod' | 'live', streamType: 'audio' | 'video') { | |
91 | return this.encodersPriorities[type][streamType] | |
92 | .sort((e1, e2) => { | |
93 | if (e1.priority > e2.priority) return -1 | |
94 | else if (e1.priority === e2.priority) return 0 | |
95 | ||
96 | return 1 | |
97 | }) | |
98 | .map(e => e.name) | |
99 | } | |
100 | ||
101 | private buildAvailableProfiles () { | |
102 | for (const type of [ 'vod', 'live' ]) { | |
103 | const result = new Set() | |
104 | ||
105 | const encoders = this.availableEncoders[type] | |
106 | ||
107 | for (const encoderName of Object.keys(encoders)) { | |
108 | for (const profile of Object.keys(encoders[encoderName])) { | |
109 | result.add(profile) | |
110 | } | |
111 | } | |
112 | ||
113 | this.availableProfiles[type] = Array.from(result) | |
114 | } | |
115 | ||
116 | logger.debug('Available transcoding profiles built.', { availableProfiles: this.availableProfiles }) | |
117 | } | |
118 | ||
119 | private buildDefaultEncodersPriorities () { | |
120 | return { | |
121 | video: [ | |
122 | { name: 'libx264', priority: 100 } | |
123 | ], | |
124 | ||
125 | // Try the first one, if not available try the second one etc | |
126 | audio: [ | |
127 | // we favor VBR, if a good AAC encoder is available | |
128 | { name: 'libfdk_aac', priority: 200 }, | |
129 | { name: 'aac', priority: 100 } | |
130 | ] | |
131 | } | |
132 | } | |
133 | ||
134 | static get Instance () { | |
135 | return this.instance || (this.instance = new this()) | |
136 | } | |
137 | } | |
138 | ||
139 | // --------------------------------------------------------------------------- | |
140 | ||
141 | export { | |
142 | VideoTranscodingProfilesManager | |
143 | } |