|
|
@@ -1,5 +1,5 @@ |
|
|
|
import { Job } from 'bull' |
|
|
|
import * as ffmpeg from 'fluent-ffmpeg' |
|
|
|
import ffmpeg, { FfmpegCommand, FilterSpecification, getAvailableEncoders } from 'fluent-ffmpeg' |
|
|
|
import { readFile, remove, writeFile } from 'fs-extra' |
|
|
|
import { dirname, join } from 'path' |
|
|
|
import { FFMPEG_NICE, VIDEO_LIVE } from '@server/initializers/constants' |
|
|
@@ -42,7 +42,7 @@ async function checkFFmpegEncoders (peertubeAvailableEncoders: AvailableEncoders |
|
|
|
return supportedEncoders |
|
|
|
} |
|
|
|
|
|
|
|
const getAvailableEncodersPromise = promisify0(ffmpeg.getAvailableEncoders) |
|
|
|
const getAvailableEncodersPromise = promisify0(getAvailableEncoders) |
|
|
|
const availableFFmpegEncoders = await getAvailableEncodersPromise() |
|
|
|
|
|
|
|
const searchEncoders = new Set<string>() |
|
|
@@ -191,7 +191,7 @@ type TranscodeOptions = |
|
|
|
| QuickTranscodeOptions |
|
|
|
|
|
|
|
const builders: { |
|
|
|
[ type in TranscodeOptionsType ]: (c: ffmpeg.FfmpegCommand, o?: TranscodeOptions) => Promise<ffmpeg.FfmpegCommand> | ffmpeg.FfmpegCommand |
|
|
|
[ type in TranscodeOptionsType ]: (c: FfmpegCommand, o?: TranscodeOptions) => Promise<FfmpegCommand> | FfmpegCommand |
|
|
|
} = { |
|
|
|
'quick-transcode': buildQuickTranscodeCommand, |
|
|
|
'hls': buildHLSVODCommand, |
|
|
@@ -241,7 +241,7 @@ async function getLiveTranscodingCommand (options: { |
|
|
|
|
|
|
|
const varStreamMap: string[] = [] |
|
|
|
|
|
|
|
const complexFilter: ffmpeg.FilterSpecification[] = [ |
|
|
|
const complexFilter: FilterSpecification[] = [ |
|
|
|
{ |
|
|
|
inputs: '[v:0]', |
|
|
|
filter: 'split', |
|
|
@@ -353,7 +353,7 @@ function buildStreamSuffix (base: string, streamNum?: number) { |
|
|
|
// --------------------------------------------------------------------------- |
|
|
|
|
|
|
|
function addDefaultEncoderGlobalParams (options: { |
|
|
|
command: ffmpeg.FfmpegCommand |
|
|
|
command: FfmpegCommand |
|
|
|
}) { |
|
|
|
const { command } = options |
|
|
|
|
|
|
@@ -370,7 +370,7 @@ function addDefaultEncoderGlobalParams (options: { |
|
|
|
} |
|
|
|
|
|
|
|
function addDefaultEncoderParams (options: { |
|
|
|
command: ffmpeg.FfmpegCommand |
|
|
|
command: FfmpegCommand |
|
|
|
encoder: 'libx264' | string |
|
|
|
streamNum?: number |
|
|
|
fps?: number |
|
|
@@ -390,7 +390,7 @@ function addDefaultEncoderParams (options: { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string, masterPlaylistName: string) { |
|
|
|
function addDefaultLiveHLSParams (command: FfmpegCommand, outPath: string, masterPlaylistName: string) { |
|
|
|
command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS) |
|
|
|
command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE) |
|
|
|
command.outputOption('-hls_flags delete_segments+independent_segments') |
|
|
@@ -405,7 +405,7 @@ function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string |
|
|
|
// Transcode VOD command builders |
|
|
|
// --------------------------------------------------------------------------- |
|
|
|
|
|
|
|
async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { |
|
|
|
async function buildx264VODCommand (command: FfmpegCommand, options: TranscodeOptions) { |
|
|
|
let fps = await getVideoFileFPS(options.inputPath) |
|
|
|
fps = computeFPS(fps, options.resolution) |
|
|
|
|
|
|
@@ -422,7 +422,7 @@ async function buildx264VODCommand (command: ffmpeg.FfmpegCommand, options: Tran |
|
|
|
return command |
|
|
|
} |
|
|
|
|
|
|
|
async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: MergeAudioTranscodeOptions) { |
|
|
|
async function buildAudioMergeCommand (command: FfmpegCommand, options: MergeAudioTranscodeOptions) { |
|
|
|
command = command.loop(undefined) |
|
|
|
|
|
|
|
const scaleFilterValue = getScaleCleanerValue() |
|
|
@@ -437,13 +437,13 @@ async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: M |
|
|
|
return command |
|
|
|
} |
|
|
|
|
|
|
|
function buildOnlyAudioCommand (command: ffmpeg.FfmpegCommand, _options: OnlyAudioTranscodeOptions) { |
|
|
|
function buildOnlyAudioCommand (command: FfmpegCommand, _options: OnlyAudioTranscodeOptions) { |
|
|
|
command = presetOnlyAudio(command) |
|
|
|
|
|
|
|
return command |
|
|
|
} |
|
|
|
|
|
|
|
function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { |
|
|
|
function buildQuickTranscodeCommand (command: FfmpegCommand) { |
|
|
|
command = presetCopy(command) |
|
|
|
|
|
|
|
command = command.outputOption('-map_metadata -1') // strip all metadata |
|
|
@@ -452,7 +452,7 @@ function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { |
|
|
|
return command |
|
|
|
} |
|
|
|
|
|
|
|
function addCommonHLSVODCommandOptions (command: ffmpeg.FfmpegCommand, outputPath: string) { |
|
|
|
function addCommonHLSVODCommandOptions (command: FfmpegCommand, outputPath: string) { |
|
|
|
return command.outputOption('-hls_time 4') |
|
|
|
.outputOption('-hls_list_size 0') |
|
|
|
.outputOption('-hls_playlist_type vod') |
|
|
@@ -462,7 +462,7 @@ function addCommonHLSVODCommandOptions (command: ffmpeg.FfmpegCommand, outputPat |
|
|
|
.outputOption('-hls_flags single_file') |
|
|
|
} |
|
|
|
|
|
|
|
async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { |
|
|
|
async function buildHLSVODCommand (command: FfmpegCommand, options: HLSTranscodeOptions) { |
|
|
|
const videoPath = getHLSVideoPath(options) |
|
|
|
|
|
|
|
if (options.copyCodecs) command = presetCopy(command) |
|
|
@@ -474,7 +474,7 @@ async function buildHLSVODCommand (command: ffmpeg.FfmpegCommand, options: HLSTr |
|
|
|
return command |
|
|
|
} |
|
|
|
|
|
|
|
function buildHLSVODFromTSCommand (command: ffmpeg.FfmpegCommand, options: HLSFromTSTranscodeOptions) { |
|
|
|
function buildHLSVODFromTSCommand (command: FfmpegCommand, options: HLSFromTSTranscodeOptions) { |
|
|
|
const videoPath = getHLSVideoPath(options) |
|
|
|
|
|
|
|
command.outputOption('-c copy') |
|
|
@@ -571,7 +571,7 @@ async function getEncoderBuilderResult (options: EncoderOptionsBuilderParams & { |
|
|
|
} |
|
|
|
|
|
|
|
async function presetVideo (options: { |
|
|
|
command: ffmpeg.FfmpegCommand |
|
|
|
command: FfmpegCommand |
|
|
|
input: string |
|
|
|
transcodeOptions: TranscodeOptions |
|
|
|
fps?: number |
|
|
@@ -640,21 +640,21 @@ async function presetVideo (options: { |
|
|
|
return localCommand |
|
|
|
} |
|
|
|
|
|
|
|
function presetCopy (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { |
|
|
|
function presetCopy (command: FfmpegCommand): FfmpegCommand { |
|
|
|
return command |
|
|
|
.format('mp4') |
|
|
|
.videoCodec('copy') |
|
|
|
.audioCodec('copy') |
|
|
|
} |
|
|
|
|
|
|
|
function presetOnlyAudio (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { |
|
|
|
function presetOnlyAudio (command: FfmpegCommand): FfmpegCommand { |
|
|
|
return command |
|
|
|
.format('mp4') |
|
|
|
.audioCodec('copy') |
|
|
|
.noVideo() |
|
|
|
} |
|
|
|
|
|
|
|
function applyEncoderOptions (command: ffmpeg.FfmpegCommand, options: EncoderOptions): ffmpeg.FfmpegCommand { |
|
|
|
function applyEncoderOptions (command: FfmpegCommand, options: EncoderOptions): FfmpegCommand { |
|
|
|
return command |
|
|
|
.inputOptions(options.inputOptions ?? []) |
|
|
|
.outputOptions(options.outputOptions ?? []) |
|
|
@@ -714,7 +714,7 @@ function getFFmpegVersion () { |
|
|
|
} |
|
|
|
|
|
|
|
async function runCommand (options: { |
|
|
|
command: ffmpeg.FfmpegCommand |
|
|
|
command: FfmpegCommand |
|
|
|
silent?: boolean // false |
|
|
|
job?: Job |
|
|
|
}) { |
|
|
|