Fix splitting audio/video of existing videos
This commit is contained in:
parent
7e9fba3ae5
commit
093a9bf749
|
@ -1,8 +1,7 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||||
|
|
||||||
import { expect } from 'chai'
|
|
||||||
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
|
||||||
import { HttpStatusCode, VideoDetails } from '@peertube/peertube-models'
|
import { HttpStatusCode, VideoDetails } from '@peertube/peertube-models'
|
||||||
|
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||||
import {
|
import {
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
ConfigCommand,
|
ConfigCommand,
|
||||||
|
@ -16,7 +15,8 @@ import {
|
||||||
waitJobs
|
waitJobs
|
||||||
} from '@peertube/peertube-server-commands'
|
} from '@peertube/peertube-server-commands'
|
||||||
import { expectStartWith } from '@tests/shared/checks.js'
|
import { expectStartWith } from '@tests/shared/checks.js'
|
||||||
import { checkResolutionsInMasterPlaylist } from '@tests/shared/streaming-playlists.js'
|
import { checkResolutionsInMasterPlaylist, completeCheckHlsPlaylist } from '@tests/shared/streaming-playlists.js'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
|
||||||
async function checkFilesInObjectStorage (objectStorage: ObjectStorageCommand, video: VideoDetails) {
|
async function checkFilesInObjectStorage (objectStorage: ObjectStorageCommand, video: VideoDetails) {
|
||||||
for (const file of video.files) {
|
for (const file of video.files) {
|
||||||
|
@ -81,175 +81,273 @@ function runTests (options: {
|
||||||
await servers[0].config.setTranscodingConcurrency(concurrency)
|
await servers[0].config.setTranscodingConcurrency(concurrency)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should generate HLS', async function () {
|
describe('Common transcoding', function () {
|
||||||
this.timeout(60000)
|
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({
|
it('Should generate HLS', async function () {
|
||||||
videoId: videoUUID,
|
this.timeout(60000)
|
||||||
transcodingType: 'hls'
|
|
||||||
|
await servers[0].videos.runTranscoding({
|
||||||
|
videoId: videoUUID,
|
||||||
|
transcodingType: 'hls'
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
await expectNoFailedTranscodingJob(servers[0])
|
||||||
|
|
||||||
|
for (const server of servers) {
|
||||||
|
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||||
|
|
||||||
|
expect(videoDetails.files).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||||
|
|
||||||
|
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
await waitJobs(servers)
|
it('Should generate Web Video', async function () {
|
||||||
await expectNoFailedTranscodingJob(servers[0])
|
this.timeout(60000)
|
||||||
|
|
||||||
for (const server of servers) {
|
await servers[0].videos.runTranscoding({
|
||||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
videoId: videoUUID,
|
||||||
|
transcodingType: 'web-video'
|
||||||
|
})
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(1)
|
await waitJobs(servers)
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
|
||||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
|
||||||
|
|
||||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
for (const server of servers) {
|
||||||
}
|
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||||
})
|
|
||||||
|
|
||||||
it('Should generate Web Video', async function () {
|
expect(videoDetails.files).to.have.lengthOf(5)
|
||||||
this.timeout(60000)
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({
|
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
videoId: videoUUID,
|
}
|
||||||
transcodingType: 'web-video'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await waitJobs(servers)
|
it('Should generate Web Video from HLS only video', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
for (const server of servers) {
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
|
||||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
await waitJobs(servers)
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(5)
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
await waitJobs(servers)
|
||||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
|
||||||
|
|
||||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
for (const server of servers) {
|
||||||
}
|
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||||
})
|
|
||||||
|
|
||||||
it('Should generate Web Video from HLS only video', async function () {
|
expect(videoDetails.files).to.have.lengthOf(5)
|
||||||
this.timeout(60000)
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||||
|
|
||||||
await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
|
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
await waitJobs(servers)
|
}
|
||||||
|
})
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
it('Should only generate Web Video', async function () {
|
||||||
await waitJobs(servers)
|
this.timeout(60000)
|
||||||
|
|
||||||
for (const server of servers) {
|
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
||||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
await waitJobs(servers)
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(5)
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
await waitJobs(servers)
|
||||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
|
||||||
|
|
||||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
for (const server of servers) {
|
||||||
}
|
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||||
})
|
|
||||||
|
|
||||||
it('Should only generate Web Video', async function () {
|
expect(videoDetails.files).to.have.lengthOf(5)
|
||||||
this.timeout(60000)
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
|
||||||
|
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
await waitJobs(servers)
|
}
|
||||||
|
})
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
it('Should correctly update HLS playlist on resolution change', async function () {
|
||||||
await waitJobs(servers)
|
this.timeout(120000)
|
||||||
|
|
||||||
for (const server of servers) {
|
await servers[0].config.updateExistingConfig({
|
||||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
newConfig: {
|
||||||
|
transcoding: {
|
||||||
|
enabled: true,
|
||||||
|
resolutions: ConfigCommand.getConfigResolutions(false),
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(5)
|
webVideos: {
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
|
enabled: true
|
||||||
|
},
|
||||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
hls: {
|
||||||
}
|
enabled: true
|
||||||
})
|
}
|
||||||
|
|
||||||
it('Should correctly update HLS playlist on resolution change', async function () {
|
|
||||||
this.timeout(120000)
|
|
||||||
|
|
||||||
await servers[0].config.updateExistingConfig({
|
|
||||||
newConfig: {
|
|
||||||
transcoding: {
|
|
||||||
enabled: true,
|
|
||||||
resolutions: ConfigCommand.getConfigResolutions(false),
|
|
||||||
|
|
||||||
webVideos: {
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
hls: {
|
|
||||||
enabled: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { uuid } = await servers[0].videos.quickUpload({ name: 'quick' })
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const server of servers) {
|
||||||
|
const videoDetails = await server.videos.get({ id: uuid })
|
||||||
|
|
||||||
|
expect(videoDetails.files).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(1)
|
||||||
|
|
||||||
|
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
|
|
||||||
|
shouldBeDeleted = [
|
||||||
|
videoDetails.streamingPlaylists[0].files[0].fileUrl,
|
||||||
|
videoDetails.streamingPlaylists[0].playlistUrl,
|
||||||
|
videoDetails.streamingPlaylists[0].segmentsSha256Url
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
await servers[0].config.updateExistingConfig({
|
||||||
|
newConfig: {
|
||||||
|
transcoding: {
|
||||||
|
enabled: true,
|
||||||
|
resolutions: ConfigCommand.getConfigResolutions(true),
|
||||||
|
|
||||||
|
webVideos: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
hls: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await servers[0].videos.runTranscoding({ videoId: uuid, transcodingType: 'hls' })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const server of servers) {
|
||||||
|
const videoDetails = await server.videos.get({ id: uuid })
|
||||||
|
|
||||||
|
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||||
|
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||||
|
|
||||||
|
if (enableObjectStorage) {
|
||||||
|
await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||||
|
|
||||||
|
const hlsPlaylist = videoDetails.streamingPlaylists[0]
|
||||||
|
const resolutions = hlsPlaylist.files.map(f => f.resolution.id)
|
||||||
|
await checkResolutionsInMasterPlaylist({ server: servers[0], playlistUrl: hlsPlaylist.playlistUrl, resolutions })
|
||||||
|
|
||||||
|
const shaBody = await servers[0].streamingPlaylists.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, withRetry: true })
|
||||||
|
expect(Object.keys(shaBody)).to.have.lengthOf(5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { uuid } = await servers[0].videos.quickUpload({ name: 'quick' })
|
it('Should have correctly deleted previous files', async function () {
|
||||||
|
for (const fileUrl of shouldBeDeleted) {
|
||||||
await waitJobs(servers)
|
await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||||
|
|
||||||
for (const server of servers) {
|
|
||||||
const videoDetails = await server.videos.get({ id: uuid })
|
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(1)
|
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
|
||||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(1)
|
|
||||||
|
|
||||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
|
||||||
|
|
||||||
shouldBeDeleted = [
|
|
||||||
videoDetails.streamingPlaylists[0].files[0].fileUrl,
|
|
||||||
videoDetails.streamingPlaylists[0].playlistUrl,
|
|
||||||
videoDetails.streamingPlaylists[0].segmentsSha256Url
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
await servers[0].config.updateExistingConfig({
|
|
||||||
newConfig: {
|
|
||||||
transcoding: {
|
|
||||||
enabled: true,
|
|
||||||
resolutions: ConfigCommand.getConfigResolutions(true),
|
|
||||||
|
|
||||||
webVideos: {
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
hls: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: uuid, transcodingType: 'hls' })
|
it('Should not have updated published at attributes', async function () {
|
||||||
await waitJobs(servers)
|
const video = await servers[0].videos.get({ id: videoUUID })
|
||||||
|
|
||||||
for (const server of servers) {
|
expect(video.publishedAt).to.equal(publishedAt)
|
||||||
const videoDetails = await server.videos.get({ id: uuid })
|
})
|
||||||
|
})
|
||||||
|
|
||||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
describe('With split audio and video', function () {
|
||||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
|
||||||
|
|
||||||
if (enableObjectStorage) {
|
async function runTest (options: {
|
||||||
await checkFilesInObjectStorage(objectStorage, videoDetails)
|
audio: boolean
|
||||||
|
hls: boolean
|
||||||
|
webVideo: boolean
|
||||||
|
afterWebVideo: boolean
|
||||||
|
resolutions?: number[]
|
||||||
|
}) {
|
||||||
|
let resolutions = options.resolutions
|
||||||
|
|
||||||
const hlsPlaylist = videoDetails.streamingPlaylists[0]
|
if (!resolutions) {
|
||||||
const resolutions = hlsPlaylist.files.map(f => f.resolution.id)
|
resolutions = [ 720, 240 ]
|
||||||
await checkResolutionsInMasterPlaylist({ server: servers[0], playlistUrl: hlsPlaylist.playlistUrl, resolutions })
|
|
||||||
|
|
||||||
const shaBody = await servers[0].streamingPlaylists.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, withRetry: true })
|
if (options.audio) resolutions.push(0)
|
||||||
expect(Object.keys(shaBody)).to.have.lengthOf(5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const objectStorageBaseUrl = enableObjectStorage
|
||||||
|
? objectStorage?.getMockPlaylistBaseUrl()
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
await servers[0].config.enableTranscoding({
|
||||||
|
resolutions,
|
||||||
|
hls: options.hls,
|
||||||
|
splitAudioAndVideo: false,
|
||||||
|
webVideo: options.webVideo
|
||||||
|
})
|
||||||
|
|
||||||
|
const { uuid: videoUUID } = await servers[0].videos.quickUpload({ name: 'hls splitted' })
|
||||||
|
await waitJobs(servers)
|
||||||
|
await completeCheckHlsPlaylist({
|
||||||
|
servers,
|
||||||
|
resolutions,
|
||||||
|
videoUUID,
|
||||||
|
hlsOnly: !options.webVideo,
|
||||||
|
splittedAudio: false,
|
||||||
|
objectStorageBaseUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
await servers[0].config.enableTranscoding({
|
||||||
|
resolutions,
|
||||||
|
hls: true,
|
||||||
|
splitAudioAndVideo: true,
|
||||||
|
webVideo: options.afterWebVideo
|
||||||
|
})
|
||||||
|
|
||||||
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'hls' })
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
if (options.afterWebVideo) {
|
||||||
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||||
|
await waitJobs(servers)
|
||||||
|
}
|
||||||
|
|
||||||
|
await completeCheckHlsPlaylist({
|
||||||
|
servers,
|
||||||
|
resolutions,
|
||||||
|
videoUUID,
|
||||||
|
hlsOnly: !options.afterWebVideo,
|
||||||
|
splittedAudio: true,
|
||||||
|
objectStorageBaseUrl
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
it('Should have correctly deleted previous files', async function () {
|
it('Should split audio and video from an existing Web & HLS video', async function () {
|
||||||
for (const fileUrl of shouldBeDeleted) {
|
this.timeout(60000)
|
||||||
await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should not have updated published at attributes', async function () {
|
await runTest({ webVideo: true, hls: true, afterWebVideo: true, audio: false })
|
||||||
const video = await servers[0].videos.get({ id: videoUUID })
|
})
|
||||||
|
|
||||||
expect(video.publishedAt).to.equal(publishedAt)
|
it('Should split audio and video from an existing HLS video without audio resolution', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
await runTest({ webVideo: false, hls: true, afterWebVideo: true, audio: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should split audio and video to a HLS only video from an existing HLS video without audio resolution', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
await runTest({ webVideo: false, hls: true, afterWebVideo: false, audio: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should split audio and video to a HLS only video from an existing HLS video with audio resolution', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
await runTest({ webVideo: false, hls: true, afterWebVideo: false, audio: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should split audio and video on HLS only video that only have 1 resolution', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
await runTest({ webVideo: false, hls: true, afterWebVideo: false, audio: false, resolutions: [ 720 ] })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -243,6 +243,41 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
resolutions: [ 720, 480, 360, 240, 144, 0 ]
|
resolutions: [ 720, 480, 360, 240, 144, 0 ]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should re-transcode a non splitted audio/video HLS only video', async function () {
|
||||||
|
this.timeout(240000)
|
||||||
|
|
||||||
|
const resolutions = [ 720, 240 ]
|
||||||
|
|
||||||
|
await servers[0].config.enableTranscoding({
|
||||||
|
hls: true,
|
||||||
|
webVideo: false,
|
||||||
|
resolutions,
|
||||||
|
splitAudioAndVideo: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const { uuid } = await servers[0].videos.quickUpload({ name: 'manual hls only transcoding', fixture: 'video_short.mp4' })
|
||||||
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
|
await servers[0].config.enableTranscoding({
|
||||||
|
hls: hlsEnabled,
|
||||||
|
webVideo: webVideoEnabled,
|
||||||
|
resolutions,
|
||||||
|
splitAudioAndVideo: splittedAudio
|
||||||
|
})
|
||||||
|
|
||||||
|
await servers[0].videos.runTranscoding({ transcodingType: 'hls', videoId: uuid })
|
||||||
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
|
await completeCheckHlsPlaylist({
|
||||||
|
hlsOnly: true,
|
||||||
|
servers: [ servers[0] ],
|
||||||
|
videoUUID: uuid,
|
||||||
|
splittedAudio,
|
||||||
|
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
||||||
|
resolutions
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
@ -266,10 +301,12 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
function runSuites (objectStorage?: ObjectStorageCommand) {
|
function runSuites (objectStorage?: ObjectStorageCommand) {
|
||||||
|
const resolutions = 'max'
|
||||||
|
|
||||||
describe('Web video only enabled', function () {
|
describe('Web video only enabled', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
await servers[0].config.enableTranscoding({ resolutions: 'max', webVideo: true, hls: false, with0p: true })
|
await servers[0].config.enableTranscoding({ resolutions, webVideo: true, hls: false, with0p: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: false, objectStorage })
|
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: false, objectStorage })
|
||||||
|
@ -278,7 +315,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
describe('HLS videos only enabled', function () {
|
describe('HLS videos only enabled', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
await servers[0].config.enableTranscoding({ webVideo: false, hls: true, with0p: true })
|
await servers[0].config.enableTranscoding({ resolutions, webVideo: false, hls: true, with0p: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
runSpecificSuite({ webVideoEnabled: false, hlsEnabled: true, objectStorage })
|
runSpecificSuite({ webVideoEnabled: false, hlsEnabled: true, objectStorage })
|
||||||
|
@ -287,7 +324,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
describe('HLS only with separated audio only enabled', function () {
|
describe('HLS only with separated audio only enabled', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
await servers[0].config.enableTranscoding({ webVideo: false, hls: true, splitAudioAndVideo: true, with0p: true })
|
await servers[0].config.enableTranscoding({ resolutions, webVideo: false, hls: true, splitAudioAndVideo: true, with0p: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
runSpecificSuite({ webVideoEnabled: false, hlsEnabled: true, splittedAudio: true, objectStorage })
|
runSpecificSuite({ webVideoEnabled: false, hlsEnabled: true, splittedAudio: true, objectStorage })
|
||||||
|
@ -296,7 +333,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
describe('Web video & HLS with separated audio only enabled', function () {
|
describe('Web video & HLS with separated audio only enabled', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, splitAudioAndVideo: true, with0p: true })
|
await servers[0].config.enableTranscoding({ resolutions, hls: true, webVideo: true, splitAudioAndVideo: true, with0p: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: true, splittedAudio: true, objectStorage })
|
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: true, splittedAudio: true, objectStorage })
|
||||||
|
@ -305,7 +342,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
describe('Web video & HLS enabled', function () {
|
describe('Web video & HLS enabled', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, with0p: true, splitAudioAndVideo: false })
|
await servers[0].config.enableTranscoding({ resolutions, hls: true, webVideo: true, with0p: true, splitAudioAndVideo: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: true, objectStorage })
|
runSpecificSuite({ webVideoEnabled: true, hlsEnabled: true, objectStorage })
|
||||||
|
|
|
@ -127,8 +127,7 @@ export abstract class AbstractJobBuilder <P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createJobs({
|
await this.createJobs({
|
||||||
parent: mergeOrOptimizePayload,
|
payloads: [ [ mergeOrOptimizePayload ], ...children ],
|
||||||
children,
|
|
||||||
user,
|
user,
|
||||||
video
|
video
|
||||||
})
|
})
|
||||||
|
@ -151,19 +150,24 @@ export abstract class AbstractJobBuilder <P> {
|
||||||
|
|
||||||
const inputFPS = video.getMaxFPS()
|
const inputFPS = video.getMaxFPS()
|
||||||
|
|
||||||
const children = childrenResolutions.map(resolution => {
|
const children = childrenResolutions
|
||||||
const fps = computeOutputFPS({ inputFPS, resolution, isOriginResolution: maxResolution === resolution, type: 'vod' })
|
.map(resolution => {
|
||||||
|
const fps = computeOutputFPS({ inputFPS, resolution, isOriginResolution: maxResolution === resolution, type: 'vod' })
|
||||||
|
|
||||||
if (transcodingType === 'hls') {
|
if (transcodingType === 'hls') {
|
||||||
return this.buildHLSJobPayload({ video, resolution, fps, isNewVideo, separatedAudio })
|
// We'll generate audio resolution in a parent job
|
||||||
}
|
if (resolution === VideoResolution.H_NOVIDEO && separatedAudio) return undefined
|
||||||
|
|
||||||
if (transcodingType === 'webtorrent' || transcodingType === 'web-video') {
|
return this.buildHLSJobPayload({ video, resolution, fps, isNewVideo, separatedAudio })
|
||||||
return this.buildWebVideoJobPayload({ video, resolution, fps, isNewVideo })
|
}
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unknown transcoding type')
|
if (transcodingType === 'webtorrent' || transcodingType === 'web-video') {
|
||||||
})
|
return this.buildWebVideoJobPayload({ video, resolution, fps, isNewVideo })
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unknown transcoding type')
|
||||||
|
})
|
||||||
|
.filter(r => !!r)
|
||||||
|
|
||||||
const fps = computeOutputFPS({ inputFPS, resolution: maxResolution, isOriginResolution: true, type: 'vod' })
|
const fps = computeOutputFPS({ inputFPS, resolution: maxResolution, isOriginResolution: true, type: 'vod' })
|
||||||
|
|
||||||
|
@ -171,9 +175,17 @@ export abstract class AbstractJobBuilder <P> {
|
||||||
? this.buildHLSJobPayload({ video, resolution: maxResolution, fps, isNewVideo, separatedAudio })
|
? this.buildHLSJobPayload({ video, resolution: maxResolution, fps, isNewVideo, separatedAudio })
|
||||||
: this.buildWebVideoJobPayload({ video, resolution: maxResolution, fps, isNewVideo })
|
: this.buildWebVideoJobPayload({ video, resolution: maxResolution, fps, isNewVideo })
|
||||||
|
|
||||||
// Process the last resolution after the other ones to prevent concurrency issue
|
// Low resolutions use the biggest one as ffmpeg input so we need to process max resolution (with audio) independently
|
||||||
// Because low resolutions use the biggest one as ffmpeg input
|
const payloads: [ [ P ], ...(P[][]) ] = [ [ parent ] ]
|
||||||
await this.createJobs({ video, parent, children: [ children ], user: null })
|
|
||||||
|
// Process audio first to not override the max resolution where the audio stream will be removed
|
||||||
|
if (transcodingType === 'hls' && separatedAudio) {
|
||||||
|
payloads.unshift([ this.buildHLSJobPayload({ video, resolution: VideoResolution.H_NOVIDEO, fps, isNewVideo, separatedAudio }) ])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children && children.length !== 0) payloads.push(children)
|
||||||
|
|
||||||
|
await this.createJobs({ video, payloads, user: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async buildLowerResolutionJobPayloads (options: {
|
private async buildLowerResolutionJobPayloads (options: {
|
||||||
|
@ -247,8 +259,7 @@ export abstract class AbstractJobBuilder <P> {
|
||||||
|
|
||||||
protected abstract createJobs (options: {
|
protected abstract createJobs (options: {
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
parent: P
|
payloads: [ [ P ], ...(P[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||||
children: P[][]
|
|
||||||
user: MUserId | null
|
user: MUserId | null
|
||||||
}): Promise<void>
|
}): Promise<void>
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,16 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder <Payload> {
|
||||||
|
|
||||||
protected async createJobs (options: {
|
protected async createJobs (options: {
|
||||||
video: MVideo
|
video: MVideo
|
||||||
parent: Payload
|
payloads: [ [ Payload ], ...(Payload[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||||
children: Payload[][]
|
|
||||||
user: MUserId | null
|
user: MUserId | null
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { video, parent, children, user } = options
|
const { video, payloads, user } = options
|
||||||
|
|
||||||
const nextTranscodingSequentialJobs = await Bluebird.mapSeries(children, payloads => {
|
const parent = payloads[0][0]
|
||||||
return Bluebird.mapSeries(payloads, payload => {
|
payloads.shift()
|
||||||
|
|
||||||
|
const nextTranscodingSequentialJobs = await Bluebird.mapSeries(payloads, p => {
|
||||||
|
return Bluebird.mapSeries(p, payload => {
|
||||||
return this.buildTranscodingJob({ payload, user })
|
return this.buildTranscodingJob({ payload, user })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -42,9 +44,9 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder <Payload> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mergeOrOptimizeJob = await this.buildTranscodingJob({ payload: parent, user, hasChildren: !!children.length })
|
const parentJob = await this.buildTranscodingJob({ payload: parent, user, hasChildren: payloads.length !== 0 })
|
||||||
|
|
||||||
await JobQueue.Instance.createSequentialJobFlow(mergeOrOptimizeJob, transcodingJobBuilderJob)
|
await JobQueue.Instance.createSequentialJobFlow(parentJob, transcodingJobBuilderJob)
|
||||||
|
|
||||||
// transcoding-job-builder job will increase pendingTranscode
|
// transcoding-job-builder job will increase pendingTranscode
|
||||||
await VideoJobInfoModel.increaseOrCreate(video.uuid, 'pendingTranscode')
|
await VideoJobInfoModel.increaseOrCreate(video.uuid, 'pendingTranscode')
|
||||||
|
|
|
@ -31,15 +31,17 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder <Payload> {
|
||||||
|
|
||||||
protected async createJobs (options: {
|
protected async createJobs (options: {
|
||||||
video: MVideo
|
video: MVideo
|
||||||
parent: Payload
|
payloads: [ [ Payload ], ...(Payload[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||||
children: Payload[][] // Array of sequential jobs to create that depend on parent job
|
|
||||||
user: MUserId | null
|
user: MUserId | null
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { parent, children, user } = options
|
const { payloads, user } = options
|
||||||
|
|
||||||
|
const parent = payloads[0][0]
|
||||||
|
payloads.shift()
|
||||||
|
|
||||||
const parentJob = await this.createJob({ payload: parent, user })
|
const parentJob = await this.createJob({ payload: parent, user })
|
||||||
|
|
||||||
for (const parallelPayloads of children) {
|
for (const parallelPayloads of payloads) {
|
||||||
let lastJob = parentJob
|
let lastJob = parentJob
|
||||||
|
|
||||||
for (const parallelPayload of parallelPayloads) {
|
for (const parallelPayload of parallelPayloads) {
|
||||||
|
|
Loading…
Reference in New Issue