aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/peertube-runner/server/process
diff options
context:
space:
mode:
Diffstat (limited to 'packages/peertube-runner/server/process')
-rw-r--r--packages/peertube-runner/server/process/process.ts6
-rw-r--r--packages/peertube-runner/server/process/shared/common.ts9
-rw-r--r--packages/peertube-runner/server/process/shared/index.ts1
-rw-r--r--packages/peertube-runner/server/process/shared/process-studio.ts50
-rw-r--r--packages/peertube-runner/server/process/shared/process-vod.ts85
-rw-r--r--packages/peertube-runner/server/process/shared/transcoding-profiles.ts134
6 files changed, 82 insertions, 203 deletions
diff --git a/packages/peertube-runner/server/process/process.ts b/packages/peertube-runner/server/process/process.ts
index ef231cb38..1caafda8c 100644
--- a/packages/peertube-runner/server/process/process.ts
+++ b/packages/peertube-runner/server/process/process.ts
@@ -1,7 +1,7 @@
1import { logger } from 'packages/peertube-runner/shared/logger' 1import { logger } from 'packages/peertube-runner/shared/logger'
2import { 2import {
3 RunnerJobLiveRTMPHLSTranscodingPayload, 3 RunnerJobLiveRTMPHLSTranscodingPayload,
4 RunnerJobVideoEditionTranscodingPayload, 4 RunnerJobStudioTranscodingPayload,
5 RunnerJobVODAudioMergeTranscodingPayload, 5 RunnerJobVODAudioMergeTranscodingPayload,
6 RunnerJobVODHLSTranscodingPayload, 6 RunnerJobVODHLSTranscodingPayload,
7 RunnerJobVODWebVideoTranscodingPayload 7 RunnerJobVODWebVideoTranscodingPayload
@@ -23,8 +23,8 @@ export async function processJob (options: ProcessOptions) {
23 await processHLSTranscoding(options as ProcessOptions<RunnerJobVODHLSTranscodingPayload>) 23 await processHLSTranscoding(options as ProcessOptions<RunnerJobVODHLSTranscodingPayload>)
24 } else if (job.type === 'live-rtmp-hls-transcoding') { 24 } else if (job.type === 'live-rtmp-hls-transcoding') {
25 await new ProcessLiveRTMPHLSTranscoding(options as ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>).process() 25 await new ProcessLiveRTMPHLSTranscoding(options as ProcessOptions<RunnerJobLiveRTMPHLSTranscodingPayload>).process()
26 } else if (job.type === 'video-edition-transcoding') { 26 } else if (job.type === 'video-studio-transcoding') {
27 await processStudioTranscoding(options as ProcessOptions<RunnerJobVideoEditionTranscodingPayload>) 27 await processStudioTranscoding(options as ProcessOptions<RunnerJobStudioTranscodingPayload>)
28 } else { 28 } else {
29 logger.error(`Unknown job ${job.type} to process`) 29 logger.error(`Unknown job ${job.type} to process`)
30 return 30 return
diff --git a/packages/peertube-runner/server/process/shared/common.ts b/packages/peertube-runner/server/process/shared/common.ts
index 3cac98388..88f7c33f1 100644
--- a/packages/peertube-runner/server/process/shared/common.ts
+++ b/packages/peertube-runner/server/process/shared/common.ts
@@ -1,13 +1,12 @@
1import { remove } from 'fs-extra'
1import { throttle } from 'lodash' 2import { throttle } from 'lodash'
2import { ConfigManager, downloadFile, logger } from 'packages/peertube-runner/shared' 3import { ConfigManager, downloadFile, logger } from 'packages/peertube-runner/shared'
3import { join } from 'path' 4import { join } from 'path'
4import { buildUUID } from '@shared/extra-utils' 5import { buildUUID } from '@shared/extra-utils'
5import { FFmpegEdition, FFmpegLive, FFmpegVOD } from '@shared/ffmpeg' 6import { FFmpegEdition, FFmpegLive, FFmpegVOD, getDefaultAvailableEncoders, getDefaultEncodersToTry } from '@shared/ffmpeg'
6import { RunnerJob, RunnerJobPayload } from '@shared/models' 7import { RunnerJob, RunnerJobPayload } from '@shared/models'
7import { PeerTubeServer } from '@shared/server-commands' 8import { PeerTubeServer } from '@shared/server-commands'
8import { getTranscodingLogger } from './transcoding-logger' 9import { getTranscodingLogger } from './transcoding-logger'
9import { getAvailableEncoders, getEncodersToTry } from './transcoding-profiles'
10import { remove } from 'fs-extra'
11 10
12export type JobWithToken <T extends RunnerJobPayload = RunnerJobPayload> = RunnerJob<T> & { jobToken: string } 11export type JobWithToken <T extends RunnerJobPayload = RunnerJobPayload> = RunnerJob<T> & { jobToken: string }
13 12
@@ -92,8 +91,8 @@ function getCommonFFmpegOptions () {
92 tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(), 91 tmpDirectory: ConfigManager.Instance.getTranscodingDirectory(),
93 profile: 'default', 92 profile: 'default',
94 availableEncoders: { 93 availableEncoders: {
95 available: getAvailableEncoders(), 94 available: getDefaultAvailableEncoders(),
96 encodersToTry: getEncodersToTry() 95 encodersToTry: getDefaultEncodersToTry()
97 }, 96 },
98 logger: getTranscodingLogger() 97 logger: getTranscodingLogger()
99 } 98 }
diff --git a/packages/peertube-runner/server/process/shared/index.ts b/packages/peertube-runner/server/process/shared/index.ts
index 8e09a7869..556c51365 100644
--- a/packages/peertube-runner/server/process/shared/index.ts
+++ b/packages/peertube-runner/server/process/shared/index.ts
@@ -1,4 +1,3 @@
1export * from './common' 1export * from './common'
2export * from './process-vod' 2export * from './process-vod'
3export * from './transcoding-logger' 3export * from './transcoding-logger'
4export * from './transcoding-profiles'
diff --git a/packages/peertube-runner/server/process/shared/process-studio.ts b/packages/peertube-runner/server/process/shared/process-studio.ts
index f8262096e..9c745d031 100644
--- a/packages/peertube-runner/server/process/shared/process-studio.ts
+++ b/packages/peertube-runner/server/process/shared/process-studio.ts
@@ -1,11 +1,11 @@
1import { remove } from 'fs-extra' 1import { remove } from 'fs-extra'
2import { pick } from 'lodash' 2import { pick } from 'lodash'
3import { logger } from 'packages/peertube-runner/shared' 3import { logger } from 'packages/peertube-runner/shared'
4import { extname, join } from 'path' 4import { join } from 'path'
5import { buildUUID } from '@shared/extra-utils' 5import { buildUUID } from '@shared/extra-utils'
6import { 6import {
7 RunnerJobVideoEditionTranscodingPayload, 7 RunnerJobStudioTranscodingPayload,
8 VideoEditionTranscodingSuccess, 8 VideoStudioTranscodingSuccess,
9 VideoStudioTask, 9 VideoStudioTask,
10 VideoStudioTaskCutPayload, 10 VideoStudioTaskCutPayload,
11 VideoStudioTaskIntroPayload, 11 VideoStudioTaskIntroPayload,
@@ -16,7 +16,7 @@ import {
16import { ConfigManager } from '../../../shared/config-manager' 16import { ConfigManager } from '../../../shared/config-manager'
17import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common' 17import { buildFFmpegEdition, downloadInputFile, JobWithToken, ProcessOptions } from './common'
18 18
19export async function processStudioTranscoding (options: ProcessOptions<RunnerJobVideoEditionTranscodingPayload>) { 19export async function processStudioTranscoding (options: ProcessOptions<RunnerJobStudioTranscodingPayload>) {
20 const { server, job, runnerToken } = options 20 const { server, job, runnerToken } = options
21 const payload = job.payload 21 const payload = job.payload
22 22
@@ -43,7 +43,7 @@ export async function processStudioTranscoding (options: ProcessOptions<RunnerJo
43 tmpInputFilePath = outputPath 43 tmpInputFilePath = outputPath
44 } 44 }
45 45
46 const successBody: VideoEditionTranscodingSuccess = { 46 const successBody: VideoStudioTranscodingSuccess = {
47 videoFile: outputPath 47 videoFile: outputPath
48 } 48 }
49 49
@@ -94,14 +94,18 @@ async function processAddIntroOutro (options: TaskProcessorOptions<VideoStudioTa
94 94
95 const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job }) 95 const introOutroPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
96 96
97 return buildFFmpegEdition().addIntroOutro({ 97 try {
98 ...pick(options, [ 'inputPath', 'outputPath' ]), 98 await buildFFmpegEdition().addIntroOutro({
99 ...pick(options, [ 'inputPath', 'outputPath' ]),
99 100
100 introOutroPath, 101 introOutroPath,
101 type: task.name === 'add-intro' 102 type: task.name === 'add-intro'
102 ? 'intro' 103 ? 'intro'
103 : 'outro' 104 : 'outro'
104 }) 105 })
106 } finally {
107 await remove(introOutroPath)
108 }
105} 109}
106 110
107function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) { 111function processCut (options: TaskProcessorOptions<VideoStudioTaskCutPayload>) {
@@ -124,15 +128,19 @@ async function processAddWatermark (options: TaskProcessorOptions<VideoStudioTas
124 128
125 const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job }) 129 const watermarkPath = await downloadInputFile({ url: task.options.file, runnerToken, job })
126 130
127 return buildFFmpegEdition().addWatermark({ 131 try {
128 ...pick(options, [ 'inputPath', 'outputPath' ]), 132 await buildFFmpegEdition().addWatermark({
133 ...pick(options, [ 'inputPath', 'outputPath' ]),
129 134
130 watermarkPath, 135 watermarkPath,
131 136
132 videoFilters: { 137 videoFilters: {
133 watermarkSizeRatio: task.options.watermarkSizeRatio, 138 watermarkSizeRatio: task.options.watermarkSizeRatio,
134 horitonzalMarginRatio: task.options.horitonzalMarginRatio, 139 horitonzalMarginRatio: task.options.horitonzalMarginRatio,
135 verticalMarginRatio: task.options.verticalMarginRatio 140 verticalMarginRatio: task.options.verticalMarginRatio
136 } 141 }
137 }) 142 })
143 } finally {
144 await remove(watermarkPath)
145 }
138} 146}
diff --git a/packages/peertube-runner/server/process/shared/process-vod.ts b/packages/peertube-runner/server/process/shared/process-vod.ts
index d84ece3cb..22489afd5 100644
--- a/packages/peertube-runner/server/process/shared/process-vod.ts
+++ b/packages/peertube-runner/server/process/shared/process-vod.ts
@@ -22,31 +22,34 @@ export async function processWebVideoTranscoding (options: ProcessOptions<Runner
22 22
23 const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`) 23 const outputPath = join(ConfigManager.Instance.getTranscodingDirectory(), `output-${buildUUID()}.mp4`)
24 24
25 await ffmpegVod.transcode({ 25 try {
26 type: 'video', 26 await ffmpegVod.transcode({
27 27 type: 'video',
28 inputPath,
29 28
30 outputPath, 29 inputPath,
31 30
32 inputFileMutexReleaser: () => {}, 31 outputPath,
33 32
34 resolution: payload.output.resolution, 33 inputFileMutexReleaser: () => {},
35 fps: payload.output.fps
36 })
37 34
38 const successBody: VODWebVideoTranscodingSuccess = { 35 resolution: payload.output.resolution,
39 videoFile: outputPath 36 fps: payload.output.fps
40 } 37 })
41 38
42 await server.runnerJobs.success({ 39 const successBody: VODWebVideoTranscodingSuccess = {
43 jobToken: job.jobToken, 40 videoFile: outputPath
44 jobUUID: job.uuid, 41 }
45 runnerToken,
46 payload: successBody
47 })
48 42
49 await remove(outputPath) 43 await server.runnerJobs.success({
44 jobToken: job.jobToken,
45 jobUUID: job.uuid,
46 runnerToken,
47 payload: successBody
48 })
49 } finally {
50 await remove(inputPath)
51 await remove(outputPath)
52 }
50} 53}
51 54
52export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) { 55export async function processHLSTranscoding (options: ProcessOptions<RunnerJobVODHLSTranscodingPayload>) {
@@ -105,30 +108,34 @@ export async function processAudioMergeTranscoding (options: ProcessOptions<Runn
105 108
106 const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken }) 109 const ffmpegVod = buildFFmpegVOD({ job, server, runnerToken })
107 110
108 await ffmpegVod.transcode({ 111 try {
109 type: 'merge-audio', 112 await ffmpegVod.transcode({
110 113 type: 'merge-audio',
111 audioPath,
112 inputPath,
113 114
114 outputPath, 115 audioPath,
116 inputPath,
115 117
116 inputFileMutexReleaser: () => {}, 118 outputPath,
117 119
118 resolution: payload.output.resolution, 120 inputFileMutexReleaser: () => {},
119 fps: payload.output.fps
120 })
121 121
122 const successBody: VODAudioMergeTranscodingSuccess = { 122 resolution: payload.output.resolution,
123 videoFile: outputPath 123 fps: payload.output.fps
124 } 124 })
125 125
126 await server.runnerJobs.success({ 126 const successBody: VODAudioMergeTranscodingSuccess = {
127 jobToken: job.jobToken, 127 videoFile: outputPath
128 jobUUID: job.uuid, 128 }
129 runnerToken,
130 payload: successBody
131 })
132 129
133 await remove(outputPath) 130 await server.runnerJobs.success({
131 jobToken: job.jobToken,
132 jobUUID: job.uuid,
133 runnerToken,
134 payload: successBody
135 })
136 } finally {
137 await remove(audioPath)
138 await remove(inputPath)
139 await remove(outputPath)
140 }
134} 141}
diff --git a/packages/peertube-runner/server/process/shared/transcoding-profiles.ts b/packages/peertube-runner/server/process/shared/transcoding-profiles.ts
deleted file mode 100644
index 492d17d6a..000000000
--- a/packages/peertube-runner/server/process/shared/transcoding-profiles.ts
+++ /dev/null
@@ -1,134 +0,0 @@
1import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils'
2import { buildStreamSuffix, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg'
3import { EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models'
4
5const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
6 const { fps, inputRatio, inputBitrate, resolution } = options
7
8 const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
9
10 return {
11 outputOptions: [
12 ...getCommonOutputOptions(targetBitrate),
13
14 `-r ${fps}`
15 ]
16 }
17}
18
19const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
20 const { streamNum, fps, inputBitrate, inputRatio, resolution } = options
21
22 const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
23
24 return {
25 outputOptions: [
26 ...getCommonOutputOptions(targetBitrate, streamNum),
27
28 `${buildStreamSuffix('-r:v', streamNum)} ${fps}`,
29 `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`
30 ]
31 }
32}
33
34const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => {
35 const probe = await ffprobePromise(input)
36
37 const parsedAudio = await getAudioStream(input, probe)
38
39 // We try to reduce the ceiling bitrate by making rough matches of bitrates
40 // Of course this is far from perfect, but it might save some space in the end
41
42 const audioCodecName = parsedAudio.audioStream['codec_name']
43
44 const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
45
46 // Force stereo as it causes some issues with HLS playback in Chrome
47 const base = [ '-channel_layout', 'stereo' ]
48
49 if (bitrate !== -1) {
50 return { outputOptions: base.concat([ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ]) }
51 }
52
53 return { outputOptions: base }
54}
55
56const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
57 return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
58}
59
60export function getAvailableEncoders () {
61 return {
62 vod: {
63 libx264: {
64 default: defaultX264VODOptionsBuilder
65 },
66 aac: {
67 default: defaultAACOptionsBuilder
68 },
69 libfdk_aac: {
70 default: defaultLibFDKAACVODOptionsBuilder
71 }
72 },
73 live: {
74 libx264: {
75 default: defaultX264LiveOptionsBuilder
76 },
77 aac: {
78 default: defaultAACOptionsBuilder
79 }
80 }
81 }
82}
83
84export function getEncodersToTry () {
85 return {
86 vod: {
87 video: [ 'libx264' ],
88 audio: [ 'libfdk_aac', 'aac' ]
89 },
90
91 live: {
92 video: [ 'libx264' ],
93 audio: [ 'libfdk_aac', 'aac' ]
94 }
95 }
96}
97
98// ---------------------------------------------------------------------------
99
100function getTargetBitrate (options: {
101 inputBitrate: number
102 resolution: VideoResolution
103 ratio: number
104 fps: number
105}) {
106 const { inputBitrate, resolution, ratio, fps } = options
107
108 const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio }))
109 const limit = getMinLimitBitrate({ resolution, fps, ratio })
110
111 return Math.max(limit, capped)
112}
113
114function capBitrate (inputBitrate: number, targetBitrate: number) {
115 if (!inputBitrate) return targetBitrate
116
117 // Add 30% margin to input bitrate
118 const inputBitrateWithMargin = inputBitrate + (inputBitrate * 0.3)
119
120 return Math.min(targetBitrate, inputBitrateWithMargin)
121}
122
123function getCommonOutputOptions (targetBitrate: number, streamNum?: number) {
124 return [
125 `-preset veryfast`,
126 `${buildStreamSuffix('-maxrate:v', streamNum)} ${targetBitrate}`,
127 `${buildStreamSuffix('-bufsize:v', streamNum)} ${targetBitrate * 2}`,
128
129 // NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
130 `-b_strategy 1`,
131 // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16
132 `-bf 16`
133 ]
134}