From 1896bca09e088b0da9d5e845407ecebae330618c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 28 Jan 2021 15:52:44 +0100 Subject: Support transcoding options/encoders by plugins --- server/tests/api/check-params/config.ts | 2 + server/tests/api/server/config.ts | 6 + server/tests/api/videos/video-transcoder.ts | 4 +- .../peertube-plugin-test-transcoding-one/main.js | 35 ++++ .../package.json | 20 ++ .../peertube-plugin-test-transcoding-two/main.js | 38 ++++ .../package.json | 20 ++ server/tests/plugins/index.ts | 9 +- server/tests/plugins/plugin-transcoding.ts | 226 +++++++++++++++++++++ 9 files changed, 355 insertions(+), 5 deletions(-) create mode 100644 server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js create mode 100644 server/tests/fixtures/peertube-plugin-test-transcoding-one/package.json create mode 100644 server/tests/fixtures/peertube-plugin-test-transcoding-two/main.js create mode 100644 server/tests/fixtures/peertube-plugin-test-transcoding-two/package.json create mode 100644 server/tests/plugins/plugin-transcoding.ts (limited to 'server/tests') diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index d3ae5fe0a..e6309b5f7 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts @@ -87,6 +87,7 @@ describe('Test config API validators', function () { allowAdditionalExtensions: true, allowAudioFiles: true, threads: 1, + profile: 'vod_profile', resolutions: { '0p': false, '240p': false, @@ -115,6 +116,7 @@ describe('Test config API validators', function () { transcoding: { enabled: true, threads: 4, + profile: 'live_profile', resolutions: { '240p': true, '360p': true, diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index e0575bdfd..e5bab0b77 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts @@ -70,6 +70,7 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) { expect(data.transcoding.allowAdditionalExtensions).to.be.false expect(data.transcoding.allowAudioFiles).to.be.false expect(data.transcoding.threads).to.equal(2) + expect(data.transcoding.profile).to.equal('default') expect(data.transcoding.resolutions['240p']).to.be.true expect(data.transcoding.resolutions['360p']).to.be.true expect(data.transcoding.resolutions['480p']).to.be.true @@ -87,6 +88,7 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) { expect(data.live.maxUserLives).to.equal(3) expect(data.live.transcoding.enabled).to.be.false expect(data.live.transcoding.threads).to.equal(2) + expect(data.live.transcoding.profile).to.equal('default') expect(data.live.transcoding.resolutions['240p']).to.be.false expect(data.live.transcoding.resolutions['360p']).to.be.false expect(data.live.transcoding.resolutions['480p']).to.be.false @@ -159,6 +161,7 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.transcoding.threads).to.equal(1) expect(data.transcoding.allowAdditionalExtensions).to.be.true expect(data.transcoding.allowAudioFiles).to.be.true + expect(data.transcoding.profile).to.equal('vod_profile') expect(data.transcoding.resolutions['240p']).to.be.false expect(data.transcoding.resolutions['360p']).to.be.true expect(data.transcoding.resolutions['480p']).to.be.true @@ -175,6 +178,7 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.live.maxUserLives).to.equal(10) expect(data.live.transcoding.enabled).to.be.true expect(data.live.transcoding.threads).to.equal(4) + expect(data.live.transcoding.profile).to.equal('live_profile') expect(data.live.transcoding.resolutions['240p']).to.be.true expect(data.live.transcoding.resolutions['360p']).to.be.true expect(data.live.transcoding.resolutions['480p']).to.be.true @@ -319,6 +323,7 @@ describe('Test config', function () { allowAdditionalExtensions: true, allowAudioFiles: true, threads: 1, + profile: 'vod_profile', resolutions: { '0p': false, '240p': false, @@ -345,6 +350,7 @@ describe('Test config', function () { transcoding: { enabled: true, threads: 4, + profile: 'live_profile', resolutions: { '240p': true, '360p': true, diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 631230f26..5ad02df2f 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts @@ -511,7 +511,9 @@ describe('Test video transcoding', function () { const resolutions = [ 240, 360, 480, 720, 1080 ] for (const r of resolutions) { - expect(await getServerFileSize(servers[1], `videos/${videoUUID}-${r}.mp4`)).to.be.below(60_000) + const path = `videos/${videoUUID}-${r}.mp4` + const size = await getServerFileSize(servers[1], path) + expect(size, `${path} not below ${60_000}`).to.be.below(60_000) } }) diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js new file mode 100644 index 000000000..5990ce1ce --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js @@ -0,0 +1,35 @@ +async function register ({ transcodingManager }) { + + { + const builder = () => { + return { + outputOptions: [ + '-r 10' + ] + } + } + + transcodingManager.addVODProfile('libx264', 'low-vod', builder) + } + + { + const builder = (options) => { + return { + outputOptions: [ + '-r:' + options.streamNum + ' 5' + ] + } + } + + transcodingManager.addLiveProfile('libx264', 'low-live', builder) + } +} + +async function unregister () { + return +} + +module.exports = { + register, + unregister +} diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-one/package.json b/server/tests/fixtures/peertube-plugin-test-transcoding-one/package.json new file mode 100644 index 000000000..bedbfa051 --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-transcoding-one/package.json @@ -0,0 +1,20 @@ +{ + "name": "peertube-plugin-test-transcoding-one", + "version": "0.0.1", + "description": "Plugin test transcoding 1", + "engine": { + "peertube": ">=1.3.0" + }, + "keywords": [ + "peertube", + "plugin" + ], + "homepage": "https://github.com/Chocobozzz/PeerTube", + "author": "Chocobozzz", + "bugs": "https://github.com/Chocobozzz/PeerTube/issues", + "library": "./main.js", + "staticDirs": {}, + "css": [], + "clientScripts": [], + "translations": {} +} diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-two/main.js b/server/tests/fixtures/peertube-plugin-test-transcoding-two/main.js new file mode 100644 index 000000000..a914bce49 --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-transcoding-two/main.js @@ -0,0 +1,38 @@ +async function register ({ transcodingManager }) { + + { + const builder = () => { + return { + outputOptions: [] + } + } + + transcodingManager.addVODProfile('libopus', 'test-vod-profile', builder) + transcodingManager.addVODProfile('libvpx-vp9', 'test-vod-profile', builder) + + transcodingManager.addVODEncoderPriority('audio', 'libopus', 1000) + transcodingManager.addVODEncoderPriority('video', 'libvpx-vp9', 1000) + } + + { + const builder = (options) => { + return { + outputOptions: [ + '-b:' + options.streamNum + ' 10K' + ] + } + } + + transcodingManager.addLiveProfile('libopus', 'test-live-profile', builder) + transcodingManager.addLiveEncoderPriority('audio', 'libopus', 1000) + } +} + +async function unregister () { + return +} + +module.exports = { + register, + unregister +} diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-two/package.json b/server/tests/fixtures/peertube-plugin-test-transcoding-two/package.json new file mode 100644 index 000000000..34be0454b --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-transcoding-two/package.json @@ -0,0 +1,20 @@ +{ + "name": "peertube-plugin-test-transcoding-two", + "version": "0.0.1", + "description": "Plugin test transcoding 2", + "engine": { + "peertube": ">=1.3.0" + }, + "keywords": [ + "peertube", + "plugin" + ], + "homepage": "https://github.com/Chocobozzz/PeerTube", + "author": "Chocobozzz", + "bugs": "https://github.com/Chocobozzz/PeerTube/issues", + "library": "./main.js", + "staticDirs": {}, + "css": [], + "clientScripts": [], + "translations": {} +} diff --git a/server/tests/plugins/index.ts b/server/tests/plugins/index.ts index b870a4055..fd7116efd 100644 --- a/server/tests/plugins/index.ts +++ b/server/tests/plugins/index.ts @@ -1,10 +1,11 @@ import './action-hooks' -import './html-injection' -import './id-and-pass-auth' import './external-auth' import './filter-hooks' -import './translations' -import './video-constants' +import './html-injection' +import './id-and-pass-auth' import './plugin-helpers' import './plugin-router' import './plugin-storage' +import './plugin-transcoding' +import './translations' +import './video-constants' diff --git a/server/tests/plugins/plugin-transcoding.ts b/server/tests/plugins/plugin-transcoding.ts new file mode 100644 index 000000000..96ff4c2fe --- /dev/null +++ b/server/tests/plugins/plugin-transcoding.ts @@ -0,0 +1,226 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import { expect } from 'chai' +import { join } from 'path' +import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' +import { ServerConfig, VideoDetails, VideoPrivacy } from '@shared/models' +import { + buildServerDirectory, + createLive, + getConfig, + getPluginTestPath, + getVideo, + installPlugin, + sendRTMPStreamInVideo, + setAccessTokensToServers, + setDefaultVideoChannel, + uninstallPlugin, + updateCustomSubConfig, + uploadVideoAndGetId, + waitJobs, + waitUntilLivePublished +} from '../../../shared/extra-utils' +import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' + +async function createLiveWrapper (server: ServerInfo) { + const liveAttributes = { + name: 'live video', + channelId: server.videoChannel.id, + privacy: VideoPrivacy.PUBLIC + } + + const res = await createLive(server.url, server.accessToken, liveAttributes) + return res.body.video.uuid +} + +function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { + return updateCustomSubConfig(server.url, server.accessToken, { + transcoding: { + enabled: true, + profile: vodProfile, + hls: { + enabled: true + }, + webtorrent: { + enabled: true + } + }, + live: { + transcoding: { + profile: liveProfile, + enabled: true + } + } + }) +} + +describe('Test transcoding plugins', function () { + let server: ServerInfo + + before(async function () { + this.timeout(60000) + + server = await flushAndRunServer(1) + await setAccessTokensToServers([ server ]) + await setDefaultVideoChannel([ server ]) + + await updateConf(server, 'default', 'default') + }) + + describe('When using a plugin adding profiles to existing encoders', function () { + + async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) { + const res = await getVideo(server.url, uuid) + const video = res.body as VideoDetails + const files = video.files.concat(...video.streamingPlaylists.map(p => p.files)) + + for (const file of files) { + if (type === 'above') { + expect(file.fps).to.be.above(fps) + } else { + expect(file.fps).to.be.below(fps) + } + } + } + + async function checkLiveFPS (uuid: string, type: 'above' | 'below', fps: number) { + const playlistUrl = `${server.url}/static/streaming-playlists/hls/${uuid}/0.m3u8` + const videoFPS = await getVideoFileFPS(playlistUrl) + + if (type === 'above') { + expect(videoFPS).to.be.above(fps) + } else { + expect(videoFPS).to.be.below(fps) + } + } + + before(async function () { + await installPlugin({ + url: server.url, + accessToken: server.accessToken, + path: getPluginTestPath('-transcoding-one') + }) + }) + + it('Should have the appropriate available profiles', async function () { + const res = await getConfig(server.url) + const config = res.body as ServerConfig + + expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod' ]) + expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live' ]) + }) + + it('Should not use the plugin profile if not chosen by the admin', async function () { + this.timeout(120000) + + const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid + await waitJobs([ server ]) + + await checkVideoFPS(videoUUID, 'above', 20) + }) + + it('Should use the vod profile', async function () { + this.timeout(120000) + + await updateConf(server, 'low-vod', 'default') + + const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid + await waitJobs([ server ]) + + await checkVideoFPS(videoUUID, 'below', 12) + }) + + it('Should not use the plugin profile if not chosen by the admin', async function () { + this.timeout(120000) + + const liveVideoId = await createLiveWrapper(server) + + await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') + await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) + await waitJobs([ server ]) + + await checkLiveFPS(liveVideoId, 'above', 20) + }) + + it('Should use the live profile', async function () { + this.timeout(120000) + + await updateConf(server, 'low-vod', 'low-live') + + const liveVideoId = await createLiveWrapper(server) + + await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') + await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) + await waitJobs([ server ]) + + await checkLiveFPS(liveVideoId, 'below', 12) + }) + + it('Should default to the default profile if the specified profile does not exist', async function () { + this.timeout(120000) + + await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-transcoding-one' }) + + const res = await getConfig(server.url) + const config = res.body as ServerConfig + + expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ]) + expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ]) + + const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid + await waitJobs([ server ]) + + await checkVideoFPS(videoUUID, 'above', 20) + }) + + }) + + describe('When using a plugin adding new encoders', function () { + + before(async function () { + await installPlugin({ + url: server.url, + accessToken: server.accessToken, + path: getPluginTestPath('-transcoding-two') + }) + + await updateConf(server, 'test-vod-profile', 'test-live-profile') + }) + + it('Should use the new vod encoders', async function () { + this.timeout(240000) + + const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid + await waitJobs([ server ]) + + const path = buildServerDirectory(server, join('videos', videoUUID + '-720.mp4')) + const audioProbe = await getAudioStream(path) + expect(audioProbe.audioStream.codec_name).to.equal('opus') + + const videoProbe = await getVideoStreamFromFile(path) + expect(videoProbe.codec_name).to.equal('vp9') + }) + + it('Should use the new live encoders', async function () { + this.timeout(120000) + + const liveVideoId = await createLiveWrapper(server) + + await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') + await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) + await waitJobs([ server ]) + + const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` + const audioProbe = await getAudioStream(playlistUrl) + expect(audioProbe.audioStream.codec_name).to.equal('opus') + + const videoProbe = await getVideoStreamFromFile(playlistUrl) + expect(videoProbe.codec_name).to.equal('h264') + }) + }) + + after(async function () { + await cleanupTests([ server ]) + }) +}) -- cgit v1.2.3