mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-02-16 18:25:09 -06:00
Prefer video studio instead of video edition
Clearer and easier to find in the project
This commit is contained in:
parent
5e47f6ab98
commit
ab14f0e0dc
@ -1,7 +1,7 @@
|
||||
import { logger } from 'packages/peertube-runner/shared/logger'
|
||||
import {
|
||||
RunnerJobLiveRTMPHLSTranscodingPayload,
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
RunnerJobVODAudioMergeTranscodingPayload,
|
||||
RunnerJobVODHLSTranscodingPayload,
|
||||
RunnerJobVODWebVideoTranscodingPayload
|
||||
@ -23,8 +23,8 @@ export async function processJob (options: ProcessOptions) {
|
||||
await processHLSTranscoding(options as ProcessOptions<RunnerJobVODHLSTranscodingPayload>)
|
||||
} else if (job.type === 'live-rtmp-hls-transcoding') {
|
||||
await new ProcessLiveRTMPHLSTranscoding(options as ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>).process()
|
||||
} else if (job.type === 'video-edition-transcoding') {
|
||||
await processStudioTranscoding(options as ProcessOptions<RunnerJobVideoEditionTranscodingPayload>)
|
||||
} else if (job.type === 'video-studio-transcoding') {
|
||||
await processStudioTranscoding(options as ProcessOptions<RunnerJobStudioTranscodingPayload>)
|
||||
} else {
|
||||
logger.error(`Unknown job ${job.type} to process`)
|
||||
return
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { remove } from 'fs-extra'
|
||||
import { throttle } from 'lodash'
|
||||
import { ConfigManager, downloadFile, logger } from 'packages/peertube-runner/shared'
|
||||
import { join } from 'path'
|
||||
import { buildUUID } from '@shared/extra-utils'
|
||||
import { FFmpegEdition, FFmpegLive, FFmpegVOD } from '@shared/ffmpeg'
|
||||
import { FFmpegEdition, FFmpegLive, FFmpegVOD, getDefaultAvailableEncoders, getDefaultEncodersToTry } from '@shared/ffmpeg'
|
||||
import { RunnerJob, RunnerJobPayload } from '@shared/models'
|
||||
import { PeerTubeServer } from '@shared/server-commands'
|
||||
import { getTranscodingLogger } from './transcoding-logger'
|
||||
import { getAvailableEncoders, getEncodersToTry } from './transcoding-profiles'
|
||||
import { remove } from 'fs-extra'
|
||||
|
||||
export type JobWithToken <T extends RunnerJobPayload = RunnerJobPayload> = RunnerJob<T> & { jobToken: string }
|
||||
|
||||
@ -92,8 +91,8 @@ function getCommonFFmpegOptions () {
|
||||
tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(),
|
||||
profile: 'default',
|
||||
availableEncoders: {
|
||||
available: getAvailableEncoders(),
|
||||
encodersToTry: getEncodersToTry()
|
||||
available: getDefaultAvailableEncoders(),
|
||||
encodersToTry: getDefaultEncodersToTry()
|
||||
},
|
||||
logger: getTranscodingLogger()
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export * from './common'
|
||||
export * from './process-vod'
|
||||
export * from './transcoding-logger'
|
||||
export * from './transcoding-profiles'
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { remove } from 'fs-extra'
|
||||
import { pick } from 'lodash'
|
||||
import { logger } from 'packages/peertube-runner/shared'
|
||||
import { extname, join } from 'path'
|
||||
import { join } from 'path'
|
||||
import { buildUUID } from '@shared/extra-utils'
|
||||
import {
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
VideoEditionTranscodingSuccess,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
VideoStudioTranscodingSuccess,
|
||||
VideoStudioTask,
|
||||
VideoStudioTaskCutPayload,
|
||||
VideoStudioTaskIntroPayload,
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import { ConfigManager } from '../../../shared/config-manager'
|
||||
import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common'
|
||||
|
||||
export async function processStudioTranscoding (options: ProcessOptions<RunnerJobVideoEditionTranscodingPayload>) {
|
||||
export async function processStudioTranscoding (options: ProcessOptions<RunnerJobStudioTranscodingPayload>) {
|
||||
const { server, job, runnerToken } = options
|
||||
const payload = job.payload
|
||||
|
||||
@ -43,7 +43,7 @@ export async function processStudioTranscoding (options: ProcessOptions<RunnerJo
|
||||
tmpInputFilePath = outputPath
|
||||
}
|
||||
|
||||
const successBody: VideoEditionTranscodingSuccess = {
|
||||
const successBody: VideoStudioTranscodingSuccess = {
|
||||
videoFile: outputPath
|
||||
}
|
||||
|
||||
@ -94,14 +94,18 @@ async function processAddIntroOutro (options: TaskProcessorOptions<VideoStudioTa
|
||||
|
||||
const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
||||
|
||||
return buildFFmpegEdition().addIntroOutro({
|
||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||
try {
|
||||
await buildFFmpegEdition().addIntroOutro({
|
||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||
|
||||
introOutroPath,
|
||||
type: task.name === 'add-intro'
|
||||
? 'intro'
|
||||
: 'outro'
|
||||
})
|
||||
introOutroPath,
|
||||
type: task.name === 'add-intro'
|
||||
? 'intro'
|
||||
: 'outro'
|
||||
})
|
||||
} finally {
|
||||
await remove(introOutroPath)
|
||||
}
|
||||
}
|
||||
|
||||
function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) {
|
||||
@ -124,15 +128,19 @@ async function processAddWatermark (options: TaskProcessorOptions<VideoStudioTas
|
||||
|
||||
const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
|
||||
|
||||
return buildFFmpegEdition().addWatermark({
|
||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||
try {
|
||||
await buildFFmpegEdition().addWatermark({
|
||||
...pick(options, [ 'inputPath', 'outputPath' ]),
|
||||
|
||||
watermarkPath,
|
||||
watermarkPath,
|
||||
|
||||
videoFilters: {
|
||||
watermarkSizeRatio: task.options.watermarkSizeRatio,
|
||||
horitonzalMarginRatio: task.options.horitonzalMarginRatio,
|
||||
verticalMarginRatio: task.options.verticalMarginRatio
|
||||
}
|
||||
})
|
||||
videoFilters: {
|
||||
watermarkSizeRatio: task.options.watermarkSizeRatio,
|
||||
horitonzalMarginRatio: task.options.horitonzalMarginRatio,
|
||||
verticalMarginRatio: task.options.verticalMarginRatio
|
||||
}
|
||||
})
|
||||
} finally {
|
||||
await remove(watermarkPath)
|
||||
}
|
||||
}
|
||||
|
@ -22,31 +22,34 @@ export async function processWebVideoTranscoding (options: ProcessOptions<Runner
|
||||
|
||||
const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`)
|
||||
|
||||
await ffmpegVod.transcode({
|
||||
type: 'video',
|
||||
try {
|
||||
await ffmpegVod.transcode({
|
||||
type: 'video',
|
||||
|
||||
inputPath,
|
||||
inputPath,
|
||||
|
||||
outputPath,
|
||||
outputPath,
|
||||
|
||||
inputFileMutexReleaser: () => {},
|
||||
inputFileMutexReleaser: () => {},
|
||||
|
||||
resolution: payload.output.resolution,
|
||||
fps: payload.output.fps
|
||||
})
|
||||
resolution: payload.output.resolution,
|
||||
fps: payload.output.fps
|
||||
})
|
||||
|
||||
const successBody: VODWebVideoTranscodingSuccess = {
|
||||
videoFile: outputPath
|
||||
const successBody: VODWebVideoTranscodingSuccess = {
|
||||
videoFile: outputPath
|
||||
}
|
||||
|
||||
await server.runnerJobs.success({
|
||||
jobToken: job.jobToken,
|
||||
jobUUID: job.uuid,
|
||||
runnerToken,
|
||||
payload: successBody
|
||||
})
|
||||
} finally {
|
||||
await remove(inputPath)
|
||||
await remove(outputPath)
|
||||
}
|
||||
|
||||
await server.runnerJobs.success({
|
||||
jobToken: job.jobToken,
|
||||
jobUUID: job.uuid,
|
||||
runnerToken,
|
||||
payload: successBody
|
||||
})
|
||||
|
||||
await remove(outputPath)
|
||||
}
|
||||
|
||||
export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) {
|
||||
@ -105,30 +108,34 @@ export async function processAudioMergeTranscoding (options: ProcessOptions<Runn
|
||||
|
||||
const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken })
|
||||
|
||||
await ffmpegVod.transcode({
|
||||
type: 'merge-audio',
|
||||
try {
|
||||
await ffmpegVod.transcode({
|
||||
type: 'merge-audio',
|
||||
|
||||
audioPath,
|
||||
inputPath,
|
||||
audioPath,
|
||||
inputPath,
|
||||
|
||||
outputPath,
|
||||
outputPath,
|
||||
|
||||
inputFileMutexReleaser: () => {},
|
||||
inputFileMutexReleaser: () => {},
|
||||
|
||||
resolution: payload.output.resolution,
|
||||
fps: payload.output.fps
|
||||
})
|
||||
resolution: payload.output.resolution,
|
||||
fps: payload.output.fps
|
||||
})
|
||||
|
||||
const successBody: VODAudioMergeTranscodingSuccess = {
|
||||
videoFile: outputPath
|
||||
const successBody: VODAudioMergeTranscodingSuccess = {
|
||||
videoFile: outputPath
|
||||
}
|
||||
|
||||
await server.runnerJobs.success({
|
||||
jobToken: job.jobToken,
|
||||
jobUUID: job.uuid,
|
||||
runnerToken,
|
||||
payload: successBody
|
||||
})
|
||||
} finally {
|
||||
await remove(audioPath)
|
||||
await remove(inputPath)
|
||||
await remove(outputPath)
|
||||
}
|
||||
|
||||
await server.runnerJobs.success({
|
||||
jobToken: job.jobToken,
|
||||
jobUUID: job.uuid,
|
||||
runnerToken,
|
||||
payload: successBody
|
||||
})
|
||||
|
||||
await remove(outputPath)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
RunnerJobLiveRTMPHLSTranscodingPayload,
|
||||
RunnerJobPayload,
|
||||
RunnerJobType,
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
RunnerJobVODAudioMergeTranscodingPayload,
|
||||
RunnerJobVODHLSTranscodingPayload,
|
||||
RunnerJobVODWebVideoTranscodingPayload,
|
||||
@ -22,7 +22,7 @@ const supportedMatrix = {
|
||||
'live-rtmp-hls-transcoding': (_payload: RunnerJobLiveRTMPHLSTranscodingPayload) => {
|
||||
return true
|
||||
},
|
||||
'video-edition-transcoding': (payload: RunnerJobVideoEditionTranscodingPayload) => {
|
||||
'video-studio-transcoding': (payload: RunnerJobStudioTranscodingPayload) => {
|
||||
const tasks = payload?.tasks
|
||||
const supported = new Set<VideoStudioTaskPayload['name']>([ 'add-intro', 'add-outro', 'add-watermark', 'cut' ])
|
||||
|
||||
|
@ -31,7 +31,7 @@ runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-file
|
||||
asyncMiddleware(jobOfRunnerGetValidator),
|
||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||
runnerJobGetVideoStudioTaskFileValidator,
|
||||
getVideoEditionTaskFile
|
||||
getVideoStudioTaskFile
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -94,14 +94,14 @@ function getMaxQualityVideoPreview (req: express.Request, res: express.Response)
|
||||
return res.sendFile(file.getPath())
|
||||
}
|
||||
|
||||
function getVideoEditionTaskFile (req: express.Request, res: express.Response) {
|
||||
function getVideoStudioTaskFile (req: express.Request, res: express.Response) {
|
||||
const runnerJob = res.locals.runnerJob
|
||||
const runner = runnerJob.Runner
|
||||
const video = res.locals.videoAll
|
||||
const filename = req.params.filename
|
||||
|
||||
logger.info(
|
||||
'Get video edition task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
|
||||
'Get video studio task file %s of video %s of job %s for runner %s', filename, video.uuid, runnerJob.uuid, runner.name,
|
||||
lTags(runner.name, runnerJob.id, runnerJob.type)
|
||||
)
|
||||
|
||||
|
@ -42,7 +42,7 @@ import {
|
||||
RunnerJobUpdateBody,
|
||||
RunnerJobUpdatePayload,
|
||||
UserRight,
|
||||
VideoEditionTranscodingSuccess,
|
||||
VideoStudioTranscodingSuccess,
|
||||
VODAudioMergeTranscodingSuccess,
|
||||
VODHLSTranscodingSuccess,
|
||||
VODWebVideoTranscodingSuccess
|
||||
@ -300,7 +300,7 @@ const jobSuccessPayloadBuilders: {
|
||||
}
|
||||
},
|
||||
|
||||
'video-edition-transcoding': (payload: VideoEditionTranscodingSuccess, files) => {
|
||||
'video-studio-transcoding': (payload: VideoStudioTranscodingSuccess, files) => {
|
||||
return {
|
||||
...payload,
|
||||
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
RunnerJobSuccessPayload,
|
||||
RunnerJobType,
|
||||
RunnerJobUpdatePayload,
|
||||
VideoEditionTranscodingSuccess,
|
||||
VideoStudioTranscodingSuccess,
|
||||
VODAudioMergeTranscodingSuccess,
|
||||
VODHLSTranscodingSuccess,
|
||||
VODWebVideoTranscodingSuccess
|
||||
@ -25,7 +25,7 @@ function isRunnerJobSuccessPayloadValid (value: RunnerJobSuccessPayload, type: R
|
||||
isRunnerJobVODHLSResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
||||
isRunnerJobVODAudioMergeResultPayloadValid(value as VODHLSTranscodingSuccess, type, files) ||
|
||||
isRunnerJobLiveRTMPHLSResultPayloadValid(value as LiveRTMPHLSTranscodingSuccess, type) ||
|
||||
isRunnerJobVideoEditionResultPayloadValid(value as VideoEditionTranscodingSuccess, type, files)
|
||||
isRunnerJobVideoStudioResultPayloadValid(value as VideoStudioTranscodingSuccess, type, files)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -37,7 +37,7 @@ function isRunnerJobProgressValid (value: string) {
|
||||
function isRunnerJobUpdatePayloadValid (value: RunnerJobUpdatePayload, type: RunnerJobType, files: UploadFilesForCheck) {
|
||||
return isRunnerJobVODWebVideoUpdatePayloadValid(value, type, files) ||
|
||||
isRunnerJobVODHLSUpdatePayloadValid(value, type, files) ||
|
||||
isRunnerJobVideoEditionUpdatePayloadValid(value, type, files) ||
|
||||
isRunnerJobVideoStudioUpdatePayloadValid(value, type, files) ||
|
||||
isRunnerJobVODAudioMergeUpdatePayloadValid(value, type, files) ||
|
||||
isRunnerJobLiveRTMPHLSUpdatePayloadValid(value, type, files)
|
||||
}
|
||||
@ -105,12 +105,12 @@ function isRunnerJobLiveRTMPHLSResultPayloadValid (
|
||||
return type === 'live-rtmp-hls-transcoding' && (!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
||||
}
|
||||
|
||||
function isRunnerJobVideoEditionResultPayloadValid (
|
||||
_value: VideoEditionTranscodingSuccess,
|
||||
function isRunnerJobVideoStudioResultPayloadValid (
|
||||
_value: VideoStudioTranscodingSuccess,
|
||||
type: RunnerJobType,
|
||||
files: UploadFilesForCheck
|
||||
) {
|
||||
return type === 'video-edition-transcoding' &&
|
||||
return type === 'video-studio-transcoding' &&
|
||||
isFileValid({ files, field: 'payload[videoFile]', mimeTypeRegex: null, maxSize: null })
|
||||
}
|
||||
|
||||
@ -177,11 +177,11 @@ function isRunnerJobLiveRTMPHLSUpdatePayloadValid (
|
||||
)
|
||||
}
|
||||
|
||||
function isRunnerJobVideoEditionUpdatePayloadValid (
|
||||
function isRunnerJobVideoStudioUpdatePayloadValid (
|
||||
value: RunnerJobUpdatePayload,
|
||||
type: RunnerJobType,
|
||||
_files: UploadFilesForCheck
|
||||
) {
|
||||
return type === 'video-edition-transcoding' &&
|
||||
return type === 'video-studio-transcoding' &&
|
||||
(!value || (typeof value === 'object' && Object.keys(value).length === 0))
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { CONFIG } from '@server/initializers/config'
|
||||
import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/default-transcoding-profiles'
|
||||
import { isAbleToUploadVideo } from '@server/lib/user'
|
||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||
import { approximateIntroOutroAdditionalSize, onVideoEditionEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||
import { approximateIntroOutroAdditionalSize, onVideoStudioEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||
import { UserModel } from '@server/models/user/user'
|
||||
import { VideoModel } from '@server/models/video/video'
|
||||
import { MVideo, MVideoFullLight } from '@server/types/models'
|
||||
@ -24,7 +24,7 @@ import {
|
||||
} from '@shared/models'
|
||||
import { logger, loggerTagsFactory } from '../../../helpers/logger'
|
||||
|
||||
const lTagsBase = loggerTagsFactory('video-edition')
|
||||
const lTagsBase = loggerTagsFactory('video-studio')
|
||||
|
||||
async function processVideoStudioEdition (job: Job) {
|
||||
const payload = job.data as VideoStudioEditionPayload
|
||||
@ -74,7 +74,7 @@ async function processVideoStudioEdition (job: Job) {
|
||||
|
||||
logger.info('Video edition ended for video %s.', video.uuid, lTags)
|
||||
|
||||
await onVideoEditionEnded({ video, editionResultPath, tasks: payload.tasks })
|
||||
await onVideoStudioEnded({ video, editionResultPath, tasks: payload.tasks })
|
||||
} catch (err) {
|
||||
await safeCleanupStudioTMPFiles(payload.tasks)
|
||||
|
||||
|
@ -15,8 +15,8 @@ import {
|
||||
RunnerJobSuccessPayload,
|
||||
RunnerJobType,
|
||||
RunnerJobUpdatePayload,
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
RunnerJobVideoEditionTranscodingPrivatePayload,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
RunnerJobVideoStudioTranscodingPrivatePayload,
|
||||
RunnerJobVODAudioMergeTranscodingPayload,
|
||||
RunnerJobVODAudioMergeTranscodingPrivatePayload,
|
||||
RunnerJobVODHLSTranscodingPayload,
|
||||
@ -47,9 +47,9 @@ type CreateRunnerJobArg =
|
||||
privatePayload: RunnerJobLiveRTMPHLSTranscodingPrivatePayload
|
||||
} |
|
||||
{
|
||||
type: Extract<RunnerJobType, 'video-edition-transcoding'>
|
||||
payload: RunnerJobVideoEditionTranscodingPayload
|
||||
privatePayload: RunnerJobVideoEditionTranscodingPrivatePayload
|
||||
type: Extract<RunnerJobType, 'video-studio-transcoding'>
|
||||
payload: RunnerJobStudioTranscodingPayload
|
||||
privatePayload: RunnerJobVideoStudioTranscodingPrivatePayload
|
||||
}
|
||||
|
||||
export abstract class AbstractJobHandler <C, U extends RunnerJobUpdatePayload, S extends RunnerJobSuccessPayload> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
export * from './abstract-job-handler'
|
||||
export * from './live-rtmp-hls-transcoding-job-handler'
|
||||
export * from './runner-job-handlers'
|
||||
export * from './video-edition-transcoding-job-handler'
|
||||
export * from './video-studio-transcoding-job-handler'
|
||||
export * from './vod-audio-merge-transcoding-job-handler'
|
||||
export * from './vod-hls-transcoding-job-handler'
|
||||
export * from './vod-web-video-transcoding-job-handler'
|
||||
|
@ -2,7 +2,7 @@ import { MRunnerJob } from '@server/types/models/runners'
|
||||
import { RunnerJobSuccessPayload, RunnerJobType, RunnerJobUpdatePayload } from '@shared/models'
|
||||
import { AbstractJobHandler } from './abstract-job-handler'
|
||||
import { LiveRTMPHLSTranscodingJobHandler } from './live-rtmp-hls-transcoding-job-handler'
|
||||
import { VideoEditionTranscodingJobHandler } from './video-edition-transcoding-job-handler'
|
||||
import { VideoStudioTranscodingJobHandler } from './video-studio-transcoding-job-handler'
|
||||
import { VODAudioMergeTranscodingJobHandler } from './vod-audio-merge-transcoding-job-handler'
|
||||
import { VODHLSTranscodingJobHandler } from './vod-hls-transcoding-job-handler'
|
||||
import { VODWebVideoTranscodingJobHandler } from './vod-web-video-transcoding-job-handler'
|
||||
@ -12,7 +12,7 @@ const processors: Record<RunnerJobType, new() => AbstractJobHandler<unknown, Run
|
||||
'vod-hls-transcoding': VODHLSTranscodingJobHandler,
|
||||
'vod-audio-merge-transcoding': VODAudioMergeTranscodingJobHandler,
|
||||
'live-rtmp-hls-transcoding': LiveRTMPHLSTranscodingJobHandler,
|
||||
'video-edition-transcoding': VideoEditionTranscodingJobHandler
|
||||
'video-studio-transcoding': VideoStudioTranscodingJobHandler
|
||||
}
|
||||
|
||||
export function getRunnerJobHandlerClass (job: MRunnerJob) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
import { basename } from 'path'
|
||||
import { logger } from '@server/helpers/logger'
|
||||
import { onVideoEditionEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||
import { onVideoStudioEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio'
|
||||
import { MVideo } from '@server/types/models'
|
||||
import { MRunnerJob } from '@server/types/models/runners'
|
||||
import { buildUUID } from '@shared/extra-utils'
|
||||
@ -11,9 +11,9 @@ import {
|
||||
isVideoStudioTaskWatermark,
|
||||
RunnerJobState,
|
||||
RunnerJobUpdatePayload,
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
RunnerJobVideoEditionTranscodingPrivatePayload,
|
||||
VideoEditionTranscodingSuccess,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
RunnerJobVideoStudioTranscodingPrivatePayload,
|
||||
VideoStudioTranscodingSuccess,
|
||||
VideoState,
|
||||
VideoStudioTaskPayload
|
||||
} from '@shared/models'
|
||||
@ -28,13 +28,13 @@ type CreateOptions = {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<CreateOptions, RunnerJobUpdatePayload, VideoEditionTranscodingSuccess> {
|
||||
export class VideoStudioTranscodingJobHandler extends AbstractJobHandler<CreateOptions, RunnerJobUpdatePayload, VideoStudioTranscodingSuccess> {
|
||||
|
||||
async create (options: CreateOptions) {
|
||||
const { video, priority, tasks } = options
|
||||
|
||||
const jobUUID = buildUUID()
|
||||
const payload: RunnerJobVideoEditionTranscodingPayload = {
|
||||
const payload: RunnerJobStudioTranscodingPayload = {
|
||||
input: {
|
||||
videoFileUrl: generateRunnerTranscodingVideoInputFileUrl(jobUUID, video.uuid)
|
||||
},
|
||||
@ -67,13 +67,13 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
||||
})
|
||||
}
|
||||
|
||||
const privatePayload: RunnerJobVideoEditionTranscodingPrivatePayload = {
|
||||
const privatePayload: RunnerJobVideoStudioTranscodingPrivatePayload = {
|
||||
videoUUID: video.uuid,
|
||||
originalTasks: tasks
|
||||
}
|
||||
|
||||
const job = await this.createRunnerJob({
|
||||
type: 'video-edition-transcoding',
|
||||
type: 'video-studio-transcoding',
|
||||
jobUUID,
|
||||
payload,
|
||||
privatePayload,
|
||||
@ -103,10 +103,10 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
||||
|
||||
protected async specificComplete (options: {
|
||||
runnerJob: MRunnerJob
|
||||
resultPayload: VideoEditionTranscodingSuccess
|
||||
resultPayload: VideoStudioTranscodingSuccess
|
||||
}) {
|
||||
const { runnerJob, resultPayload } = options
|
||||
const privatePayload = runnerJob.privatePayload as RunnerJobVideoEditionTranscodingPrivatePayload
|
||||
const privatePayload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload
|
||||
|
||||
const video = await loadTranscodingRunnerVideo(runnerJob, this.lTags)
|
||||
if (!video) {
|
||||
@ -116,7 +116,7 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
||||
|
||||
const videoFilePath = resultPayload.videoFile as string
|
||||
|
||||
await onVideoEditionEnded({ video, editionResultPath: videoFilePath, tasks: privatePayload.originalTasks })
|
||||
await onVideoStudioEnded({ video, editionResultPath: videoFilePath, tasks: privatePayload.originalTasks })
|
||||
|
||||
logger.info(
|
||||
'Runner video edition transcoding job %s for %s ended.',
|
||||
@ -146,7 +146,7 @@ export class VideoEditionTranscodingJobHandler extends AbstractJobHandler<Create
|
||||
}) {
|
||||
const { runnerJob } = options
|
||||
|
||||
const payload = runnerJob.privatePayload as RunnerJobVideoEditionTranscodingPrivatePayload
|
||||
const payload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload
|
||||
await safeCleanupStudioTMPFiles(payload.originalTasks)
|
||||
|
||||
const video = await loadTranscodingRunnerVideo(options.runnerJob, this.lTags)
|
@ -1,88 +1,7 @@
|
||||
|
||||
import { logger } from '@server/helpers/logger'
|
||||
import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils'
|
||||
import { buildStreamSuffix, FFmpegCommandWrapper, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg'
|
||||
import { AvailableEncoders, EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models'
|
||||
import { canDoQuickAudioTranscode } from './transcoding-quick-transcode'
|
||||
|
||||
/**
|
||||
*
|
||||
* Available encoders and profiles for the transcoding jobs
|
||||
* These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile
|
||||
*
|
||||
* Resources:
|
||||
* * https://slhck.info/video/2017/03/01/rate-control.html
|
||||
* * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default builders
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
|
||||
const { fps, inputRatio, inputBitrate, resolution } = options
|
||||
|
||||
// TODO: remove in 4.2, fps is not optional anymore
|
||||
if (!fps) return { outputOptions: [ ] }
|
||||
|
||||
const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
|
||||
|
||||
return {
|
||||
outputOptions: [
|
||||
...getCommonOutputOptions(targetBitrate),
|
||||
|
||||
`-r ${fps}`
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
|
||||
const { streamNum, fps, inputBitrate, inputRatio, resolution } = options
|
||||
|
||||
const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
|
||||
|
||||
return {
|
||||
outputOptions: [
|
||||
...getCommonOutputOptions(targetBitrate, streamNum),
|
||||
|
||||
`${buildStreamSuffix('-r:v', streamNum)} ${fps}`,
|
||||
`${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => {
|
||||
const probe = await ffprobePromise(input)
|
||||
|
||||
if (canCopyAudio && await canDoQuickAudioTranscode(input, probe)) {
|
||||
logger.debug('Copy audio stream %s by AAC encoder.', input)
|
||||
return { copy: true, outputOptions: [ ] }
|
||||
}
|
||||
|
||||
const parsedAudio = await getAudioStream(input, probe)
|
||||
|
||||
// We try to reduce the ceiling bitrate by making rough matches of bitrates
|
||||
// Of course this is far from perfect, but it might save some space in the end
|
||||
|
||||
const audioCodecName = parsedAudio.audioStream['codec_name']
|
||||
|
||||
const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
|
||||
|
||||
logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName })
|
||||
|
||||
// Force stereo as it causes some issues with HLS playback in Chrome
|
||||
const base = [ '-channel_layout', 'stereo' ]
|
||||
|
||||
if (bitrate !== -1) {
|
||||
return { outputOptions: base.concat([ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ]) }
|
||||
}
|
||||
|
||||
return { outputOptions: base }
|
||||
}
|
||||
|
||||
const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
|
||||
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
|
||||
}
|
||||
import { FFmpegCommandWrapper, getDefaultAvailableEncoders } from '@shared/ffmpeg'
|
||||
import { AvailableEncoders, EncoderOptionsBuilder } from '@shared/models'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Profile manager to get and change default profiles
|
||||
@ -97,27 +16,7 @@ class VideoTranscodingProfilesManager {
|
||||
live: this.buildDefaultEncodersPriorities()
|
||||
}
|
||||
|
||||
private readonly availableEncoders = {
|
||||
vod: {
|
||||
libx264: {
|
||||
default: defaultX264VODOptionsBuilder
|
||||
},
|
||||
aac: {
|
||||
default: defaultAACOptionsBuilder
|
||||
},
|
||||
libfdk_aac: {
|
||||
default: defaultLibFDKAACVODOptionsBuilder
|
||||
}
|
||||
},
|
||||
live: {
|
||||
libx264: {
|
||||
default: defaultX264LiveOptionsBuilder
|
||||
},
|
||||
aac: {
|
||||
default: defaultAACOptionsBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly availableEncoders = getDefaultAvailableEncoders()
|
||||
|
||||
private availableProfiles = {
|
||||
vod: [] as string[],
|
||||
@ -242,41 +141,3 @@ class VideoTranscodingProfilesManager {
|
||||
export {
|
||||
VideoTranscodingProfilesManager
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function getTargetBitrate (options: {
|
||||
inputBitrate: number
|
||||
resolution: VideoResolution
|
||||
ratio: number
|
||||
fps: number
|
||||
}) {
|
||||
const { inputBitrate, resolution, ratio, fps } = options
|
||||
|
||||
const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio }))
|
||||
const limit = getMinLimitBitrate({ resolution, fps, ratio })
|
||||
|
||||
return Math.max(limit, capped)
|
||||
}
|
||||
|
||||
function capBitrate (inputBitrate: number, targetBitrate: number) {
|
||||
if (!inputBitrate) return targetBitrate
|
||||
|
||||
// Add 30% margin to input bitrate
|
||||
const inputBitrateWithMargin = inputBitrate + (inputBitrate * 0.3)
|
||||
|
||||
return Math.min(targetBitrate, inputBitrateWithMargin)
|
||||
}
|
||||
|
||||
function getCommonOutputOptions (targetBitrate: number, streamNum?: number) {
|
||||
return [
|
||||
`-preset veryfast`,
|
||||
`${buildStreamSuffix('-maxrate:v', streamNum)} ${targetBitrate}`,
|
||||
`${buildStreamSuffix('-bufsize:v', streamNum)} ${targetBitrate * 2}`,
|
||||
|
||||
// NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
|
||||
`-b_strategy 1`,
|
||||
// NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16
|
||||
`-bf 16`
|
||||
]
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ import { getVideoStreamDuration } from '@shared/ffmpeg'
|
||||
import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@shared/models'
|
||||
import { federateVideoIfNeeded } from './activitypub/videos'
|
||||
import { JobQueue } from './job-queue'
|
||||
import { VideoEditionTranscodingJobHandler } from './runners'
|
||||
import { VideoStudioTranscodingJobHandler } from './runners'
|
||||
import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job'
|
||||
import { getTranscodingJobPriority } from './transcoding/transcoding-priority'
|
||||
import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file'
|
||||
import { VideoPathManager } from './video-path-manager'
|
||||
|
||||
const lTags = loggerTagsFactory('video-edition')
|
||||
const lTags = loggerTagsFactory('video-studio')
|
||||
|
||||
export function buildTaskFileFieldname (indice: number, fieldName = 'file') {
|
||||
return `tasks[${indice}][options][${fieldName}]`
|
||||
@ -78,14 +78,14 @@ export async function createVideoStudioJob (options: {
|
||||
const priority = await getTranscodingJobPriority({ user, type: 'studio', fallback: 0 })
|
||||
|
||||
if (CONFIG.VIDEO_STUDIO.REMOTE_RUNNERS.ENABLED) {
|
||||
await new VideoEditionTranscodingJobHandler().create({ video, tasks: payload.tasks, priority })
|
||||
await new VideoStudioTranscodingJobHandler().create({ video, tasks: payload.tasks, priority })
|
||||
return
|
||||
}
|
||||
|
||||
await JobQueue.Instance.createJob({ type: 'video-studio-edition', payload, priority })
|
||||
}
|
||||
|
||||
export async function onVideoEditionEnded (options: {
|
||||
export async function onVideoStudioEnded (options: {
|
||||
editionResultPath: string
|
||||
tasks: VideoStudioTaskPayload[]
|
||||
video: MVideoFullLight
|
||||
|
@ -2,7 +2,7 @@ import express from 'express'
|
||||
import { param } from 'express-validator'
|
||||
import { basename } from 'path'
|
||||
import { isSafeFilename } from '@server/helpers/custom-validators/misc'
|
||||
import { hasVideoStudioTaskFile, HttpStatusCode, RunnerJobVideoEditionTranscodingPayload } from '@shared/models'
|
||||
import { hasVideoStudioTaskFile, HttpStatusCode, RunnerJobStudioTranscodingPayload } from '@shared/models'
|
||||
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
|
||||
|
||||
const tags = [ 'runner' ]
|
||||
@ -37,7 +37,7 @@ export const runnerJobGetVideoStudioTaskFileValidator = [
|
||||
|
||||
const filename = req.params.filename
|
||||
|
||||
const payload = res.locals.runnerJob.payload as RunnerJobVideoEditionTranscodingPayload
|
||||
const payload = res.locals.runnerJob.payload as RunnerJobStudioTranscodingPayload
|
||||
|
||||
const found = Array.isArray(payload?.tasks) && payload.tasks.some(t => {
|
||||
if (hasVideoStudioTaskFile(t)) {
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
RunnerJobState,
|
||||
RunnerJobSuccessPayload,
|
||||
RunnerJobUpdatePayload,
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
VideoPrivacy,
|
||||
VideoStudioTaskIntro
|
||||
} from '@shared/models'
|
||||
@ -404,10 +404,10 @@ describe('Test managing runners', function () {
|
||||
tasks: VideoStudioCommand.getComplexTask()
|
||||
})
|
||||
|
||||
const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-edition-transcoding' })
|
||||
const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-studio-transcoding' })
|
||||
studioAcceptedJob = job
|
||||
|
||||
const tasks = (job.payload as RunnerJobVideoEditionTranscodingPayload).tasks
|
||||
const tasks = (job.payload as RunnerJobStudioTranscodingPayload).tasks
|
||||
const fileUrl = (tasks.find(t => isVideoStudioTaskIntro(t)) as VideoStudioTaskIntro).options.file as string
|
||||
studioFile = basename(fileUrl)
|
||||
}
|
||||
@ -787,7 +787,7 @@ describe('Test managing runners', function () {
|
||||
|
||||
describe('Video studio', function () {
|
||||
|
||||
it('Should fail with an invalid video edition transcoding payload', async function () {
|
||||
it('Should fail with an invalid video studio transcoding payload', async function () {
|
||||
await server.runnerJobs.success({
|
||||
jobUUID: studioAcceptedJob.uuid,
|
||||
jobToken: studioAcceptedJob.jobToken,
|
||||
@ -849,7 +849,7 @@ describe('Test managing runners', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Video edition tasks file routes', function () {
|
||||
describe('Video studio tasks file routes', function () {
|
||||
|
||||
it('Should fail with an invalid studio filename', async function () {
|
||||
await fetchStudioFiles({
|
||||
|
@ -5,8 +5,8 @@ import { readFile } from 'fs-extra'
|
||||
import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared'
|
||||
import { buildAbsoluteFixturePath } from '@shared/core-utils'
|
||||
import {
|
||||
RunnerJobVideoEditionTranscodingPayload,
|
||||
VideoEditionTranscodingSuccess,
|
||||
RunnerJobStudioTranscodingPayload,
|
||||
VideoStudioTranscodingSuccess,
|
||||
VideoState,
|
||||
VideoStudioTask,
|
||||
VideoStudioTaskIntro
|
||||
@ -121,10 +121,10 @@ describe('Test runner video studio transcoding', function () {
|
||||
await checkVideoDuration(server, videoUUID, 5)
|
||||
}
|
||||
|
||||
const { job } = await servers[0].runnerJobs.accept<RunnerJobVideoEditionTranscodingPayload>({ runnerToken, jobUUID })
|
||||
const { job } = await servers[0].runnerJobs.accept<RunnerJobStudioTranscodingPayload>({ runnerToken, jobUUID })
|
||||
const jobToken = job.jobToken
|
||||
|
||||
expect(job.type === 'video-edition-transcoding')
|
||||
expect(job.type === 'video-studio-transcoding')
|
||||
expect(job.payload.input.videoFileUrl).to.exist
|
||||
|
||||
// Check video input file
|
||||
@ -150,7 +150,7 @@ describe('Test runner video studio transcoding', function () {
|
||||
expect(body).to.deep.equal(inputFile)
|
||||
}
|
||||
|
||||
const payload: VideoEditionTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' }
|
||||
const payload: VideoStudioTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' }
|
||||
await servers[0].runnerJobs.success({ runnerToken, jobUUID, jobToken, payload })
|
||||
|
||||
await waitJobs(servers)
|
||||
|
@ -270,7 +270,7 @@ describe('Test video studio', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('HLS only video edition', function () {
|
||||
describe('HLS only studio edition', function () {
|
||||
|
||||
before(async function () {
|
||||
// Disable webtorrent
|
||||
@ -300,7 +300,7 @@ describe('Test video studio', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Object storage video edition', function () {
|
||||
describe('Object storage studio edition', function () {
|
||||
if (areMockObjectStorageTestsDisabled()) return
|
||||
|
||||
before(async function () {
|
||||
|
@ -57,7 +57,7 @@ const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum })
|
||||
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
|
||||
}
|
||||
|
||||
export function getAvailableEncoders () {
|
||||
export function getDefaultAvailableEncoders () {
|
||||
return {
|
||||
vod: {
|
||||
libx264: {
|
||||
@ -81,7 +81,7 @@ export function getAvailableEncoders () {
|
||||
}
|
||||
}
|
||||
|
||||
export function getEncodersToTry () {
|
||||
export function getDefaultEncodersToTry () {
|
||||
return {
|
||||
vod: {
|
||||
video: [ 'libx264' ],
|
@ -1,4 +1,5 @@
|
||||
export * from './ffmpeg-command-wrapper'
|
||||
export * from './ffmpeg-default-transcoding-profile'
|
||||
export * from './ffmpeg-edition'
|
||||
export * from './ffmpeg-images'
|
||||
export * from './ffmpeg-live'
|
||||
|
@ -8,7 +8,7 @@ export type RunnerJobVODPayload =
|
||||
export type RunnerJobPayload =
|
||||
RunnerJobVODPayload |
|
||||
RunnerJobLiveRTMPHLSTranscodingPayload |
|
||||
RunnerJobVideoEditionTranscodingPayload
|
||||
RunnerJobStudioTranscodingPayload
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -46,7 +46,7 @@ export interface RunnerJobVODAudioMergeTranscodingPayload {
|
||||
}
|
||||
}
|
||||
|
||||
export interface RunnerJobVideoEditionTranscodingPayload {
|
||||
export interface RunnerJobStudioTranscodingPayload {
|
||||
input: {
|
||||
videoFileUrl: string
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export type RunnerJobVODPrivatePayload =
|
||||
export type RunnerJobPrivatePayload =
|
||||
RunnerJobVODPrivatePayload |
|
||||
RunnerJobLiveRTMPHLSTranscodingPrivatePayload |
|
||||
RunnerJobVideoEditionTranscodingPrivatePayload
|
||||
RunnerJobVideoStudioTranscodingPrivatePayload
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -38,7 +38,7 @@ export interface RunnerJobLiveRTMPHLSTranscodingPrivatePayload {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface RunnerJobVideoEditionTranscodingPrivatePayload {
|
||||
export interface RunnerJobVideoStudioTranscodingPrivatePayload {
|
||||
videoUUID: string
|
||||
originalTasks: VideoStudioTaskPayload[]
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ export type RunnerJobSuccessPayload =
|
||||
VODHLSTranscodingSuccess |
|
||||
VODAudioMergeTranscodingSuccess |
|
||||
LiveRTMPHLSTranscodingSuccess |
|
||||
VideoEditionTranscodingSuccess
|
||||
VideoStudioTranscodingSuccess
|
||||
|
||||
export interface VODWebVideoTranscodingSuccess {
|
||||
videoFile: Blob | string
|
||||
@ -31,7 +31,7 @@ export interface LiveRTMPHLSTranscodingSuccess {
|
||||
|
||||
}
|
||||
|
||||
export interface VideoEditionTranscodingSuccess {
|
||||
export interface VideoStudioTranscodingSuccess {
|
||||
videoFile: Blob | string
|
||||
}
|
||||
|
||||
|
@ -3,4 +3,4 @@ export type RunnerJobType =
|
||||
'vod-hls-transcoding' |
|
||||
'vod-audio-merge-transcoding' |
|
||||
'live-rtmp-hls-transcoding' |
|
||||
'video-edition-transcoding'
|
||||
'video-studio-transcoding'
|
||||
|
Loading…
Reference in New Issue
Block a user