]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/helpers/ffprobe-utils.ts
Better 413 error handling in cli script
[github/Chocobozzz/PeerTube.git] / server / helpers / ffprobe-utils.ts
CommitLineData
06aad801 1import { FfprobeData } from 'fluent-ffmpeg'
679c12e6 2import { getMaxBitrate } from '@shared/core-utils'
06aad801 3import { VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos'
daf6e480
C
4import { CONFIG } from '../initializers/config'
5import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
6import { logger } from './logger'
06aad801 7import {
8 canDoQuickAudioTranscode,
9 ffprobePromise,
10 getDurationFromVideoFile,
11 getAudioStream,
12 getMaxAudioBitrate,
13 getMetadataFromFile,
14 getVideoFileBitrate,
15 getVideoFileFPS,
16 getVideoFileResolution,
17 getVideoStreamFromFile,
18 getVideoStreamSize
19} from '@shared/extra-utils/ffprobe'
daf6e480 20
6b67897e
C
21/**
22 *
23 * Helpers to run ffprobe and extract data from the JSON output
24 *
25 */
26
daf6e480
C
27async function getVideoStreamCodec (path: string) {
28 const videoStream = await getVideoStreamFromFile(path)
29
30 if (!videoStream) return ''
31
32 const videoCodec = videoStream.codec_tag_string
33
78995146 34 if (videoCodec === 'vp09') return 'vp09.00.50.08'
08370f62 35 if (videoCodec === 'hev1') return 'hev1.1.6.L93.B0'
78995146 36
daf6e480 37 const baseProfileMatrix = {
78995146
C
38 avc1: {
39 High: '6400',
40 Main: '4D40',
41 Baseline: '42E0'
42 },
43 av01: {
44 High: '1',
45 Main: '0',
46 Professional: '2'
47 }
daf6e480
C
48 }
49
78995146 50 let baseProfile = baseProfileMatrix[videoCodec][videoStream.profile]
daf6e480
C
51 if (!baseProfile) {
52 logger.warn('Cannot get video profile codec of %s.', path, { videoStream })
78995146
C
53 baseProfile = baseProfileMatrix[videoCodec]['High'] // Fallback
54 }
55
56 if (videoCodec === 'av01') {
57 const level = videoStream.level
58
59 // Guess the tier indicator and bit depth
60 return `${videoCodec}.${baseProfile}.${level}M.08`
daf6e480
C
61 }
62
78995146 63 // Default, h264 codec
daf6e480
C
64 let level = videoStream.level.toString(16)
65 if (level.length === 1) level = `0${level}`
66
67 return `${videoCodec}.${baseProfile}${level}`
68}
69
41fb13c3 70async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) {
daf6e480
C
71 const { audioStream } = await getAudioStream(path, existingProbe)
72
73 if (!audioStream) return ''
74
78995146
C
75 const audioCodecName = audioStream.codec_name
76
77 if (audioCodecName === 'opus') return 'opus'
78 if (audioCodecName === 'vorbis') return 'vorbis'
79 if (audioCodecName === 'aac') return 'mp4a.40.2'
daf6e480
C
80
81 logger.warn('Cannot get audio codec of %s.', path, { audioStream })
82
83 return 'mp4a.40.2' // Fallback
84}
85
ad5db104 86function computeLowerResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') {
daf6e480
C
87 const configResolutions = type === 'vod'
88 ? CONFIG.TRANSCODING.RESOLUTIONS
89 : CONFIG.LIVE.TRANSCODING.RESOLUTIONS
90
91 const resolutionsEnabled: number[] = []
92
93 // Put in the order we want to proceed jobs
ad5db104 94 const resolutions: VideoResolution[] = [
daf6e480
C
95 VideoResolution.H_NOVIDEO,
96 VideoResolution.H_480P,
97 VideoResolution.H_360P,
98 VideoResolution.H_720P,
99 VideoResolution.H_240P,
8dd754c7 100 VideoResolution.H_144P,
daf6e480 101 VideoResolution.H_1080P,
b7085c71 102 VideoResolution.H_1440P,
daf6e480
C
103 VideoResolution.H_4K
104 ]
105
106 for (const resolution of resolutions) {
107 if (configResolutions[resolution + 'p'] === true && videoFileResolution > resolution) {
108 resolutionsEnabled.push(resolution)
109 }
110 }
111
112 return resolutionsEnabled
113}
114
115async function canDoQuickTranscode (path: string): Promise<boolean> {
ffd970fa
C
116 if (CONFIG.TRANSCODING.PROFILE !== 'default') return false
117
daf6e480
C
118 const probe = await ffprobePromise(path)
119
5a547f69
C
120 return await canDoQuickVideoTranscode(path, probe) &&
121 await canDoQuickAudioTranscode(path, probe)
122}
123
41fb13c3 124async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
daf6e480 125 const videoStream = await getVideoStreamFromFile(path, probe)
daf6e480
C
126 const fps = await getVideoFileFPS(path, probe)
127 const bitRate = await getVideoFileBitrate(path, probe)
679c12e6 128 const resolutionData = await getVideoFileResolution(path, probe)
daf6e480 129
33ff70ba
C
130 // If ffprobe did not manage to guess the bitrate
131 if (!bitRate) return false
132
daf6e480
C
133 // check video params
134 if (videoStream == null) return false
135 if (videoStream['codec_name'] !== 'h264') return false
136 if (videoStream['pix_fmt'] !== 'yuv420p') return false
137 if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false
679c12e6 138 if (bitRate > getMaxBitrate({ ...resolutionData, fps })) return false
daf6e480 139
5a547f69
C
140 return true
141}
142
679c12e6 143function getClosestFramerateStandard <K extends keyof Pick<VideoTranscodingFPS, 'HD_STANDARD' | 'STANDARD'>> (fps: number, type: K) {
daf6e480
C
144 return VIDEO_TRANSCODING_FPS[type].slice(0)
145 .sort((a, b) => fps % a - fps % b)[0]
146}
147
884d2c39
C
148function computeFPS (fpsArg: number, resolution: VideoResolution) {
149 let fps = fpsArg
150
151 if (
152 // On small/medium resolutions, limit FPS
153 resolution !== undefined &&
154 resolution < VIDEO_TRANSCODING_FPS.KEEP_ORIGIN_FPS_RESOLUTION_MIN &&
155 fps > VIDEO_TRANSCODING_FPS.AVERAGE
156 ) {
157 // Get closest standard framerate by modulo: downsampling has to be done to a divisor of the nominal fps value
158 fps = getClosestFramerateStandard(fps, 'STANDARD')
159 }
160
161 // Hard FPS limits
162 if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, 'HD_STANDARD')
f7bb2bb5
C
163
164 if (fps < VIDEO_TRANSCODING_FPS.MIN) {
165 throw new Error(`Cannot compute FPS because ${fps} is lower than our minimum value ${VIDEO_TRANSCODING_FPS.MIN}`)
166 }
884d2c39
C
167
168 return fps
169}
170
daf6e480
C
171// ---------------------------------------------------------------------------
172
173export {
174 getVideoStreamCodec,
175 getAudioStreamCodec,
176 getVideoStreamSize,
177 getVideoFileResolution,
178 getMetadataFromFile,
179 getMaxAudioBitrate,
5a547f69 180 getVideoStreamFromFile,
daf6e480
C
181 getDurationFromVideoFile,
182 getAudioStream,
884d2c39 183 computeFPS,
daf6e480 184 getVideoFileFPS,
5a547f69 185 ffprobePromise,
daf6e480 186 getClosestFramerateStandard,
ad5db104 187 computeLowerResolutionsToTranscode,
daf6e480 188 getVideoFileBitrate,
5a547f69
C
189 canDoQuickTranscode,
190 canDoQuickVideoTranscode,
191 canDoQuickAudioTranscode
daf6e480 192}