Skip to content

Commit

Permalink
Respect "transcode original resolution" for runner
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Nov 17, 2023
1 parent 1682b0b commit d4f2149
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ export type RunnerJobPrivatePayload =
export interface RunnerJobVODWebVideoTranscodingPrivatePayload {
videoUUID: string
isNewVideo: boolean
deleteInputFileId: number | null
}

export interface RunnerJobVODAudioMergeTranscodingPrivatePayload {
videoUUID: string
isNewVideo: boolean
deleteInputFileId: number | null
}

export interface RunnerJobVODHLSTranscodingPrivatePayload {
Expand Down
39 changes: 39 additions & 0 deletions packages/tests/src/peertube-runner/vod-transcoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,45 @@ describe('Test VOD transcoding in peertube-runner program', function () {
resolutions: [ 720, 480, 360, 240, 144, 0 ]
})
})

it('Should not generate an upper resolution than original file', async function () {
this.timeout(120_000)

await servers[0].config.updateExistingSubConfig({
newConfig: {
transcoding: {
enabled: true,
hls: { enabled: true },
webVideos: { enabled: true },
resolutions: {
'0p': false,
'144p': false,
'240p': true,
'360p': false,
'480p': true,
'720p': false,
'1080p': false,
'1440p': false,
'2160p': false
},
alwaysTranscodeOriginalResolution: false
}
}
})

const { uuid } = await servers[0].videos.quickUpload({ name: 'video', fixture: 'video_short.webm' })
await waitJobs(servers, { runnerJobs: true })

const video = await servers[0].videos.get({ id: uuid })
const hlsFiles = video.streamingPlaylists[0].files

expect(video.files).to.have.lengthOf(2)
expect(hlsFiles).to.have.lengthOf(2)

// eslint-disable-next-line @typescript-eslint/require-array-sort-compare
const resolutions = getAllFiles(video).map(f => f.resolution.id).sort()
expect(resolutions).to.deep.equal([ 240, 240, 480, 480 ])
})
}

before(async function () {
Expand Down
2 changes: 2 additions & 0 deletions packages/tests/src/shared/videos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ async function completeWebVideoFilesCheck (options: {

const transcodingEnabled = serverConfig.transcoding.web_videos.enabled

expect(files).to.have.lengthOf(files.length)

for (const attributeFile of files) {
const file = video.files.find(f => f.resolution.id === attributeFile.resolution)
expect(file, `resolution ${attributeFile.resolution} does not exist`).not.to.be.undefined
Expand Down
18 changes: 18 additions & 0 deletions server/core/lib/runners/job-handlers/shared/vod-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VideoModel } from '@server/models/video/video.js'
import { MVideoFullLight } from '@server/types/models/index.js'
import { MRunnerJob } from '@server/types/models/runners/index.js'
import { RunnerJobVODAudioMergeTranscodingPrivatePayload, RunnerJobVODWebVideoTranscodingPrivatePayload } from '@peertube/peertube-models'
import { lTags } from '@server/lib/object-storage/shared/logger.js'

export async function onVODWebVideoOrAudioMergeTranscodingJob (options: {
video: MVideoFullLight
Expand All @@ -28,6 +29,23 @@ export async function onVODWebVideoOrAudioMergeTranscodingJob (options: {
videoOutputPath: newVideoFilePath
})

if (privatePayload.deleteInputFileId) {
const inputFile = video.VideoFiles.find(f => f.id === privatePayload.deleteInputFileId)

if (inputFile) {
await video.removeWebVideoFile(inputFile)
await inputFile.destroy()

video.VideoFiles = video.VideoFiles.filter(f => f.id !== inputFile.id)
} else {
logger.error(
'Cannot delete input file %d of video %s: does not exist anymore',
privatePayload.deleteInputFileId, video.uuid,
{ ...lTags(video.uuid), privatePayload }
)
}
}

await onTranscodingEnded({ isNewVideo: privatePayload.isNewVideo, moveVideoToNextState: true, video })
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type CreateOptions = {
resolution: number
fps: number
priority: number
deleteInputFileId: number | null
dependsOnRunnerJob?: MRunnerJob
}

Expand All @@ -43,7 +44,7 @@ export class VODAudioMergeTranscodingJobHandler extends AbstractVODTranscodingJo
}

const privatePayload: RunnerJobVODWebVideoTranscodingPrivatePayload = {
...pick(options, [ 'isNewVideo' ]),
...pick(options, [ 'isNewVideo', 'deleteInputFileId' ]),

videoUUID: video.uuid
}
Expand Down Expand Up @@ -81,12 +82,6 @@ export class VODAudioMergeTranscodingJobHandler extends AbstractVODTranscodingJo
video.duration = await getVideoStreamDuration(videoFilePath)
await video.save()

// We can remove the old audio file
const oldAudioFile = video.VideoFiles[0]
await video.removeWebVideoFile(oldAudioFile)
await oldAudioFile.destroy()
video.VideoFiles = []

await onVODWebVideoOrAudioMergeTranscodingJob({ video, videoFilePath, privatePayload })

logger.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type CreateOptions = {
resolution: number
fps: number
priority: number
deleteInputFileId: number | null
dependsOnRunnerJob?: MRunnerJob
}

Expand All @@ -41,7 +42,7 @@ export class VODWebVideoTranscodingJobHandler extends AbstractVODTranscodingJobH
}

const privatePayload: RunnerJobVODWebVideoTranscodingPrivatePayload = {
...pick(options, [ 'isNewVideo' ]),
...pick(options, [ 'isNewVideo', 'deleteInputFileId' ]),

videoUUID: video.uuid
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { MUserId, MVideoFile, MVideoFullLight, MVideoWithFileThumbnail } from '@
import { MRunnerJob } from '@server/types/models/runners/index.js'
import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS, hasAudioStream, isAudioFile } from '@peertube/peertube-ffmpeg'
import { getTranscodingJobPriority } from '../../transcoding-priority.js'
import { computeResolutionsToTranscode } from '../../transcoding-resolutions.js'
import { buildOriginalFileResolution, computeResolutionsToTranscode } from '../../transcoding-resolutions.js'
import { AbstractJobBuilder } from './abstract-job-builder.js'

/**
Expand Down Expand Up @@ -52,16 +52,23 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {
? VIDEO_TRANSCODING_FPS.AUDIO_MERGE // The first transcoding job will transcode to this FPS value
: await getVideoStreamFPS(videoFilePath, probe)

const maxResolution = await isAudioFile(videoFilePath, probe)
const isAudioInput = await isAudioFile(videoFilePath, probe)
const maxResolution = isAudioInput
? DEFAULT_AUDIO_RESOLUTION
: resolution
: buildOriginalFileResolution(resolution)

const fps = computeOutputFPS({ inputFPS, resolution: maxResolution })
const priority = await getTranscodingJobPriority({ user, type: 'vod', fallback: 0 })

const deleteInputFileId = isAudioInput || maxResolution !== resolution
? videoFile.id
: null

const jobPayload = { video, resolution: maxResolution, fps, isNewVideo, priority, deleteInputFileId }

const mainRunnerJob = videoFile.isAudio()
? await new VODAudioMergeTranscodingJobHandler().create({ video, resolution: maxResolution, fps, isNewVideo, priority })
: await new VODWebVideoTranscodingJobHandler().create({ video, resolution: maxResolution, fps, isNewVideo, priority })
? await new VODAudioMergeTranscodingJobHandler().create(jobPayload)
: await new VODWebVideoTranscodingJobHandler().create(jobPayload)

if (CONFIG.TRANSCODING.HLS.ENABLED === true) {
await new VODHLSTranscodingJobHandler().create({
Expand Down Expand Up @@ -110,12 +117,14 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {

logger.info('Manually creating transcoding jobs for %s.', transcodingType, { childrenResolutions, maxResolution })

const jobPayload = { video, resolution: maxResolution, fps: maxFPS, isNewVideo, priority, deleteInputFileId: null }

// Process the last resolution before the other ones to prevent concurrency issue
// Because low resolutions use the biggest one as ffmpeg input
const mainJob = transcodingType === 'hls'
// eslint-disable-next-line max-len
? await new VODHLSTranscodingJobHandler().create({ video, resolution: maxResolution, fps: maxFPS, isNewVideo, deleteWebVideoFiles: false, priority })
: await new VODWebVideoTranscodingJobHandler().create({ video, resolution: maxResolution, fps: maxFPS, isNewVideo, priority })
? await new VODHLSTranscodingJobHandler().create({ ...jobPayload, deleteWebVideoFiles: false })
: await new VODWebVideoTranscodingJobHandler().create(jobPayload)

for (const resolution of childrenResolutions) {
const dependsOnRunnerJob = mainJob
Expand All @@ -141,6 +150,7 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {
fps,
isNewVideo,
dependsOnRunnerJob,
deleteInputFileId: null,
priority: await getTranscodingJobPriority({ user, type: 'vod', fallback: 0 })
})
continue
Expand Down Expand Up @@ -180,6 +190,7 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {
fps,
isNewVideo,
dependsOnRunnerJob: mainRunnerJob,
deleteInputFileId: null,
priority: await getTranscodingJobPriority({ user, type: 'vod', fallback: 0 })
})
}
Expand Down

0 comments on commit d4f2149

Please sign in to comment.