diff options
author | Chocobozzz <me@florianbigard.com> | 2023-04-21 14:55:10 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2023-05-09 08:57:34 +0200 |
commit | 0c9668f77901e7540e2c7045eb0f2974a4842a69 (patch) | |
tree | 226d3dd1565b0bb56588897af3b8530e6216e96b /server/helpers/ffmpeg/ffmpeg-live.ts | |
parent | 6bcb854cdea8688a32240bc5719c7d139806e00b (diff) | |
download | PeerTube-0c9668f77901e7540e2c7045eb0f2974a4842a69.tar.gz PeerTube-0c9668f77901e7540e2c7045eb0f2974a4842a69.tar.zst PeerTube-0c9668f77901e7540e2c7045eb0f2974a4842a69.zip |
Implement remote runner jobs in server
Move ffmpeg functions to @shared
Diffstat (limited to 'server/helpers/ffmpeg/ffmpeg-live.ts')
-rw-r--r-- | server/helpers/ffmpeg/ffmpeg-live.ts | 204 |
1 files changed, 0 insertions, 204 deletions
diff --git a/server/helpers/ffmpeg/ffmpeg-live.ts b/server/helpers/ffmpeg/ffmpeg-live.ts deleted file mode 100644 index 379d7b1ad..000000000 --- a/server/helpers/ffmpeg/ffmpeg-live.ts +++ /dev/null | |||
@@ -1,204 +0,0 @@ | |||
1 | import { FfmpegCommand, FilterSpecification } from 'fluent-ffmpeg' | ||
2 | import { join } from 'path' | ||
3 | import { VIDEO_LIVE } from '@server/initializers/constants' | ||
4 | import { AvailableEncoders, LiveVideoLatencyMode } from '@shared/models' | ||
5 | import { logger, loggerTagsFactory } from '../logger' | ||
6 | import { buildStreamSuffix, getFFmpeg, getScaleFilter, StreamType } from './ffmpeg-commons' | ||
7 | import { getEncoderBuilderResult } from './ffmpeg-encoders' | ||
8 | import { addDefaultEncoderGlobalParams, addDefaultEncoderParams, applyEncoderOptions } from './ffmpeg-presets' | ||
9 | import { computeFPS } from './ffprobe-utils' | ||
10 | |||
11 | const lTags = loggerTagsFactory('ffmpeg') | ||
12 | |||
13 | async function getLiveTranscodingCommand (options: { | ||
14 | inputUrl: string | ||
15 | |||
16 | outPath: string | ||
17 | masterPlaylistName: string | ||
18 | latencyMode: LiveVideoLatencyMode | ||
19 | |||
20 | resolutions: number[] | ||
21 | |||
22 | // Input information | ||
23 | fps: number | ||
24 | bitrate: number | ||
25 | ratio: number | ||
26 | hasAudio: boolean | ||
27 | |||
28 | availableEncoders: AvailableEncoders | ||
29 | profile: string | ||
30 | }) { | ||
31 | const { | ||
32 | inputUrl, | ||
33 | outPath, | ||
34 | resolutions, | ||
35 | fps, | ||
36 | bitrate, | ||
37 | availableEncoders, | ||
38 | profile, | ||
39 | masterPlaylistName, | ||
40 | ratio, | ||
41 | latencyMode, | ||
42 | hasAudio | ||
43 | } = options | ||
44 | |||
45 | const command = getFFmpeg(inputUrl, 'live') | ||
46 | |||
47 | const varStreamMap: string[] = [] | ||
48 | |||
49 | const complexFilter: FilterSpecification[] = [ | ||
50 | { | ||
51 | inputs: '[v:0]', | ||
52 | filter: 'split', | ||
53 | options: resolutions.length, | ||
54 | outputs: resolutions.map(r => `vtemp${r}`) | ||
55 | } | ||
56 | ] | ||
57 | |||
58 | command.outputOption('-sc_threshold 0') | ||
59 | |||
60 | addDefaultEncoderGlobalParams(command) | ||
61 | |||
62 | for (let i = 0; i < resolutions.length; i++) { | ||
63 | const streamMap: string[] = [] | ||
64 | const resolution = resolutions[i] | ||
65 | const resolutionFPS = computeFPS(fps, resolution) | ||
66 | |||
67 | const baseEncoderBuilderParams = { | ||
68 | input: inputUrl, | ||
69 | |||
70 | availableEncoders, | ||
71 | profile, | ||
72 | |||
73 | canCopyAudio: true, | ||
74 | canCopyVideo: true, | ||
75 | |||
76 | inputBitrate: bitrate, | ||
77 | inputRatio: ratio, | ||
78 | |||
79 | resolution, | ||
80 | fps: resolutionFPS, | ||
81 | |||
82 | streamNum: i, | ||
83 | videoType: 'live' as 'live' | ||
84 | } | ||
85 | |||
86 | { | ||
87 | const streamType: StreamType = 'video' | ||
88 | const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) | ||
89 | if (!builderResult) { | ||
90 | throw new Error('No available live video encoder found') | ||
91 | } | ||
92 | |||
93 | command.outputOption(`-map [vout${resolution}]`) | ||
94 | |||
95 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) | ||
96 | |||
97 | logger.debug( | ||
98 | 'Apply ffmpeg live video params from %s using %s profile.', builderResult.encoder, profile, | ||
99 | { builderResult, fps: resolutionFPS, resolution, ...lTags() } | ||
100 | ) | ||
101 | |||
102 | command.outputOption(`${buildStreamSuffix('-c:v', i)} ${builderResult.encoder}`) | ||
103 | applyEncoderOptions(command, builderResult.result) | ||
104 | |||
105 | complexFilter.push({ | ||
106 | inputs: `vtemp${resolution}`, | ||
107 | filter: getScaleFilter(builderResult.result), | ||
108 | options: `w=-2:h=${resolution}`, | ||
109 | outputs: `vout${resolution}` | ||
110 | }) | ||
111 | |||
112 | streamMap.push(`v:${i}`) | ||
113 | } | ||
114 | |||
115 | if (hasAudio) { | ||
116 | const streamType: StreamType = 'audio' | ||
117 | const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) | ||
118 | if (!builderResult) { | ||
119 | throw new Error('No available live audio encoder found') | ||
120 | } | ||
121 | |||
122 | command.outputOption('-map a:0') | ||
123 | |||
124 | addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i }) | ||
125 | |||
126 | logger.debug( | ||
127 | 'Apply ffmpeg live audio params from %s using %s profile.', builderResult.encoder, profile, | ||
128 | { builderResult, fps: resolutionFPS, resolution, ...lTags() } | ||
129 | ) | ||
130 | |||
131 | command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`) | ||
132 | applyEncoderOptions(command, builderResult.result) | ||
133 | |||
134 | streamMap.push(`a:${i}`) | ||
135 | } | ||
136 | |||
137 | varStreamMap.push(streamMap.join(',')) | ||
138 | } | ||
139 | |||
140 | command.complexFilter(complexFilter) | ||
141 | |||
142 | addDefaultLiveHLSParams({ command, outPath, masterPlaylistName, latencyMode }) | ||
143 | |||
144 | command.outputOption('-var_stream_map', varStreamMap.join(' ')) | ||
145 | |||
146 | return command | ||
147 | } | ||
148 | |||
149 | function getLiveMuxingCommand (options: { | ||
150 | inputUrl: string | ||
151 | outPath: string | ||
152 | masterPlaylistName: string | ||
153 | latencyMode: LiveVideoLatencyMode | ||
154 | }) { | ||
155 | const { inputUrl, outPath, masterPlaylistName, latencyMode } = options | ||
156 | |||
157 | const command = getFFmpeg(inputUrl, 'live') | ||
158 | |||
159 | command.outputOption('-c:v copy') | ||
160 | command.outputOption('-c:a copy') | ||
161 | command.outputOption('-map 0:a?') | ||
162 | command.outputOption('-map 0:v?') | ||
163 | |||
164 | addDefaultLiveHLSParams({ command, outPath, masterPlaylistName, latencyMode }) | ||
165 | |||
166 | return command | ||
167 | } | ||
168 | |||
169 | function getLiveSegmentTime (latencyMode: LiveVideoLatencyMode) { | ||
170 | if (latencyMode === LiveVideoLatencyMode.SMALL_LATENCY) { | ||
171 | return VIDEO_LIVE.SEGMENT_TIME_SECONDS.SMALL_LATENCY | ||
172 | } | ||
173 | |||
174 | return VIDEO_LIVE.SEGMENT_TIME_SECONDS.DEFAULT_LATENCY | ||
175 | } | ||
176 | |||
177 | // --------------------------------------------------------------------------- | ||
178 | |||
179 | export { | ||
180 | getLiveSegmentTime, | ||
181 | |||
182 | getLiveTranscodingCommand, | ||
183 | getLiveMuxingCommand | ||
184 | } | ||
185 | |||
186 | // --------------------------------------------------------------------------- | ||
187 | |||
188 | function addDefaultLiveHLSParams (options: { | ||
189 | command: FfmpegCommand | ||
190 | outPath: string | ||
191 | masterPlaylistName: string | ||
192 | latencyMode: LiveVideoLatencyMode | ||
193 | }) { | ||
194 | const { command, outPath, masterPlaylistName, latencyMode } = options | ||
195 | |||
196 | command.outputOption('-hls_time ' + getLiveSegmentTime(latencyMode)) | ||
197 | command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE) | ||
198 | command.outputOption('-hls_flags delete_segments+independent_segments+program_date_time') | ||
199 | command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`) | ||
200 | command.outputOption('-master_pl_name ' + masterPlaylistName) | ||
201 | command.outputOption(`-f hls`) | ||
202 | |||
203 | command.output(join(outPath, '%v.m3u8')) | ||
204 | } | ||