]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/helpers/ffmpeg/ffmpeg-live.ts
Prevent weird error on sync failure
[github/Chocobozzz/PeerTube.git] / server / helpers / ffmpeg / ffmpeg-live.ts
CommitLineData
c729caf6
C
1import { FfmpegCommand, FilterSpecification } from 'fluent-ffmpeg'
2import { join } from 'path'
3import { VIDEO_LIVE } from '@server/initializers/constants'
f443a746 4import { AvailableEncoders, LiveVideoLatencyMode } from '@shared/models'
c729caf6
C
5import { logger, loggerTagsFactory } from '../logger'
6import { buildStreamSuffix, getFFmpeg, getScaleFilter, StreamType } from './ffmpeg-commons'
7import { getEncoderBuilderResult } from './ffmpeg-encoders'
8import { addDefaultEncoderGlobalParams, addDefaultEncoderParams, applyEncoderOptions } from './ffmpeg-presets'
9import { computeFPS } from './ffprobe-utils'
10
11const lTags = loggerTagsFactory('ffmpeg')
12
13async function getLiveTranscodingCommand (options: {
14 inputUrl: string
15
16 outPath: string
17 masterPlaylistName: string
f443a746 18 latencyMode: LiveVideoLatencyMode
c729caf6
C
19
20 resolutions: number[]
21
22 // Input information
23 fps: number
24 bitrate: number
25 ratio: number
26
27 availableEncoders: AvailableEncoders
28 profile: string
29}) {
f443a746 30 const { inputUrl, outPath, resolutions, fps, bitrate, availableEncoders, profile, masterPlaylistName, ratio, latencyMode } = options
c729caf6
C
31
32 const command = getFFmpeg(inputUrl, 'live')
33
34 const varStreamMap: string[] = []
35
36 const complexFilter: FilterSpecification[] = [
37 {
38 inputs: '[v:0]',
39 filter: 'split',
40 options: resolutions.length,
41 outputs: resolutions.map(r => `vtemp${r}`)
42 }
43 ]
44
45 command.outputOption('-sc_threshold 0')
46
47 addDefaultEncoderGlobalParams(command)
48
49 for (let i = 0; i < resolutions.length; i++) {
50 const resolution = resolutions[i]
51 const resolutionFPS = computeFPS(fps, resolution)
52
53 const baseEncoderBuilderParams = {
54 input: inputUrl,
55
56 availableEncoders,
57 profile,
58
59 canCopyAudio: true,
60 canCopyVideo: true,
61
62 inputBitrate: bitrate,
63 inputRatio: ratio,
64
65 resolution,
66 fps: resolutionFPS,
67
68 streamNum: i,
69 videoType: 'live' as 'live'
70 }
71
72 {
73 const streamType: StreamType = 'video'
74 const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType })
75 if (!builderResult) {
76 throw new Error('No available live video encoder found')
77 }
78
79 command.outputOption(`-map [vout${resolution}]`)
80
81 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
82
83 logger.debug(
84 'Apply ffmpeg live video params from %s using %s profile.', builderResult.encoder, profile,
85 { builderResult, fps: resolutionFPS, resolution, ...lTags() }
86 )
87
88 command.outputOption(`${buildStreamSuffix('-c:v', i)} ${builderResult.encoder}`)
89 applyEncoderOptions(command, builderResult.result)
90
91 complexFilter.push({
92 inputs: `vtemp${resolution}`,
93 filter: getScaleFilter(builderResult.result),
94 options: `w=-2:h=${resolution}`,
95 outputs: `vout${resolution}`
96 })
97 }
98
99 {
100 const streamType: StreamType = 'audio'
101 const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType })
102 if (!builderResult) {
103 throw new Error('No available live audio encoder found')
104 }
105
106 command.outputOption('-map a:0')
107
108 addDefaultEncoderParams({ command, encoder: builderResult.encoder, fps: resolutionFPS, streamNum: i })
109
110 logger.debug(
111 'Apply ffmpeg live audio params from %s using %s profile.', builderResult.encoder, profile,
112 { builderResult, fps: resolutionFPS, resolution, ...lTags() }
113 )
114
115 command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`)
116 applyEncoderOptions(command, builderResult.result)
117 }
118
119 varStreamMap.push(`v:${i},a:${i}`)
120 }
121
122 command.complexFilter(complexFilter)
123
f443a746 124 addDefaultLiveHLSParams({ command, outPath, masterPlaylistName, latencyMode })
c729caf6
C
125
126 command.outputOption('-var_stream_map', varStreamMap.join(' '))
127
128 return command
129}
130
f443a746
C
131function getLiveMuxingCommand (options: {
132 inputUrl: string
133 outPath: string
134 masterPlaylistName: string
135 latencyMode: LiveVideoLatencyMode
136}) {
137 const { inputUrl, outPath, masterPlaylistName, latencyMode } = options
138
c729caf6
C
139 const command = getFFmpeg(inputUrl, 'live')
140
141 command.outputOption('-c:v copy')
142 command.outputOption('-c:a copy')
143 command.outputOption('-map 0:a?')
144 command.outputOption('-map 0:v?')
145
f443a746 146 addDefaultLiveHLSParams({ command, outPath, masterPlaylistName, latencyMode })
c729caf6
C
147
148 return command
149}
150
f443a746
C
151function getLiveSegmentTime (latencyMode: LiveVideoLatencyMode) {
152 if (latencyMode === LiveVideoLatencyMode.SMALL_LATENCY) {
153 return VIDEO_LIVE.SEGMENT_TIME_SECONDS.SMALL_LATENCY
154 }
155
156 return VIDEO_LIVE.SEGMENT_TIME_SECONDS.DEFAULT_LATENCY
157}
158
c729caf6
C
159// ---------------------------------------------------------------------------
160
161export {
f443a746
C
162 getLiveSegmentTime,
163
c729caf6
C
164 getLiveTranscodingCommand,
165 getLiveMuxingCommand
166}
167
168// ---------------------------------------------------------------------------
169
f443a746
C
170function addDefaultLiveHLSParams (options: {
171 command: FfmpegCommand
172 outPath: string
173 masterPlaylistName: string
174 latencyMode: LiveVideoLatencyMode
175}) {
176 const { command, outPath, masterPlaylistName, latencyMode } = options
177
178 command.outputOption('-hls_time ' + getLiveSegmentTime(latencyMode))
c729caf6 179 command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE)
ffcfb32b 180 command.outputOption('-hls_flags delete_segments+independent_segments+program_date_time')
c729caf6
C
181 command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`)
182 command.outputOption('-master_pl_name ' + masterPlaylistName)
183 command.outputOption(`-f hls`)
184
185 command.output(join(outPath, '%v.m3u8'))
186}