import { logger } from 'packages/peertube-runner/shared/logger'
import {
RunnerJobLiveRTMPHLSTranscodingPayload,
- RunnerJobVideoEditionTranscodingPayload,
+ RunnerJobStudioTranscodingPayload,
RunnerJobVODAudioMergeTranscodingPayload,
RunnerJobVODHLSTranscodingPayload,
RunnerJobVODWebVideoTranscodingPayload
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
+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 }
tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(),
profile: 'default',
availableEncoders: {
- available: getAvailableEncoders(),
- encodersToTry: getEncodersToTry()
+ available: getDefaultAvailableEncoders(),
+ encodersToTry: getDefaultEncodersToTry()
},
logger: getTranscodingLogger()
}
export * from './common'
export * from './process-vod'
export * from './transcoding-logger'
-export * from './transcoding-profiles'
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,
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
tmpInputFilePath = outputPath
}
- const successBody: VideoEditionTranscodingSuccess = {
+ const successBody: VideoStudioTranscodingSuccess = {
videoFile: outputPath
}
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>) {
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)
+ }
}
const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`)
- await ffmpegVod.transcode({
- type: 'video',
-
- inputPath,
+ try {
+ await ffmpegVod.transcode({
+ type: 'video',
- outputPath,
+ inputPath,
- inputFileMutexReleaser: () => {},
+ outputPath,
- resolution: payload.output.resolution,
- fps: payload.output.fps
- })
+ inputFileMutexReleaser: () => {},
- const successBody: VODWebVideoTranscodingSuccess = {
- videoFile: outputPath
- }
+ resolution: payload.output.resolution,
+ fps: payload.output.fps
+ })
- await server.runnerJobs.success({
- jobToken: job.jobToken,
- jobUUID: job.uuid,
- runnerToken,
- payload: successBody
- })
+ const successBody: VODWebVideoTranscodingSuccess = {
+ videoFile: outputPath
+ }
- await remove(outputPath)
+ await server.runnerJobs.success({
+ jobToken: job.jobToken,
+ jobUUID: job.uuid,
+ runnerToken,
+ payload: successBody
+ })
+ } finally {
+ await remove(inputPath)
+ await remove(outputPath)
+ }
}
export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) {
const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken })
- await ffmpegVod.transcode({
- type: 'merge-audio',
-
- audioPath,
- inputPath,
+ try {
+ await ffmpegVod.transcode({
+ type: 'merge-audio',
- outputPath,
+ audioPath,
+ inputPath,
- inputFileMutexReleaser: () => {},
+ outputPath,
- resolution: payload.output.resolution,
- fps: payload.output.fps
- })
+ inputFileMutexReleaser: () => {},
- const successBody: VODAudioMergeTranscodingSuccess = {
- videoFile: outputPath
- }
+ resolution: payload.output.resolution,
+ fps: payload.output.fps
+ })
- await server.runnerJobs.success({
- jobToken: job.jobToken,
- jobUUID: job.uuid,
- runnerToken,
- payload: successBody
- })
+ const successBody: VODAudioMergeTranscodingSuccess = {
+ videoFile: outputPath
+ }
- await remove(outputPath)
+ await server.runnerJobs.success({
+ jobToken: job.jobToken,
+ jobUUID: job.uuid,
+ runnerToken,
+ payload: successBody
+ })
+ } finally {
+ await remove(audioPath)
+ await remove(inputPath)
+ await remove(outputPath)
+ }
}
RunnerJobLiveRTMPHLSTranscodingPayload,
RunnerJobPayload,
RunnerJobType,
- RunnerJobVideoEditionTranscodingPayload,
+ RunnerJobStudioTranscodingPayload,
RunnerJobVODAudioMergeTranscodingPayload,
RunnerJobVODHLSTranscodingPayload,
RunnerJobVODWebVideoTranscodingPayload,
'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' ])
asyncMiddleware(jobOfRunnerGetValidator),
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
runnerJobGetVideoStudioTaskFileValidator,
- getVideoEditionTaskFile
+ getVideoStudioTaskFile
)
// ---------------------------------------------------------------------------
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)
)
RunnerJobUpdateBody,
RunnerJobUpdatePayload,
UserRight,
- VideoEditionTranscodingSuccess,
+ VideoStudioTranscodingSuccess,
VODAudioMergeTranscodingSuccess,
VODHLSTranscodingSuccess,
VODWebVideoTranscodingSuccess
}
},
- 'video-edition-transcoding': (payload: VideoEditionTranscodingSuccess, files) => {
+ 'video-studio-transcoding': (payload: VideoStudioTranscodingSuccess, files) => {
return {
...payload,
RunnerJobSuccessPayload,
RunnerJobType,
RunnerJobUpdatePayload,
- VideoEditionTranscodingSuccess,
+ VideoStudioTranscodingSuccess,
VODAudioMergeTranscodingSuccess,
VODHLSTranscodingSuccess,
VODWebVideoTranscodingSuccess
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)
}
// ---------------------------------------------------------------------------
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)
}
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 })
}
)
}
-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))
}
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'
} 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
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)
RunnerJobSuccessPayload,
RunnerJobType,
RunnerJobUpdatePayload,
- RunnerJobVideoEditionTranscodingPayload,
- RunnerJobVideoEditionTranscodingPrivatePayload,
+ RunnerJobStudioTranscodingPayload,
+ RunnerJobVideoStudioTranscodingPrivatePayload,
RunnerJobVODAudioMergeTranscodingPayload,
RunnerJobVODAudioMergeTranscodingPrivatePayload,
RunnerJobVODHLSTranscodingPayload,
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> {
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'
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'
'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) {
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'
isVideoStudioTaskWatermark,
RunnerJobState,
RunnerJobUpdatePayload,
- RunnerJobVideoEditionTranscodingPayload,
- RunnerJobVideoEditionTranscodingPrivatePayload,
- VideoEditionTranscodingSuccess,
+ RunnerJobStudioTranscodingPayload,
+ RunnerJobVideoStudioTranscodingPrivatePayload,
+ VideoStudioTranscodingSuccess,
VideoState,
VideoStudioTaskPayload
} from '@shared/models'
}
// 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)
},
})
}
- 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,
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) {
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.',
}) {
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)
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
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[],
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`
- ]
-}
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}]`
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
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' ]
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)) {
RunnerJobState,
RunnerJobSuccessPayload,
RunnerJobUpdatePayload,
- RunnerJobVideoEditionTranscodingPayload,
+ RunnerJobStudioTranscodingPayload,
VideoPrivacy,
VideoStudioTaskIntro
} from '@shared/models'
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)
}
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,
})
})
- 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({
import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared'
import { buildAbsoluteFixturePath } from '@shared/core-utils'
import {
- RunnerJobVideoEditionTranscodingPayload,
- VideoEditionTranscodingSuccess,
+ RunnerJobStudioTranscodingPayload,
+ VideoStudioTranscodingSuccess,
VideoState,
VideoStudioTask,
VideoStudioTaskIntro
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
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)
})
})
- describe('HLS only video edition', function () {
+ describe('HLS only studio edition', function () {
before(async function () {
// Disable webtorrent
})
})
- describe('Object storage video edition', function () {
+ describe('Object storage studio edition', function () {
if (areMockObjectStorageTestsDisabled()) return
before(async function () {
return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
}
-export function getAvailableEncoders () {
+export function getDefaultAvailableEncoders () {
return {
vod: {
libx264: {
}
}
-export function getEncodersToTry () {
+export function getDefaultEncodersToTry () {
return {
vod: {
video: [ 'libx264' ],
export * from './ffmpeg-command-wrapper'
+export * from './ffmpeg-default-transcoding-profile'
export * from './ffmpeg-edition'
export * from './ffmpeg-images'
export * from './ffmpeg-live'
export type RunnerJobPayload =
RunnerJobVODPayload |
RunnerJobLiveRTMPHLSTranscodingPayload |
- RunnerJobVideoEditionTranscodingPayload
+ RunnerJobStudioTranscodingPayload
// ---------------------------------------------------------------------------
}
}
-export interface RunnerJobVideoEditionTranscodingPayload {
+export interface RunnerJobStudioTranscodingPayload {
input: {
videoFileUrl: string
}
export type RunnerJobPrivatePayload =
RunnerJobVODPrivatePayload |
RunnerJobLiveRTMPHLSTranscodingPrivatePayload |
- RunnerJobVideoEditionTranscodingPrivatePayload
+ RunnerJobVideoStudioTranscodingPrivatePayload
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-export interface RunnerJobVideoEditionTranscodingPrivatePayload {
+export interface RunnerJobVideoStudioTranscodingPrivatePayload {
videoUUID: string
originalTasks: VideoStudioTaskPayload[]
}
VODHLSTranscodingSuccess |
VODAudioMergeTranscodingSuccess |
LiveRTMPHLSTranscodingSuccess |
- VideoEditionTranscodingSuccess
+ VideoStudioTranscodingSuccess
export interface VODWebVideoTranscodingSuccess {
videoFile: Blob | string
}
-export interface VideoEditionTranscodingSuccess {
+export interface VideoStudioTranscodingSuccess {
videoFile: Blob | string
}
'vod-hls-transcoding' |
'vod-audio-merge-transcoding' |
'live-rtmp-hls-transcoding' |
- 'video-edition-transcoding'
+ 'video-studio-transcoding'