1 import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils'
2 import { buildStreamSuffix, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg'
3 import { EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models'
5 const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
6 const { fps, inputRatio, inputBitrate, resolution } = options
8 const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
12 ...getCommonOutputOptions(targetBitrate),
19 const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
20 const { streamNum, fps, inputBitrate, inputRatio, resolution } = options
22 const targetBitrate = getTargetBitrate({ inputBitrate, ratio: inputRatio, fps, resolution })
26 ...getCommonOutputOptions(targetBitrate, streamNum),
28 `${buildStreamSuffix('-r:v', streamNum)} ${fps}`,
29 `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`
34 const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => {
35 const probe = await ffprobePromise(input)
37 const parsedAudio = await getAudioStream(input, probe)
39 // We try to reduce the ceiling bitrate by making rough matches of bitrates
40 // Of course this is far from perfect, but it might save some space in the end
42 const audioCodecName = parsedAudio.audioStream['codec_name']
44 const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
46 // Force stereo as it causes some issues with HLS playback in Chrome
47 const base = [ '-channel_layout', 'stereo' ]
50 return { outputOptions: base.concat([ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ]) }
53 return { outputOptions: base }
56 const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
57 return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
60 export function getAvailableEncoders () {
64 default: defaultX264VODOptionsBuilder
67 default: defaultAACOptionsBuilder
70 default: defaultLibFDKAACVODOptionsBuilder
75 default: defaultX264LiveOptionsBuilder
78 default: defaultAACOptionsBuilder
84 export function getEncodersToTry () {
88 audio: [ 'libfdk_aac', 'aac' ]
93 audio: [ 'libfdk_aac', 'aac' ]
98 // ---------------------------------------------------------------------------
100 function getTargetBitrate (options: {
102 resolution: VideoResolution
106 const { inputBitrate, resolution, ratio, fps } = options
108 const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio }))
109 const limit = getMinLimitBitrate({ resolution, fps, ratio })
111 return Math.max(limit, capped)
114 function capBitrate (inputBitrate: number, targetBitrate: number) {
115 if (!inputBitrate) return targetBitrate
117 // Add 30% margin to input bitrate
118 const inputBitrateWithMargin = inputBitrate + (inputBitrate * 0.3)
120 return Math.min(targetBitrate, inputBitrateWithMargin)
123 function getCommonOutputOptions (targetBitrate: number, streamNum?: number) {
126 `${buildStreamSuffix('-maxrate:v', streamNum)} ${targetBitrate}`,
127 `${buildStreamSuffix('-bufsize:v', streamNum)} ${targetBitrate * 2}`,
129 // NOTE: b-strategy 1 - heuristic algorithm, 16 is optimal B-frames for it
131 // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16