X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fffmpeg-utils.ts;h=3b794b8a29144167f996ea4b237536d6b5633dc6;hb=bd54ad1953ee0484ba90cf5f588f4c282048f368;hp=fac2595f1001559e79d7a5e0212cf177fdca77f4;hpb=c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index fac2595f1..3b794b8a2 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -5,7 +5,7 @@ import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' import { getMaxBitrate, getTargetBitrate, VideoResolution } from '../../shared/models/videos' import { checkFFmpegEncoders } from '../initializers/checker-before-init' import { CONFIG } from '../initializers/config' -import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' +import { FFMPEG_NICE, VIDEO_LIVE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { processImage } from './image-utils' import { logger } from './logger' @@ -353,7 +353,7 @@ function convertWebPToJPG (path: string, destination: string): Promise { }) } -function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: number[]) { +function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: number[], fps, deleteSegments: boolean) { const command = getFFmpeg(rtmpUrl) command.inputOption('-fflags nobuffer') @@ -375,23 +375,21 @@ function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: numb })) ]) - const liveFPS = VIDEO_TRANSCODING_FPS.AVERAGE - - command.withFps(liveFPS) - command.outputOption('-b_strategy 1') command.outputOption('-bf 16') command.outputOption('-preset superfast') command.outputOption('-level 3.1') command.outputOption('-map_metadata -1') command.outputOption('-pix_fmt yuv420p') + command.outputOption('-max_muxing_queue_size 1024') + command.outputOption('-g ' + (fps * 2)) for (let i = 0; i < resolutions.length; i++) { const resolution = resolutions[i] command.outputOption(`-map [vout${resolution}]`) command.outputOption(`-c:v:${i} libx264`) - command.outputOption(`-b:v:${i} ${getTargetBitrate(resolution, liveFPS, VIDEO_TRANSCODING_FPS)}`) + command.outputOption(`-b:v:${i} ${getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)}`) command.outputOption(`-map a:0`) command.outputOption(`-c:a:${i} aac`) @@ -399,7 +397,7 @@ function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: numb varStreamMap.push(`v:${i},a:${i}`) } - addDefaultLiveHLSParams(command, outPath) + addDefaultLiveHLSParams(command, outPath, deleteSegments) command.outputOption('-var_stream_map', varStreamMap.join(' ')) @@ -408,7 +406,7 @@ function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: numb return command } -function runLiveMuxing (rtmpUrl: string, outPath: string) { +function runLiveMuxing (rtmpUrl: string, outPath: string, deleteSegments: boolean) { const command = getFFmpeg(rtmpUrl) command.inputOption('-fflags nobuffer') @@ -417,13 +415,50 @@ function runLiveMuxing (rtmpUrl: string, outPath: string) { command.outputOption('-map 0:a?') command.outputOption('-map 0:v?') - addDefaultLiveHLSParams(command, outPath) + addDefaultLiveHLSParams(command, outPath, deleteSegments) command.run() return command } +async function hlsPlaylistToFragmentedMP4 (hlsDirectory: string, segmentFiles: string[], outputPath: string) { + const concatFile = 'concat.txt' + const concatFilePath = join(hlsDirectory, concatFile) + const content = segmentFiles.map(f => 'file ' + f) + .join('\n') + + await writeFile(concatFilePath, content + '\n') + + const command = getFFmpeg(concatFilePath) + command.inputOption('-safe 0') + command.inputOption('-f concat') + + command.outputOption('-c copy') + command.output(outputPath) + + command.run() + + function cleaner () { + remove(concatFilePath) + .catch(err => logger.error('Cannot remove concat file in %s.', hlsDirectory, { err })) + } + + return new Promise((res, rej) => { + command.on('error', err => { + cleaner() + + rej(err) + }) + + command.on('end', () => { + cleaner() + + res() + }) + }) +} + // --------------------------------------------------------------------------- export { @@ -443,6 +478,7 @@ export { getVideoFileFPS, computeResolutionsToTranscode, audio, + hlsPlaylistToFragmentedMP4, getVideoFileBitrate, canDoQuickTranscode } @@ -457,10 +493,14 @@ function addDefaultX264Params (command: ffmpeg.FfmpegCommand) { .outputOption('-map_metadata -1') // strip all metadata } -function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string) { - command.outputOption('-hls_time 4') - command.outputOption('-hls_list_size 15') - command.outputOption('-hls_flags delete_segments') +function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string, deleteSegments: boolean) { + command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS) + command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE) + + if (deleteSegments === true) { + command.outputOption('-hls_flags delete_segments') + } + command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%d.ts')}`) command.outputOption('-master_pl_name master.m3u8') command.outputOption(`-f hls`)