diff options
author | Chocobozzz <me@florianbigard.com> | 2022-02-11 10:51:33 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2022-02-28 10:42:19 +0100 |
commit | c729caf6cc34630877a0e5a1bda1719384cd0c8a (patch) | |
tree | 1d2e13722e518c73d2c9e6f0969615e29d51cf8c /shared/extra-utils | |
parent | a24bf4dc659cebb65d887862bf21d7a35e9ec791 (diff) | |
download | PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.tar.gz PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.tar.zst PeerTube-c729caf6cc34630877a0e5a1bda1719384cd0c8a.zip |
Add basic video editor support
Diffstat (limited to 'shared/extra-utils')
-rw-r--r-- | shared/extra-utils/ffprobe.ts | 96 |
1 files changed, 41 insertions, 55 deletions
diff --git a/shared/extra-utils/ffprobe.ts b/shared/extra-utils/ffprobe.ts index 53a3aa001..dfacd251c 100644 --- a/shared/extra-utils/ffprobe.ts +++ b/shared/extra-utils/ffprobe.ts | |||
@@ -17,12 +17,22 @@ function ffprobePromise (path: string) { | |||
17 | }) | 17 | }) |
18 | } | 18 | } |
19 | 19 | ||
20 | // --------------------------------------------------------------------------- | ||
21 | // Audio | ||
22 | // --------------------------------------------------------------------------- | ||
23 | |||
20 | async function isAudioFile (path: string, existingProbe?: FfprobeData) { | 24 | async function isAudioFile (path: string, existingProbe?: FfprobeData) { |
21 | const videoStream = await getVideoStreamFromFile(path, existingProbe) | 25 | const videoStream = await getVideoStream(path, existingProbe) |
22 | 26 | ||
23 | return !videoStream | 27 | return !videoStream |
24 | } | 28 | } |
25 | 29 | ||
30 | async function hasAudioStream (path: string, existingProbe?: FfprobeData) { | ||
31 | const { audioStream } = await getAudioStream(path, existingProbe) | ||
32 | |||
33 | return !!audioStream | ||
34 | } | ||
35 | |||
26 | async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { | 36 | async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { |
27 | // without position, ffprobe considers the last input only | 37 | // without position, ffprobe considers the last input only |
28 | // we make it consider the first input only | 38 | // we make it consider the first input only |
@@ -78,29 +88,26 @@ function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) { | |||
78 | } | 88 | } |
79 | } | 89 | } |
80 | 90 | ||
81 | async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> { | 91 | // --------------------------------------------------------------------------- |
82 | const videoStream = await getVideoStreamFromFile(path, existingProbe) | 92 | // Video |
83 | 93 | // --------------------------------------------------------------------------- | |
84 | return videoStream === null | ||
85 | ? { width: 0, height: 0 } | ||
86 | : { width: videoStream.width, height: videoStream.height } | ||
87 | } | ||
88 | 94 | ||
89 | async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) { | 95 | async function getVideoStreamDimensionsInfo (path: string, existingProbe?: FfprobeData) { |
90 | const size = await getVideoStreamSize(path, existingProbe) | 96 | const videoStream = await getVideoStream(path, existingProbe) |
97 | if (!videoStream) return undefined | ||
91 | 98 | ||
92 | return { | 99 | return { |
93 | width: size.width, | 100 | width: videoStream.width, |
94 | height: size.height, | 101 | height: videoStream.height, |
95 | ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width), | 102 | ratio: Math.max(videoStream.height, videoStream.width) / Math.min(videoStream.height, videoStream.width), |
96 | resolution: Math.min(size.height, size.width), | 103 | resolution: Math.min(videoStream.height, videoStream.width), |
97 | isPortraitMode: size.height > size.width | 104 | isPortraitMode: videoStream.height > videoStream.width |
98 | } | 105 | } |
99 | } | 106 | } |
100 | 107 | ||
101 | async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) { | 108 | async function getVideoStreamFPS (path: string, existingProbe?: FfprobeData) { |
102 | const videoStream = await getVideoStreamFromFile(path, existingProbe) | 109 | const videoStream = await getVideoStream(path, existingProbe) |
103 | if (videoStream === null) return 0 | 110 | if (!videoStream) return 0 |
104 | 111 | ||
105 | for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { | 112 | for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { |
106 | const valuesText: string = videoStream[key] | 113 | const valuesText: string = videoStream[key] |
@@ -116,19 +123,19 @@ async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) { | |||
116 | return 0 | 123 | return 0 |
117 | } | 124 | } |
118 | 125 | ||
119 | async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) { | 126 | async function buildFileMetadata (path: string, existingProbe?: FfprobeData) { |
120 | const metadata = existingProbe || await ffprobePromise(path) | 127 | const metadata = existingProbe || await ffprobePromise(path) |
121 | 128 | ||
122 | return new VideoFileMetadata(metadata) | 129 | return new VideoFileMetadata(metadata) |
123 | } | 130 | } |
124 | 131 | ||
125 | async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise<number> { | 132 | async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData): Promise<number> { |
126 | const metadata = await getMetadataFromFile(path, existingProbe) | 133 | const metadata = await buildFileMetadata(path, existingProbe) |
127 | 134 | ||
128 | let bitrate = metadata.format.bit_rate as number | 135 | let bitrate = metadata.format.bit_rate as number |
129 | if (bitrate && !isNaN(bitrate)) return bitrate | 136 | if (bitrate && !isNaN(bitrate)) return bitrate |
130 | 137 | ||
131 | const videoStream = await getVideoStreamFromFile(path, existingProbe) | 138 | const videoStream = await getVideoStream(path, existingProbe) |
132 | if (!videoStream) return undefined | 139 | if (!videoStream) return undefined |
133 | 140 | ||
134 | bitrate = videoStream?.bit_rate | 141 | bitrate = videoStream?.bit_rate |
@@ -137,51 +144,30 @@ async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): | |||
137 | return undefined | 144 | return undefined |
138 | } | 145 | } |
139 | 146 | ||
140 | async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) { | 147 | async function getVideoStreamDuration (path: string, existingProbe?: FfprobeData) { |
141 | const metadata = await getMetadataFromFile(path, existingProbe) | 148 | const metadata = await buildFileMetadata(path, existingProbe) |
142 | 149 | ||
143 | return Math.round(metadata.format.duration) | 150 | return Math.round(metadata.format.duration) |
144 | } | 151 | } |
145 | 152 | ||
146 | async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) { | 153 | async function getVideoStream (path: string, existingProbe?: FfprobeData) { |
147 | const metadata = await getMetadataFromFile(path, existingProbe) | 154 | const metadata = await buildFileMetadata(path, existingProbe) |
148 | |||
149 | return metadata.streams.find(s => s.codec_type === 'video') || null | ||
150 | } | ||
151 | |||
152 | async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> { | ||
153 | const parsedAudio = await getAudioStream(path, probe) | ||
154 | |||
155 | if (!parsedAudio.audioStream) return true | ||
156 | |||
157 | if (parsedAudio.audioStream['codec_name'] !== 'aac') return false | ||
158 | |||
159 | const audioBitrate = parsedAudio.bitrate | ||
160 | if (!audioBitrate) return false | ||
161 | |||
162 | const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate) | ||
163 | if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false | ||
164 | |||
165 | const channelLayout = parsedAudio.audioStream['channel_layout'] | ||
166 | // Causes playback issues with Chrome | ||
167 | if (!channelLayout || channelLayout === 'unknown') return false | ||
168 | 155 | ||
169 | return true | 156 | return metadata.streams.find(s => s.codec_type === 'video') |
170 | } | 157 | } |
171 | 158 | ||
172 | // --------------------------------------------------------------------------- | 159 | // --------------------------------------------------------------------------- |
173 | 160 | ||
174 | export { | 161 | export { |
175 | getVideoStreamSize, | 162 | getVideoStreamDimensionsInfo, |
176 | getVideoFileResolution, | 163 | buildFileMetadata, |
177 | getMetadataFromFile, | ||
178 | getMaxAudioBitrate, | 164 | getMaxAudioBitrate, |
179 | getVideoStreamFromFile, | 165 | getVideoStream, |
180 | getDurationFromVideoFile, | 166 | getVideoStreamDuration, |
181 | getAudioStream, | 167 | getAudioStream, |
182 | getVideoFileFPS, | 168 | getVideoStreamFPS, |
183 | isAudioFile, | 169 | isAudioFile, |
184 | ffprobePromise, | 170 | ffprobePromise, |
185 | getVideoFileBitrate, | 171 | getVideoStreamBitrate, |
186 | canDoQuickAudioTranscode | 172 | hasAudioStream |
187 | } | 173 | } |