]>
Commit | Line | Data |
---|---|---|
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | |
2 | ||
3 | import 'mocha' | |
4 | import { expect } from 'chai' | |
5 | import { join } from 'path' | |
6 | import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' | |
7 | import { | |
8 | cleanupTests, | |
9 | flushAndRunServer, | |
10 | getVideo, | |
11 | PluginsCommand, | |
12 | ServerInfo, | |
13 | setAccessTokensToServers, | |
14 | setDefaultVideoChannel, | |
15 | testFfmpegStreamError, | |
16 | uploadVideoAndGetId, | |
17 | waitJobs | |
18 | } from '@shared/extra-utils' | |
19 | import { VideoDetails, VideoPrivacy } from '@shared/models' | |
20 | ||
21 | async function createLiveWrapper (server: ServerInfo) { | |
22 | const liveAttributes = { | |
23 | name: 'live video', | |
24 | channelId: server.videoChannel.id, | |
25 | privacy: VideoPrivacy.PUBLIC | |
26 | } | |
27 | ||
28 | const { uuid } = await server.liveCommand.create({ fields: liveAttributes }) | |
29 | ||
30 | return uuid | |
31 | } | |
32 | ||
33 | function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { | |
34 | return server.configCommand.updateCustomSubConfig({ | |
35 | newConfig: { | |
36 | transcoding: { | |
37 | enabled: true, | |
38 | profile: vodProfile, | |
39 | hls: { | |
40 | enabled: true | |
41 | }, | |
42 | webtorrent: { | |
43 | enabled: true | |
44 | }, | |
45 | resolutions: { | |
46 | '240p': true, | |
47 | '360p': false, | |
48 | '480p': false, | |
49 | '720p': true | |
50 | } | |
51 | }, | |
52 | live: { | |
53 | transcoding: { | |
54 | profile: liveProfile, | |
55 | enabled: true, | |
56 | resolutions: { | |
57 | '240p': true, | |
58 | '360p': false, | |
59 | '480p': false, | |
60 | '720p': true | |
61 | } | |
62 | } | |
63 | } | |
64 | } | |
65 | }) | |
66 | } | |
67 | ||
68 | describe('Test transcoding plugins', function () { | |
69 | let server: ServerInfo | |
70 | ||
71 | before(async function () { | |
72 | this.timeout(60000) | |
73 | ||
74 | server = await flushAndRunServer(1) | |
75 | await setAccessTokensToServers([ server ]) | |
76 | await setDefaultVideoChannel([ server ]) | |
77 | ||
78 | await updateConf(server, 'default', 'default') | |
79 | }) | |
80 | ||
81 | describe('When using a plugin adding profiles to existing encoders', function () { | |
82 | ||
83 | async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) { | |
84 | const res = await getVideo(server.url, uuid) | |
85 | const video = res.body as VideoDetails | |
86 | const files = video.files.concat(...video.streamingPlaylists.map(p => p.files)) | |
87 | ||
88 | for (const file of files) { | |
89 | if (type === 'above') { | |
90 | expect(file.fps).to.be.above(fps) | |
91 | } else { | |
92 | expect(file.fps).to.be.below(fps) | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | async function checkLiveFPS (uuid: string, type: 'above' | 'below', fps: number) { | |
98 | const playlistUrl = `${server.url}/static/streaming-playlists/hls/${uuid}/0.m3u8` | |
99 | const videoFPS = await getVideoFileFPS(playlistUrl) | |
100 | ||
101 | if (type === 'above') { | |
102 | expect(videoFPS).to.be.above(fps) | |
103 | } else { | |
104 | expect(videoFPS).to.be.below(fps) | |
105 | } | |
106 | } | |
107 | ||
108 | before(async function () { | |
109 | await server.pluginsCommand.install({ path: PluginsCommand.getPluginTestPath('-transcoding-one') }) | |
110 | }) | |
111 | ||
112 | it('Should have the appropriate available profiles', async function () { | |
113 | const config = await server.configCommand.getConfig() | |
114 | ||
115 | expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ]) | |
116 | expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live', 'input-options-live', 'bad-scale-live' ]) | |
117 | }) | |
118 | ||
119 | it('Should not use the plugin profile if not chosen by the admin', async function () { | |
120 | this.timeout(240000) | |
121 | ||
122 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid | |
123 | await waitJobs([ server ]) | |
124 | ||
125 | await checkVideoFPS(videoUUID, 'above', 20) | |
126 | }) | |
127 | ||
128 | it('Should use the vod profile', async function () { | |
129 | this.timeout(240000) | |
130 | ||
131 | await updateConf(server, 'low-vod', 'default') | |
132 | ||
133 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid | |
134 | await waitJobs([ server ]) | |
135 | ||
136 | await checkVideoFPS(videoUUID, 'below', 12) | |
137 | }) | |
138 | ||
139 | it('Should apply input options in vod profile', async function () { | |
140 | this.timeout(240000) | |
141 | ||
142 | await updateConf(server, 'input-options-vod', 'default') | |
143 | ||
144 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid | |
145 | await waitJobs([ server ]) | |
146 | ||
147 | await checkVideoFPS(videoUUID, 'below', 6) | |
148 | }) | |
149 | ||
150 | it('Should apply the scale filter in vod profile', async function () { | |
151 | this.timeout(240000) | |
152 | ||
153 | await updateConf(server, 'bad-scale-vod', 'default') | |
154 | ||
155 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid | |
156 | await waitJobs([ server ]) | |
157 | ||
158 | // Transcoding failed | |
159 | const res = await getVideo(server.url, videoUUID) | |
160 | const video: VideoDetails = res.body | |
161 | ||
162 | expect(video.files).to.have.lengthOf(1) | |
163 | expect(video.streamingPlaylists).to.have.lengthOf(0) | |
164 | }) | |
165 | ||
166 | it('Should not use the plugin profile if not chosen by the admin', async function () { | |
167 | this.timeout(240000) | |
168 | ||
169 | const liveVideoId = await createLiveWrapper(server) | |
170 | ||
171 | await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' }) | |
172 | await server.liveCommand.waitUntilPublished({ videoId: liveVideoId }) | |
173 | await waitJobs([ server ]) | |
174 | ||
175 | await checkLiveFPS(liveVideoId, 'above', 20) | |
176 | }) | |
177 | ||
178 | it('Should use the live profile', async function () { | |
179 | this.timeout(240000) | |
180 | ||
181 | await updateConf(server, 'low-vod', 'low-live') | |
182 | ||
183 | const liveVideoId = await createLiveWrapper(server) | |
184 | ||
185 | await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' }) | |
186 | await server.liveCommand.waitUntilPublished({ videoId: liveVideoId }) | |
187 | await waitJobs([ server ]) | |
188 | ||
189 | await checkLiveFPS(liveVideoId, 'below', 12) | |
190 | }) | |
191 | ||
192 | it('Should apply the input options on live profile', async function () { | |
193 | this.timeout(240000) | |
194 | ||
195 | await updateConf(server, 'low-vod', 'input-options-live') | |
196 | ||
197 | const liveVideoId = await createLiveWrapper(server) | |
198 | ||
199 | await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' }) | |
200 | await server.liveCommand.waitUntilPublished({ videoId: liveVideoId }) | |
201 | await waitJobs([ server ]) | |
202 | ||
203 | await checkLiveFPS(liveVideoId, 'below', 6) | |
204 | }) | |
205 | ||
206 | it('Should apply the scale filter name on live profile', async function () { | |
207 | this.timeout(240000) | |
208 | ||
209 | await updateConf(server, 'low-vod', 'bad-scale-live') | |
210 | ||
211 | const liveVideoId = await createLiveWrapper(server) | |
212 | ||
213 | const command = await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' }) | |
214 | await testFfmpegStreamError(command, true) | |
215 | }) | |
216 | ||
217 | it('Should default to the default profile if the specified profile does not exist', async function () { | |
218 | this.timeout(240000) | |
219 | ||
220 | await server.pluginsCommand.uninstall({ npmName: 'peertube-plugin-test-transcoding-one' }) | |
221 | ||
222 | const config = await server.configCommand.getConfig() | |
223 | ||
224 | expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ]) | |
225 | expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ]) | |
226 | ||
227 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid | |
228 | await waitJobs([ server ]) | |
229 | ||
230 | await checkVideoFPS(videoUUID, 'above', 20) | |
231 | }) | |
232 | ||
233 | }) | |
234 | ||
235 | describe('When using a plugin adding new encoders', function () { | |
236 | ||
237 | before(async function () { | |
238 | await server.pluginsCommand.install({ path: PluginsCommand.getPluginTestPath('-transcoding-two') }) | |
239 | ||
240 | await updateConf(server, 'test-vod-profile', 'test-live-profile') | |
241 | }) | |
242 | ||
243 | it('Should use the new vod encoders', async function () { | |
244 | this.timeout(240000) | |
245 | ||
246 | const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video', fixture: 'video_short_240p.mp4' })).uuid | |
247 | await waitJobs([ server ]) | |
248 | ||
249 | const path = server.serversCommand.buildDirectory(join('videos', videoUUID + '-240.mp4')) | |
250 | const audioProbe = await getAudioStream(path) | |
251 | expect(audioProbe.audioStream.codec_name).to.equal('opus') | |
252 | ||
253 | const videoProbe = await getVideoStreamFromFile(path) | |
254 | expect(videoProbe.codec_name).to.equal('vp9') | |
255 | }) | |
256 | ||
257 | it('Should use the new live encoders', async function () { | |
258 | this.timeout(240000) | |
259 | ||
260 | const liveVideoId = await createLiveWrapper(server) | |
261 | ||
262 | await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' }) | |
263 | await server.liveCommand.waitUntilPublished({ videoId: liveVideoId }) | |
264 | await waitJobs([ server ]) | |
265 | ||
266 | const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` | |
267 | const audioProbe = await getAudioStream(playlistUrl) | |
268 | expect(audioProbe.audioStream.codec_name).to.equal('opus') | |
269 | ||
270 | const videoProbe = await getVideoStreamFromFile(playlistUrl) | |
271 | expect(videoProbe.codec_name).to.equal('h264') | |
272 | }) | |
273 | }) | |
274 | ||
275 | after(async function () { | |
276 | await cleanupTests([ server ]) | |
277 | }) | |
278 | }) |