From ab14f0e0dca878dbaccc8f6a895a68e4269c9873 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 4 May 2023 15:55:51 +0200 Subject: Prefer video studio instead of video edition Clearer and easier to find in the project --- .../ffmpeg/ffmpeg-default-transcoding-profile.ts | 134 +++++++++++++++++++++ shared/ffmpeg/index.ts | 1 + 2 files changed, 135 insertions(+) create mode 100644 shared/ffmpeg/ffmpeg-default-transcoding-profile.ts (limited to 'shared/ffmpeg') diff --git a/shared/ffmpeg/ffmpeg-default-transcoding-profile.ts b/shared/ffmpeg/ffmpeg-default-transcoding-profile.ts new file mode 100644 index 000000000..f7fc49465 --- /dev/null +++ b/shared/ffmpeg/ffmpeg-default-transcoding-profile.ts @@ -0,0 +1,134 @@ +import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils' +import { buildStreamSuffix, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg' +import { EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models' + +const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => { + const { fps, inputRatio, inputBitrate, resolution } = options + + const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution }) + + return { + outputOptions: [ + ...getCommonOutputOptions(targetBitrate), + + `-r ${fps}` + ] + } +} + +const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => { + const { streamNum, fps, inputBitrate, inputRatio, resolution } = options + + const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution }) + + return { + outputOptions: [ + ...getCommonOutputOptions(targetBitrate, streamNum), + + `${buildStreamSuffix('-r:v', streamNum)} ${fps}`, + `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}` + ] + } +} + +const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => { + const probe = await ffprobePromise(input) + + const parsedAudio = await getAudioStream(input, probe) + + // We try to reduce the ceiling bitrate by making rough matches of bitrates + // Of course this is far from perfect, but it might save some space in the end + + const audioCodecName = parsedAudio.audioStream['codec_name'] + + const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) + + // Force stereo as it causes some issues with HLS playback in Chrome + const base = [ '-channel_layout', 'stereo' ] + + if (bitrate !== -1) { + return { outputOptions: base.concat([ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ]) } + } + + return { outputOptions: base } +} + +const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { + return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] } +} + +export function getDefaultAvailableEncoders () { + return { + vod: { + libx264: { + default: defaultX264VODOptionsBuilder + }, + aac: { + default: defaultAACOptionsBuilder + }, + libfdk_aac: { + default: defaultLibFDKAACVODOptionsBuilder + } + }, + live: { + libx264: { + default: defaultX264LiveOptionsBuilder + }, + aac: { + default: defaultAACOptionsBuilder + } + } + } +} + +export function getDefaultEncodersToTry () { + return { + vod: { + video: [ 'libx264' ], + audio: [ 'libfdk_aac', 'aac' ] + }, + + live: { + video: [ 'libx264' ], + audio: [ 'libfdk_aac', 'aac' ] + } + } +} + +// --------------------------------------------------------------------------- + +function getTargetBitrate (options: { + inputBitrate: number + resolution: VideoResolution + ratio: number + fps: number +}) { + const { inputBitrate, resolution, ratio, fps } = options + + const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio })) + const limit = getMinLimitBitrate({ resolution, fps, ratio }) + + return Math.max(limit, capped) +} + +function capBitrate (inputBitrate: number, targetBitrate: number) { + if (!inputBitrate) return targetBitrate + + // Add 30% margin to input bitrate + const inputBitrateWithMargin = inputBitrate + (inputBitrate * 0.3) + + return Math.min(targetBitrate, inputBitrateWithMargin) +} + +function getCommonOutputOptions (targetBitrate: number, streamNum?: number) { + return [ + `-preset veryfast`, + `${buildStreamSuffix('-maxrate:v', streamNum)} ${targetBitrate}`, + `${buildStreamSuffix('-bufsize:v', streamNum)} ${targetBitrate * 2}`, + + // NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it + `-b_strategy 1`, + // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 + `-bf 16` + ] +} diff --git a/shared/ffmpeg/index.ts b/shared/ffmpeg/index.ts index 07a7d5402..1dab292da 100644 --- a/shared/ffmpeg/index.ts +++ b/shared/ffmpeg/index.ts @@ -1,4 +1,5 @@ export * from './ffmpeg-command-wrapper' +export * from './ffmpeg-default-transcoding-profile' export * from './ffmpeg-edition' export * from './ffmpeg-images' export * from './ffmpeg-live' -- cgit v1.2.3