Fix splitting audio/video of existing videos
This commit is contained in:
parent
7e9fba3ae5
commit
093a9bf749
5 changed files with 313 additions and 163 deletions
|
@ -1,8 +1,7 @@
|
|||
/* 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 { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||
import {
|
||||
cleanupTests,
|
||||
ConfigCommand,
|
||||
|
@ -16,7 +15,8 @@ import {
|
|||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
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) {
|
||||
for (const file of video.files) {
|
||||
|
@ -81,175 +81,273 @@ function runTests (options: {
|
|||
await servers[0].config.setTranscodingConcurrency(concurrency)
|
||||
})
|
||||
|
||||
it('Should generate HLS', async function () {
|
||||
this.timeout(60000)
|
||||
describe('Common transcoding', function () {
|
||||
|
||||
await servers[0].videos.runTranscoding({
|
||||
videoId: videoUUID,
|
||||
transcodingType: 'hls'
|
||||
it('Should generate HLS', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
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)
|
||||
await expectNoFailedTranscodingJob(servers[0])
|
||||
it('Should generate Web Video', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
for (const server of servers) {
|
||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||
await servers[0].videos.runTranscoding({
|
||||
videoId: videoUUID,
|
||||
transcodingType: 'web-video'
|
||||
})
|
||||
|
||||
expect(videoDetails.files).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
await waitJobs(servers)
|
||||
|
||||
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 () {
|
||||
this.timeout(60000)
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
|
||||
await servers[0].videos.runTranscoding({
|
||||
videoId: videoUUID,
|
||||
transcodingType: 'web-video'
|
||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||
}
|
||||
})
|
||||
|
||||
await waitJobs(servers)
|
||||
it('Should generate Web Video from HLS only video', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
for (const server of servers) {
|
||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||
await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
|
||||
await waitJobs(servers)
|
||||
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||
await waitJobs(servers)
|
||||
|
||||
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 () {
|
||||
this.timeout(60000)
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
|
||||
await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
|
||||
await waitJobs(servers)
|
||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||
}
|
||||
})
|
||||
|
||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||
await waitJobs(servers)
|
||||
it('Should only generate Web Video', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
for (const server of servers) {
|
||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
||||
await waitJobs(servers)
|
||||
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||
await waitJobs(servers)
|
||||
|
||||
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 () {
|
||||
this.timeout(60000)
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
|
||||
|
||||
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
||||
await waitJobs(servers)
|
||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||
}
|
||||
})
|
||||
|
||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||
await waitJobs(servers)
|
||||
it('Should correctly update HLS playlist on resolution change', async function () {
|
||||
this.timeout(120000)
|
||||
|
||||
for (const server of servers) {
|
||||
const videoDetails = await server.videos.get({ id: videoUUID })
|
||||
await servers[0].config.updateExistingConfig({
|
||||
newConfig: {
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
resolutions: ConfigCommand.getConfigResolutions(false),
|
||||
|
||||
expect(videoDetails.files).to.have.lengthOf(5)
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
|
||||
|
||||
if (enableObjectStorage) await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||
}
|
||||
})
|
||||
|
||||
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
|
||||
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' })
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
it('Should have correctly deleted previous files', async function () {
|
||||
for (const fileUrl of shouldBeDeleted) {
|
||||
await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||
}
|
||||
})
|
||||
|
||||
await servers[0].videos.runTranscoding({ videoId: uuid, transcodingType: 'hls' })
|
||||
await waitJobs(servers)
|
||||
it('Should not have updated published at attributes', async function () {
|
||||
const video = await servers[0].videos.get({ id: videoUUID })
|
||||
|
||||
for (const server of servers) {
|
||||
const videoDetails = await server.videos.get({ id: uuid })
|
||||
expect(video.publishedAt).to.equal(publishedAt)
|
||||
})
|
||||
})
|
||||
|
||||
expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
|
||||
expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
|
||||
describe('With split audio and video', function () {
|
||||
|
||||
if (enableObjectStorage) {
|
||||
await checkFilesInObjectStorage(objectStorage, videoDetails)
|
||||
async function runTest (options: {
|
||||
audio: boolean
|
||||
hls: boolean
|
||||
webVideo: boolean
|
||||
afterWebVideo: boolean
|
||||
resolutions?: number[]
|
||||
}) {
|
||||
let resolutions = options.resolutions
|
||||
|
||||
const hlsPlaylist = videoDetails.streamingPlaylists[0]
|
||||
const resolutions = hlsPlaylist.files.map(f => f.resolution.id)
|
||||
await checkResolutionsInMasterPlaylist({ server: servers[0], playlistUrl: hlsPlaylist.playlistUrl, resolutions })
|
||||
if (!resolutions) {
|
||||
resolutions = [ 720, 240 ]
|
||||
|
||||
const shaBody = await servers[0].streamingPlaylists.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, withRetry: true })
|
||||
expect(Object.keys(shaBody)).to.have.lengthOf(5)
|
||||
if (options.audio) resolutions.push(0)
|
||||
}
|
||||
|
||||
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 () {
|
||||
for (const fileUrl of shouldBeDeleted) {
|
||||
await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||
}
|
||||
})
|
||||
it('Should split audio and video from an existing Web & HLS video', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
it('Should not have updated published at attributes', async function () {
|
||||
const video = await servers[0].videos.get({ id: videoUUID })
|
||||
await runTest({ webVideo: true, hls: true, afterWebVideo: true, audio: false })
|
||||
})
|
||||
|
||||
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 () {
|
||||
|
|
|
@ -243,6 +243,41 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
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 () {
|
||||
|
@ -266,10 +301,12 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
})
|
||||
|
||||
function runSuites (objectStorage?: ObjectStorageCommand) {
|
||||
const resolutions = 'max'
|
||||
|
||||
describe('Web video only enabled', 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 })
|
||||
|
@ -278,7 +315,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
describe('HLS videos only enabled', 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 })
|
||||
|
@ -287,7 +324,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
describe('HLS only with separated audio only enabled', 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 })
|
||||
|
@ -296,7 +333,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
describe('Web video & HLS with separated audio only enabled', 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 })
|
||||
|
@ -305,7 +342,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
|||
describe('Web video & HLS enabled', 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 })
|
||||
|
|
|
@ -127,8 +127,7 @@ export abstract class AbstractJobBuilder <P> {
|
|||
}
|
||||
|
||||
await this.createJobs({
|
||||
parent: mergeOrOptimizePayload,
|
||||
children,
|
||||
payloads: [ [ mergeOrOptimizePayload ], ...children ],
|
||||
user,
|
||||
video
|
||||
})
|
||||
|
@ -151,19 +150,24 @@ export abstract class AbstractJobBuilder <P> {
|
|||
|
||||
const inputFPS = video.getMaxFPS()
|
||||
|
||||
const children = childrenResolutions.map(resolution => {
|
||||
const fps = computeOutputFPS({ inputFPS, resolution, isOriginResolution: maxResolution === resolution, type: 'vod' })
|
||||
const children = childrenResolutions
|
||||
.map(resolution => {
|
||||
const fps = computeOutputFPS({ inputFPS, resolution, isOriginResolution: maxResolution === resolution, type: 'vod' })
|
||||
|
||||
if (transcodingType === 'hls') {
|
||||
return this.buildHLSJobPayload({ video, resolution, fps, isNewVideo, separatedAudio })
|
||||
}
|
||||
if (transcodingType === 'hls') {
|
||||
// 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.buildWebVideoJobPayload({ video, resolution, fps, isNewVideo })
|
||||
}
|
||||
return this.buildHLSJobPayload({ video, resolution, fps, isNewVideo, separatedAudio })
|
||||
}
|
||||
|
||||
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' })
|
||||
|
||||
|
@ -171,9 +175,17 @@ export abstract class AbstractJobBuilder <P> {
|
|||
? this.buildHLSJobPayload({ video, resolution: maxResolution, fps, isNewVideo, separatedAudio })
|
||||
: this.buildWebVideoJobPayload({ video, resolution: maxResolution, fps, isNewVideo })
|
||||
|
||||
// Process the last resolution after the other ones to prevent concurrency issue
|
||||
// Because low resolutions use the biggest one as ffmpeg input
|
||||
await this.createJobs({ video, parent, children: [ children ], user: null })
|
||||
// Low resolutions use the biggest one as ffmpeg input so we need to process max resolution (with audio) independently
|
||||
const payloads: [ [ P ], ...(P[][]) ] = [ [ parent ] ]
|
||||
|
||||
// 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: {
|
||||
|
@ -247,8 +259,7 @@ export abstract class AbstractJobBuilder <P> {
|
|||
|
||||
protected abstract createJobs (options: {
|
||||
video: MVideoFullLight
|
||||
parent: P
|
||||
children: P[][]
|
||||
payloads: [ [ P ], ...(P[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||
user: MUserId | null
|
||||
}): Promise<void>
|
||||
|
||||
|
|
|
@ -22,14 +22,16 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder <Payload> {
|
|||
|
||||
protected async createJobs (options: {
|
||||
video: MVideo
|
||||
parent: Payload
|
||||
children: Payload[][]
|
||||
payloads: [ [ Payload ], ...(Payload[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||
user: MUserId | null
|
||||
}): Promise<void> {
|
||||
const { video, parent, children, user } = options
|
||||
const { video, payloads, user } = options
|
||||
|
||||
const nextTranscodingSequentialJobs = await Bluebird.mapSeries(children, payloads => {
|
||||
return Bluebird.mapSeries(payloads, payload => {
|
||||
const parent = payloads[0][0]
|
||||
payloads.shift()
|
||||
|
||||
const nextTranscodingSequentialJobs = await Bluebird.mapSeries(payloads, p => {
|
||||
return Bluebird.mapSeries(p, payload => {
|
||||
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
|
||||
await VideoJobInfoModel.increaseOrCreate(video.uuid, 'pendingTranscode')
|
||||
|
|
|
@ -31,15 +31,17 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder <Payload> {
|
|||
|
||||
protected async createJobs (options: {
|
||||
video: MVideo
|
||||
parent: Payload
|
||||
children: Payload[][] // Array of sequential jobs to create that depend on parent job
|
||||
payloads: [ [ Payload ], ...(Payload[][]) ] // Array of sequential jobs to create that depend on parent job
|
||||
user: MUserId | null
|
||||
}): 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 })
|
||||
|
||||
for (const parallelPayloads of children) {
|
||||
for (const parallelPayloads of payloads) {
|
||||
let lastJob = parentJob
|
||||
|
||||
for (const parallelPayload of parallelPayloads) {
|
||||
|
|
Loading…
Reference in a new issue