diff options
Diffstat (limited to 'server/lib/video-transcoding.ts')
-rw-r--r-- | server/lib/video-transcoding.ts | 131 |
1 files changed, 95 insertions, 36 deletions
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index e022f2a68..890b23a44 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' | 1 | import { copyFile, ensureDir, move, remove, stat, writeFile } from 'fs-extra' |
2 | import { basename, extname as extnameUtil, join } from 'path' | 2 | import { basename, extname as extnameUtil, join } from 'path' |
3 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 3 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
4 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' | 4 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' |
@@ -163,15 +163,104 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video | |||
163 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) | 163 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) |
164 | } | 164 | } |
165 | 165 | ||
166 | // Concat TS segments from a live video to a fragmented mp4 HLS playlist | ||
167 | async function generateHlsPlaylistFromTS (options: { | ||
168 | video: MVideoWithFile | ||
169 | replayDirectory: string | ||
170 | segmentFiles: string[] | ||
171 | resolution: VideoResolution | ||
172 | isPortraitMode: boolean | ||
173 | }) { | ||
174 | const concatFilePath = join(options.replayDirectory, 'concat.txt') | ||
175 | |||
176 | function cleaner () { | ||
177 | remove(concatFilePath) | ||
178 | .catch(err => logger.error('Cannot remove concat file in %s.', options.replayDirectory, { err })) | ||
179 | } | ||
180 | |||
181 | // First concat the ts files to a mp4 file | ||
182 | const content = options.segmentFiles.map(f => 'file ' + f) | ||
183 | .join('\n') | ||
184 | |||
185 | await writeFile(concatFilePath, content + '\n') | ||
186 | |||
187 | try { | ||
188 | const outputPath = await generateHlsPlaylistCommon({ | ||
189 | video: options.video, | ||
190 | resolution: options.resolution, | ||
191 | isPortraitMode: options.isPortraitMode, | ||
192 | inputPath: concatFilePath, | ||
193 | type: 'hls-from-ts' as 'hls-from-ts' | ||
194 | }) | ||
195 | |||
196 | cleaner() | ||
197 | |||
198 | return outputPath | ||
199 | } catch (err) { | ||
200 | cleaner() | ||
201 | |||
202 | throw err | ||
203 | } | ||
204 | } | ||
205 | |||
166 | // Generate an HLS playlist from an input file, and update the master playlist | 206 | // Generate an HLS playlist from an input file, and update the master playlist |
167 | async function generateHlsPlaylist (options: { | 207 | function generateHlsPlaylist (options: { |
168 | video: MVideoWithFile | 208 | video: MVideoWithFile |
169 | videoInputPath: string | 209 | videoInputPath: string |
170 | resolution: VideoResolution | 210 | resolution: VideoResolution |
171 | copyCodecs: boolean | 211 | copyCodecs: boolean |
172 | isPortraitMode: boolean | 212 | isPortraitMode: boolean |
173 | }) { | 213 | }) { |
174 | const { video, videoInputPath, resolution, copyCodecs, isPortraitMode } = options | 214 | return generateHlsPlaylistCommon({ |
215 | video: options.video, | ||
216 | resolution: options.resolution, | ||
217 | copyCodecs: options.copyCodecs, | ||
218 | isPortraitMode: options.isPortraitMode, | ||
219 | inputPath: options.videoInputPath, | ||
220 | type: 'hls' as 'hls' | ||
221 | }) | ||
222 | } | ||
223 | |||
224 | // --------------------------------------------------------------------------- | ||
225 | |||
226 | export { | ||
227 | generateHlsPlaylist, | ||
228 | generateHlsPlaylistFromTS, | ||
229 | optimizeOriginalVideofile, | ||
230 | transcodeNewResolution, | ||
231 | mergeAudioVideofile | ||
232 | } | ||
233 | |||
234 | // --------------------------------------------------------------------------- | ||
235 | |||
236 | async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) { | ||
237 | const stats = await stat(transcodingPath) | ||
238 | const fps = await getVideoFileFPS(transcodingPath) | ||
239 | const metadata = await getMetadataFromFile(transcodingPath) | ||
240 | |||
241 | await move(transcodingPath, outputPath, { overwrite: true }) | ||
242 | |||
243 | videoFile.size = stats.size | ||
244 | videoFile.fps = fps | ||
245 | videoFile.metadata = metadata | ||
246 | |||
247 | await createTorrentAndSetInfoHash(video, videoFile) | ||
248 | |||
249 | await VideoFileModel.customUpsert(videoFile, 'video', undefined) | ||
250 | video.VideoFiles = await video.$get('VideoFiles') | ||
251 | |||
252 | return video | ||
253 | } | ||
254 | |||
255 | async function generateHlsPlaylistCommon (options: { | ||
256 | type: 'hls' | 'hls-from-ts' | ||
257 | video: MVideoWithFile | ||
258 | inputPath: string | ||
259 | resolution: VideoResolution | ||
260 | copyCodecs?: boolean | ||
261 | isPortraitMode: boolean | ||
262 | }) { | ||
263 | const { type, video, inputPath, resolution, copyCodecs, isPortraitMode } = options | ||
175 | 264 | ||
176 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 265 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
177 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) | 266 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) |
@@ -180,9 +269,9 @@ async function generateHlsPlaylist (options: { | |||
180 | const videoFilename = generateVideoStreamingPlaylistName(video.uuid, resolution) | 269 | const videoFilename = generateVideoStreamingPlaylistName(video.uuid, resolution) |
181 | 270 | ||
182 | const transcodeOptions = { | 271 | const transcodeOptions = { |
183 | type: 'hls' as 'hls', | 272 | type, |
184 | 273 | ||
185 | inputPath: videoInputPath, | 274 | inputPath, |
186 | outputPath, | 275 | outputPath, |
187 | 276 | ||
188 | availableEncoders, | 277 | availableEncoders, |
@@ -242,35 +331,5 @@ async function generateHlsPlaylist (options: { | |||
242 | await updateMasterHLSPlaylist(video) | 331 | await updateMasterHLSPlaylist(video) |
243 | await updateSha256VODSegments(video) | 332 | await updateSha256VODSegments(video) |
244 | 333 | ||
245 | return video | 334 | return outputPath |
246 | } | ||
247 | |||
248 | // --------------------------------------------------------------------------- | ||
249 | |||
250 | export { | ||
251 | generateHlsPlaylist, | ||
252 | optimizeOriginalVideofile, | ||
253 | transcodeNewResolution, | ||
254 | mergeAudioVideofile | ||
255 | } | ||
256 | |||
257 | // --------------------------------------------------------------------------- | ||
258 | |||
259 | async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) { | ||
260 | const stats = await stat(transcodingPath) | ||
261 | const fps = await getVideoFileFPS(transcodingPath) | ||
262 | const metadata = await getMetadataFromFile(transcodingPath) | ||
263 | |||
264 | await move(transcodingPath, outputPath, { overwrite: true }) | ||
265 | |||
266 | videoFile.size = stats.size | ||
267 | videoFile.fps = fps | ||
268 | videoFile.metadata = metadata | ||
269 | |||
270 | await createTorrentAndSetInfoHash(video, videoFile) | ||
271 | |||
272 | await VideoFileModel.customUpsert(videoFile, 'video', undefined) | ||
273 | video.VideoFiles = await video.$get('VideoFiles') | ||
274 | |||
275 | return video | ||
276 | } | 335 | } |