diff options
Diffstat (limited to 'server/tests/api/videos/video-transcoder.ts')
-rw-r--r-- | server/tests/api/videos/video-transcoder.ts | 155 |
1 files changed, 132 insertions, 23 deletions
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 4be74901a..13b3530b1 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts | |||
@@ -1,29 +1,40 @@ | |||
1 | /* tslint:disable:no-unused-expression */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { omit } from 'lodash' | 5 | import { omit } from 'lodash' |
6 | import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' | 6 | import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' |
7 | import { audio, canDoQuickTranscode, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' | 7 | import { |
8 | audio, | ||
9 | canDoQuickTranscode, | ||
10 | getVideoFileBitrate, | ||
11 | getVideoFileFPS, | ||
12 | getVideoFileResolution, | ||
13 | getMetadataFromFile | ||
14 | } from '../../../helpers/ffmpeg-utils' | ||
8 | import { | 15 | import { |
9 | buildAbsoluteFixturePath, | 16 | buildAbsoluteFixturePath, |
10 | cleanupTests, | 17 | cleanupTests, |
11 | doubleFollow, | 18 | doubleFollow, |
12 | flushAndRunMultipleServers, | 19 | flushAndRunMultipleServers, |
13 | generateHighBitrateVideo, | 20 | generateHighBitrateVideo, |
21 | generateVideoWithFramerate, | ||
14 | getMyVideos, | 22 | getMyVideos, |
15 | getVideo, | 23 | getVideo, |
24 | getVideoFileMetadataUrl, | ||
16 | getVideosList, | 25 | getVideosList, |
17 | makeGetRequest, | 26 | makeGetRequest, |
18 | root, | 27 | root, |
19 | ServerInfo, | 28 | ServerInfo, |
20 | setAccessTokensToServers, | 29 | setAccessTokensToServers, |
21 | uploadVideo, | 30 | uploadVideo, uploadVideoAndGetId, |
22 | waitJobs, | 31 | waitJobs, |
23 | webtorrentAdd | 32 | webtorrentAdd |
24 | } from '../../../../shared/extra-utils' | 33 | } from '../../../../shared/extra-utils' |
25 | import { join } from 'path' | 34 | import { join } from 'path' |
26 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' | 35 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' |
36 | import { FfprobeData } from 'fluent-ffmpeg' | ||
37 | import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' | ||
27 | 38 | ||
28 | const expect = chai.expect | 39 | const expect = chai.expect |
29 | 40 | ||
@@ -55,19 +66,19 @@ describe('Test video transcoding', function () { | |||
55 | 66 | ||
56 | for (const server of servers) { | 67 | for (const server of servers) { |
57 | const res = await getVideosList(server.url) | 68 | const res = await getVideosList(server.url) |
58 | const video = res.body.data[ 0 ] | 69 | const video = res.body.data[0] |
59 | 70 | ||
60 | const res2 = await getVideo(server.url, video.id) | 71 | const res2 = await getVideo(server.url, video.id) |
61 | const videoDetails = res2.body | 72 | const videoDetails = res2.body |
62 | expect(videoDetails.files).to.have.lengthOf(1) | 73 | expect(videoDetails.files).to.have.lengthOf(1) |
63 | 74 | ||
64 | const magnetUri = videoDetails.files[ 0 ].magnetUri | 75 | const magnetUri = videoDetails.files[0].magnetUri |
65 | expect(magnetUri).to.match(/\.webm/) | 76 | expect(magnetUri).to.match(/\.webm/) |
66 | 77 | ||
67 | const torrent = await webtorrentAdd(magnetUri, true) | 78 | const torrent = await webtorrentAdd(magnetUri, true) |
68 | expect(torrent.files).to.be.an('array') | 79 | expect(torrent.files).to.be.an('array') |
69 | expect(torrent.files.length).to.equal(1) | 80 | expect(torrent.files.length).to.equal(1) |
70 | expect(torrent.files[ 0 ].path).match(/\.webm$/) | 81 | expect(torrent.files[0].path).match(/\.webm$/) |
71 | } | 82 | } |
72 | }) | 83 | }) |
73 | 84 | ||
@@ -92,13 +103,13 @@ describe('Test video transcoding', function () { | |||
92 | 103 | ||
93 | expect(videoDetails.files).to.have.lengthOf(4) | 104 | expect(videoDetails.files).to.have.lengthOf(4) |
94 | 105 | ||
95 | const magnetUri = videoDetails.files[ 0 ].magnetUri | 106 | const magnetUri = videoDetails.files[0].magnetUri |
96 | expect(magnetUri).to.match(/\.mp4/) | 107 | expect(magnetUri).to.match(/\.mp4/) |
97 | 108 | ||
98 | const torrent = await webtorrentAdd(magnetUri, true) | 109 | const torrent = await webtorrentAdd(magnetUri, true) |
99 | expect(torrent.files).to.be.an('array') | 110 | expect(torrent.files).to.be.an('array') |
100 | expect(torrent.files.length).to.equal(1) | 111 | expect(torrent.files.length).to.equal(1) |
101 | expect(torrent.files[ 0 ].path).match(/\.mp4$/) | 112 | expect(torrent.files[0].path).match(/\.mp4$/) |
102 | } | 113 | } |
103 | }) | 114 | }) |
104 | 115 | ||
@@ -126,8 +137,8 @@ describe('Test video transcoding', function () { | |||
126 | const probe = await audio.get(path) | 137 | const probe = await audio.get(path) |
127 | 138 | ||
128 | if (probe.audioStream) { | 139 | if (probe.audioStream) { |
129 | expect(probe.audioStream[ 'codec_name' ]).to.be.equal('aac') | 140 | expect(probe.audioStream['codec_name']).to.be.equal('aac') |
130 | expect(probe.audioStream[ 'bit_rate' ]).to.be.at.most(384 * 8000) | 141 | expect(probe.audioStream['bit_rate']).to.be.at.most(384 * 8000) |
131 | } else { | 142 | } else { |
132 | this.fail('Could not retrieve the audio stream on ' + probe.absolutePath) | 143 | this.fail('Could not retrieve the audio stream on ' + probe.absolutePath) |
133 | } | 144 | } |
@@ -211,10 +222,10 @@ describe('Test video transcoding', function () { | |||
211 | const videoDetails: VideoDetails = res2.body | 222 | const videoDetails: VideoDetails = res2.body |
212 | 223 | ||
213 | expect(videoDetails.files).to.have.lengthOf(4) | 224 | expect(videoDetails.files).to.have.lengthOf(4) |
214 | expect(videoDetails.files[ 0 ].fps).to.be.above(58).and.below(62) | 225 | expect(videoDetails.files[0].fps).to.be.above(58).and.below(62) |
215 | expect(videoDetails.files[ 1 ].fps).to.be.below(31) | 226 | expect(videoDetails.files[1].fps).to.be.below(31) |
216 | expect(videoDetails.files[ 2 ].fps).to.be.below(31) | 227 | expect(videoDetails.files[2].fps).to.be.below(31) |
217 | expect(videoDetails.files[ 3 ].fps).to.be.below(31) | 228 | expect(videoDetails.files[3].fps).to.be.below(31) |
218 | 229 | ||
219 | for (const resolution of [ '240', '360', '480' ]) { | 230 | for (const resolution of [ '240', '360', '480' ]) { |
220 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') | 231 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') |
@@ -240,11 +251,11 @@ describe('Test video transcoding', function () { | |||
240 | fixture: 'video_short1.webm', | 251 | fixture: 'video_short1.webm', |
241 | waitTranscoding: true | 252 | waitTranscoding: true |
242 | } | 253 | } |
243 | const resVideo = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributes) | 254 | const resVideo = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) |
244 | const videoId = resVideo.body.video.uuid | 255 | const videoId = resVideo.body.video.uuid |
245 | 256 | ||
246 | // Should be in transcode state | 257 | // Should be in transcode state |
247 | const { body } = await getVideo(servers[ 1 ].url, videoId) | 258 | const { body } = await getVideo(servers[1].url, videoId) |
248 | expect(body.name).to.equal('waiting video') | 259 | expect(body.name).to.equal('waiting video') |
249 | expect(body.state.id).to.equal(VideoState.TO_TRANSCODE) | 260 | expect(body.state.id).to.equal(VideoState.TO_TRANSCODE) |
250 | expect(body.state.label).to.equal('To transcode') | 261 | expect(body.state.label).to.equal('To transcode') |
@@ -310,7 +321,7 @@ describe('Test video transcoding', function () { | |||
310 | 321 | ||
311 | const video = res.body.data.find(v => v.name === videoAttributes.name) | 322 | const video = res.body.data.find(v => v.name === videoAttributes.name) |
312 | 323 | ||
313 | for (const resolution of ['240', '360', '480', '720', '1080']) { | 324 | for (const resolution of [ '240', '360', '480', '720', '1080' ]) { |
314 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') | 325 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') |
315 | const bitrate = await getVideoFileBitrate(path) | 326 | const bitrate = await getVideoFileBitrate(path) |
316 | const fps = await getVideoFileFPS(path) | 327 | const fps = await getVideoFileFPS(path) |
@@ -340,7 +351,7 @@ describe('Test video transcoding', function () { | |||
340 | fixture | 351 | fixture |
341 | } | 352 | } |
342 | 353 | ||
343 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributes) | 354 | await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) |
344 | 355 | ||
345 | await waitJobs(servers) | 356 | await waitJobs(servers) |
346 | 357 | ||
@@ -353,7 +364,7 @@ describe('Test video transcoding', function () { | |||
353 | 364 | ||
354 | expect(videoDetails.files).to.have.lengthOf(4) | 365 | expect(videoDetails.files).to.have.lengthOf(4) |
355 | 366 | ||
356 | const magnetUri = videoDetails.files[ 0 ].magnetUri | 367 | const magnetUri = videoDetails.files[0].magnetUri |
357 | expect(magnetUri).to.contain('.mp4') | 368 | expect(magnetUri).to.contain('.mp4') |
358 | } | 369 | } |
359 | } | 370 | } |
@@ -370,7 +381,7 @@ describe('Test video transcoding', function () { | |||
370 | this.timeout(60000) | 381 | this.timeout(60000) |
371 | 382 | ||
372 | const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } | 383 | const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } |
373 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) | 384 | await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg) |
374 | 385 | ||
375 | await waitJobs(servers) | 386 | await waitJobs(servers) |
376 | 387 | ||
@@ -386,7 +397,7 @@ describe('Test video transcoding', function () { | |||
386 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) | 397 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) |
387 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) | 398 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) |
388 | 399 | ||
389 | const magnetUri = videoDetails.files[ 0 ].magnetUri | 400 | const magnetUri = videoDetails.files[0].magnetUri |
390 | expect(magnetUri).to.contain('.mp4') | 401 | expect(magnetUri).to.contain('.mp4') |
391 | } | 402 | } |
392 | }) | 403 | }) |
@@ -395,7 +406,7 @@ describe('Test video transcoding', function () { | |||
395 | this.timeout(60000) | 406 | this.timeout(60000) |
396 | 407 | ||
397 | const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } | 408 | const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } |
398 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) | 409 | await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg) |
399 | 410 | ||
400 | await waitJobs(servers) | 411 | await waitJobs(servers) |
401 | 412 | ||
@@ -411,11 +422,109 @@ describe('Test video transcoding', function () { | |||
411 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) | 422 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) |
412 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) | 423 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) |
413 | 424 | ||
414 | const magnetUri = videoDetails.files[ 0 ].magnetUri | 425 | const magnetUri = videoDetails.files[0].magnetUri |
415 | expect(magnetUri).to.contain('.mp4') | 426 | expect(magnetUri).to.contain('.mp4') |
416 | } | 427 | } |
417 | }) | 428 | }) |
418 | 429 | ||
430 | it('Should downscale to the closest divisor standard framerate', async function () { | ||
431 | this.timeout(160000) | ||
432 | |||
433 | let tempFixturePath: string | ||
434 | |||
435 | { | ||
436 | tempFixturePath = await generateVideoWithFramerate(59) | ||
437 | |||
438 | const fps = await getVideoFileFPS(tempFixturePath) | ||
439 | expect(fps).to.be.equal(59) | ||
440 | } | ||
441 | |||
442 | const videoAttributes = { | ||
443 | name: '59fps video', | ||
444 | description: '59fps video', | ||
445 | fixture: tempFixturePath | ||
446 | } | ||
447 | |||
448 | await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) | ||
449 | |||
450 | await waitJobs(servers) | ||
451 | |||
452 | for (const server of servers) { | ||
453 | const res = await getVideosList(server.url) | ||
454 | |||
455 | const video = res.body.data.find(v => v.name === videoAttributes.name) | ||
456 | |||
457 | { | ||
458 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') | ||
459 | const fps = await getVideoFileFPS(path) | ||
460 | expect(fps).to.be.equal(25) | ||
461 | } | ||
462 | |||
463 | { | ||
464 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-720.mp4') | ||
465 | const fps = await getVideoFileFPS(path) | ||
466 | expect(fps).to.be.equal(59) | ||
467 | } | ||
468 | } | ||
469 | }) | ||
470 | |||
471 | it('Should provide valid ffprobe data', async function () { | ||
472 | this.timeout(160000) | ||
473 | |||
474 | const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'ffprobe data' })).uuid | ||
475 | await waitJobs(servers) | ||
476 | |||
477 | { | ||
478 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', videoUUID + '-240.mp4') | ||
479 | const metadata = await getMetadataFromFile<VideoFileMetadata>(path) | ||
480 | |||
481 | // expected format properties | ||
482 | for (const p of [ | ||
483 | 'tags.encoder', | ||
484 | 'format_long_name', | ||
485 | 'size', | ||
486 | 'bit_rate' | ||
487 | ]) { | ||
488 | expect(metadata.format).to.have.nested.property(p) | ||
489 | } | ||
490 | |||
491 | // expected stream properties | ||
492 | for (const p of [ | ||
493 | 'codec_long_name', | ||
494 | 'profile', | ||
495 | 'width', | ||
496 | 'height', | ||
497 | 'display_aspect_ratio', | ||
498 | 'avg_frame_rate', | ||
499 | 'pix_fmt' | ||
500 | ]) { | ||
501 | expect(metadata.streams[0]).to.have.nested.property(p) | ||
502 | } | ||
503 | |||
504 | expect(metadata).to.not.have.nested.property('format.filename') | ||
505 | } | ||
506 | |||
507 | for (const server of servers) { | ||
508 | const res2 = await getVideo(server.url, videoUUID) | ||
509 | const videoDetails: VideoDetails = res2.body | ||
510 | |||
511 | const videoFiles = videoDetails.files | ||
512 | .concat(videoDetails.streamingPlaylists[0].files) | ||
513 | expect(videoFiles).to.have.lengthOf(8) | ||
514 | |||
515 | for (const file of videoFiles) { | ||
516 | expect(file.metadata).to.be.undefined | ||
517 | expect(file.metadataUrl).to.exist | ||
518 | expect(file.metadataUrl).to.contain(servers[1].url) | ||
519 | expect(file.metadataUrl).to.contain(videoUUID) | ||
520 | |||
521 | const res3 = await getVideoFileMetadataUrl(file.metadataUrl) | ||
522 | const metadata: FfprobeData = res3.body | ||
523 | expect(metadata).to.have.nested.property('format.size') | ||
524 | } | ||
525 | } | ||
526 | }) | ||
527 | |||
419 | after(async function () { | 528 | after(async function () { |
420 | await cleanupTests(servers) | 529 | await cleanupTests(servers) |
421 | }) | 530 | }) |