aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/ffmpeg-utils.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-12-02 10:07:26 +0100
committerChocobozzz <me@florianbigard.com>2020-12-02 10:18:15 +0100
commit2650d6d489f775a38c5c3fdb65daabc7d55c15b5 (patch)
tree3065083bd5e8889601423521710caf3c0e989811 /server/helpers/ffmpeg-utils.ts
parent543e18726214aa789acecba5615fff1bec83d4cc (diff)
downloadPeerTube-2650d6d489f775a38c5c3fdb65daabc7d55c15b5.tar.gz
PeerTube-2650d6d489f775a38c5c3fdb65daabc7d55c15b5.tar.zst
PeerTube-2650d6d489f775a38c5c3fdb65daabc7d55c15b5.zip
Fix live replay duration glitch
Diffstat (limited to 'server/helpers/ffmpeg-utils.ts')
-rw-r--r--server/helpers/ffmpeg-utils.ts76
1 files changed, 39 insertions, 37 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 085635b5a..c6b8a0eb0 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -110,7 +110,7 @@ async function generateImageFromVideoFile (fromPath: string, folder: string, ima
110// Transcode meta function 110// Transcode meta function
111// --------------------------------------------------------------------------- 111// ---------------------------------------------------------------------------
112 112
113type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio' | 'only-audio' 113type TranscodeOptionsType = 'hls' | 'hls-from-ts' | 'quick-transcode' | 'video' | 'merge-audio' | 'only-audio'
114 114
115interface BaseTranscodeOptions { 115interface BaseTranscodeOptions {
116 type: TranscodeOptionsType 116 type: TranscodeOptionsType
@@ -134,6 +134,14 @@ interface HLSTranscodeOptions extends BaseTranscodeOptions {
134 } 134 }
135} 135}
136 136
137interface HLSFromTSTranscodeOptions extends BaseTranscodeOptions {
138 type: 'hls-from-ts'
139
140 hlsPlaylist: {
141 videoFilename: string
142 }
143}
144
137interface QuickTranscodeOptions extends BaseTranscodeOptions { 145interface QuickTranscodeOptions extends BaseTranscodeOptions {
138 type: 'quick-transcode' 146 type: 'quick-transcode'
139} 147}
@@ -153,6 +161,7 @@ interface OnlyAudioTranscodeOptions extends BaseTranscodeOptions {
153 161
154type TranscodeOptions = 162type TranscodeOptions =
155 HLSTranscodeOptions 163 HLSTranscodeOptions
164 | HLSFromTSTranscodeOptions
156 | VideoTranscodeOptions 165 | VideoTranscodeOptions
157 | MergeAudioTranscodeOptions 166 | MergeAudioTranscodeOptions
158 | OnlyAudioTranscodeOptions 167 | OnlyAudioTranscodeOptions
@@ -163,6 +172,7 @@ const builders: {
163} = { 172} = {
164 'quick-transcode': buildQuickTranscodeCommand, 173 'quick-transcode': buildQuickTranscodeCommand,
165 'hls': buildHLSVODCommand, 174 'hls': buildHLSVODCommand,
175 'hls-from-ts': buildHLSVODFromTSCommand,
166 'merge-audio': buildAudioMergeCommand, 176 'merge-audio': buildAudioMergeCommand,
167 'only-audio': buildOnlyAudioCommand, 177 'only-audio': buildOnlyAudioCommand,
168 'video': buildx264VODCommand 178 'video': buildx264VODCommand
@@ -292,31 +302,6 @@ function getLiveMuxingCommand (rtmpUrl: string, outPath: string) {
292 return command 302 return command
293} 303}
294 304
295async function hlsPlaylistToFragmentedMP4 (replayDirectory: string, segmentFiles: string[], outputPath: string) {
296 const concatFilePath = join(replayDirectory, 'concat.txt')
297
298 function cleaner () {
299 remove(concatFilePath)
300 .catch(err => logger.error('Cannot remove concat file in %s.', replayDirectory, { err }))
301 }
302
303 // First concat the ts files to a mp4 file
304 const content = segmentFiles.map(f => 'file ' + f)
305 .join('\n')
306
307 await writeFile(concatFilePath, content + '\n')
308
309 const command = getFFmpeg(concatFilePath)
310 command.inputOption('-safe 0')
311 command.inputOption('-f concat')
312
313 command.outputOption('-c:v copy')
314 command.audioFilter('aresample=async=1:first_pts=0')
315 command.output(outputPath)
316
317 return runCommand(command, cleaner)
318}
319
320function buildStreamSuffix (base: string, streamNum?: number) { 305function buildStreamSuffix (base: string, streamNum?: number) {
321 if (streamNum !== undefined) { 306 if (streamNum !== undefined) {
322 return `${base}:${streamNum}` 307 return `${base}:${streamNum}`
@@ -336,8 +321,7 @@ export {
336 generateImageFromVideoFile, 321 generateImageFromVideoFile,
337 TranscodeOptions, 322 TranscodeOptions,
338 TranscodeOptionsType, 323 TranscodeOptionsType,
339 transcode, 324 transcode
340 hlsPlaylistToFragmentedMP4
341} 325}
342 326
343// --------------------------------------------------------------------------- 327// ---------------------------------------------------------------------------
@@ -447,6 +431,16 @@ function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) {
447 return command 431 return command
448} 432}
449 433
434function addCommonHLSVODCommandOptions (command: ffmpeg.FfmpegCommand, outputPath: string) {
435 return command.outputOption('-hls_time 4')
436 .outputOption('-hls_list_size 0')
437 .outputOption('-hls_playlist_type vod')
438 .outputOption('-hls_segment_filename ' + outputPath)
439 .outputOption('-hls_segment_type fmp4')
440 .outputOption('-f hls')
441 .outputOption('-hls_flags single_file')
442}
443
450async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { 444async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) {
451 const videoPath = getHLSVideoPath(options) 445 const videoPath = getHLSVideoPath(options)
452 446
@@ -454,19 +448,27 @@ async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTr
454 else if (options.resolution === VideoResolution.H_NOVIDEO) command = presetOnlyAudio(command) 448 else if (options.resolution === VideoResolution.H_NOVIDEO) command = presetOnlyAudio(command)
455 else command = await buildx264VODCommand(command, options) 449 else command = await buildx264VODCommand(command, options)
456 450
457 command = command.outputOption('-hls_time 4') 451 addCommonHLSVODCommandOptions(command, videoPath)
458 .outputOption('-hls_list_size 0') 452
459 .outputOption('-hls_playlist_type vod') 453 return command
460 .outputOption('-hls_segment_filename ' + videoPath) 454}
461 .outputOption('-hls_segment_type fmp4') 455
462 .outputOption('-f hls') 456async function buildHLSVODFromTSCommand (command: ffmpeg.FfmpegCommand, options: HLSFromTSTranscodeOptions) {
463 .outputOption('-hls_flags single_file') 457 const videoPath = getHLSVideoPath(options)
458
459 command.inputOption('-safe 0')
460 command.inputOption('-f concat')
461
462 command.outputOption('-c:v copy')
463 command.audioFilter('aresample=async=1:first_pts=0')
464
465 addCommonHLSVODCommandOptions(command, videoPath)
464 466
465 return command 467 return command
466} 468}
467 469
468async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) { 470async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) {
469 if (options.type !== 'hls') return 471 if (options.type !== 'hls' && options.type !== 'hls-from-ts') return
470 472
471 const fileContent = await readFile(options.outputPath) 473 const fileContent = await readFile(options.outputPath)
472 474
@@ -480,7 +482,7 @@ async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) {
480 await writeFile(options.outputPath, newContent) 482 await writeFile(options.outputPath, newContent)
481} 483}
482 484
483function getHLSVideoPath (options: HLSTranscodeOptions) { 485function getHLSVideoPath (options: HLSTranscodeOptions | HLSFromTSTranscodeOptions) {
484 return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}` 486 return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}`
485} 487}
486 488