diff --git a/packages/ffmpeg/src/ffmpeg-default-transcoding-profile.ts b/packages/ffmpeg/src/ffmpeg-default-transcoding-profile.ts index 26ddf5c22..7fc2a4c0d 100644 --- a/packages/ffmpeg/src/ffmpeg-default-transcoding-profile.ts +++ b/packages/ffmpeg/src/ffmpeg-default-transcoding-profile.ts @@ -2,7 +2,7 @@ import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate, getMinTheoretic import { buildStreamSuffix, getAudioStream, - getMaxAudioBitrate, + getMaxAudioKBitrate, getVideoStream, getVideoStreamBitrate, getVideoStreamDimensionsInfo, @@ -42,7 +42,7 @@ const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOp const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio, inputProbe }) => { if (canCopyAudio && await canDoQuickAudioTranscode(input, inputProbe)) { - return { copy: true, outputOptions: [ ] } + return { copy: true, outputOptions: [] } } const parsedAudio = await getAudioStream(input, inputProbe) @@ -52,7 +52,7 @@ const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNu const audioCodecName = parsedAudio.audioStream['codec_name'] - const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) + const bitrate = getMaxAudioKBitrate(audioCodecName, parsedAudio.bitrate) // Force stereo as it causes some issues with HLS playback in Chrome const base = [ '-channel_layout', 'stereo' ] @@ -116,7 +116,7 @@ export async function canDoQuickAudioTranscode (path: string, probe?: FfprobeDat const audioBitrate = parsedAudio.bitrate if (!audioBitrate) return false - const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate) + const maxAudioBitrate = getMaxAudioKBitrate('aac', audioBitrate) if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false const channelLayout = parsedAudio.audioStream['channel_layout'] diff --git a/packages/ffmpeg/src/ffprobe.ts b/packages/ffmpeg/src/ffprobe.ts index c2b5753d7..ad0f25da4 100644 --- a/packages/ffmpeg/src/ffprobe.ts +++ b/packages/ffmpeg/src/ffprobe.ts @@ -6,7 +6,7 @@ import ffmpeg, { FfprobeData } from 'fluent-ffmpeg' * Helpers to run ffprobe and extract data from the JSON output */ -function ffprobePromise (path: string) { +export function ffprobePromise (path: string) { return new Promise((res, rej) => { ffmpeg.ffprobe(path, [ '-show_chapters' ], (err, data) => { if (err) return rej(err) @@ -62,7 +62,7 @@ const imageCodecs = new Set([ 'xwd' ]) -async function isAudioFile (path: string, existingProbe?: FfprobeData) { +export async function isAudioFile (path: string, existingProbe?: FfprobeData) { const videoStream = await getVideoStream(path, existingProbe) if (!videoStream) return true @@ -71,13 +71,13 @@ async function isAudioFile (path: string, existingProbe?: FfprobeData) { return false } -async function hasAudioStream (path: string, existingProbe?: FfprobeData) { +export async function hasAudioStream (path: string, existingProbe?: FfprobeData) { const { audioStream } = await getAudioStream(path, existingProbe) return !!audioStream } -async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { +export async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { const data = existingProbe || await ffprobePromise(videoPath) if (Array.isArray(data.streams)) { @@ -95,45 +95,34 @@ async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { return { absolutePath: data.format.filename } } -function getMaxAudioBitrate (type: string, bitrate: number) { +export function getMaxAudioKBitrate (type: string, bitrate: number) { const maxKBitrate = 384 - const kToBits = (kbits: number) => kbits * 1000 // If we did not manage to get the bitrate, use an average value - if (!bitrate) return 256 + if (!bitrate) { + // We expect uploader wants a high quality audio if the input is in FLAC format + if (type === 'flac') return maxKBitrate + + return 256 + } + + const kBitrate = Math.round(bitrate / 1000) if (type === 'aac') { - switch (true) { - case bitrate > kToBits(maxKBitrate): - return maxKBitrate + if (kBitrate > maxKBitrate) return maxKBitrate - default: - return -1 // we interpret it as a signal to copy the audio stream as is - } + // We interpret it as a signal to copy the audio stream as is + return -1 } - /* - a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac. - That's why, when using aac, we can go to lower kbit/sec. The equivalences - made here are not made to be accurate, especially with good mp3 encoders. - */ - switch (true) { - case bitrate <= kToBits(192): - return 128 - - case bitrate <= kToBits(384): - return 256 - - default: - return maxKBitrate - } + return Math.min(maxKBitrate, kBitrate) } // --------------------------------------------------------------------------- // Video // --------------------------------------------------------------------------- -async function getVideoStreamDimensionsInfo (path: string, existingProbe?: FfprobeData) { +export async function getVideoStreamDimensionsInfo (path: string, existingProbe?: FfprobeData) { const videoStream = await getVideoStream(path, existingProbe) if (!videoStream) { return { @@ -164,7 +153,7 @@ async function getVideoStreamDimensionsInfo (path: string, existingProbe?: Ffpro } } -async function getVideoStreamFPS (path: string, existingProbe?: FfprobeData) { +export async function getVideoStreamFPS (path: string, existingProbe?: FfprobeData) { const videoStream = await getVideoStream(path, existingProbe) if (!videoStream) return 0 @@ -182,7 +171,7 @@ async function getVideoStreamFPS (path: string, existingProbe?: FfprobeData) { return 0 } -async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData): Promise { +export async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData): Promise { const metadata = existingProbe || await ffprobePromise(path) let bitrate = metadata.format.bit_rate @@ -197,19 +186,19 @@ async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData) return undefined } -async function getVideoStreamDuration (path: string, existingProbe?: FfprobeData) { +export async function getVideoStreamDuration (path: string, existingProbe?: FfprobeData) { const metadata = existingProbe || await ffprobePromise(path) return Math.round(metadata.format.duration) } -async function getVideoStream (path: string, existingProbe?: FfprobeData) { +export async function getVideoStream (path: string, existingProbe?: FfprobeData) { const metadata = existingProbe || await ffprobePromise(path) return metadata.streams.find(s => s.codec_type === 'video') } -async function hasVideoStream (path: string, existingProbe?: FfprobeData) { +export async function hasVideoStream (path: string, existingProbe?: FfprobeData) { const videoStream = await getVideoStream(path, existingProbe) return !!videoStream @@ -219,7 +208,7 @@ async function hasVideoStream (path: string, existingProbe?: FfprobeData) { // Chapters // --------------------------------------------------------------------------- -async function getChaptersFromContainer (options: { +export async function getChaptersFromContainer (options: { path: string maxTitleLength: number ffprobe?: FfprobeData @@ -236,20 +225,3 @@ async function getChaptersFromContainer (options: { title: (c['TAG:title'] || '').slice(0, maxTitleLength) })) } - -// --------------------------------------------------------------------------- - -export { - ffprobePromise, - getAudioStream, - getChaptersFromContainer, - getMaxAudioBitrate, - getVideoStream, - getVideoStreamBitrate, - getVideoStreamDimensionsInfo, - getVideoStreamDuration, - getVideoStreamFPS, - hasAudioStream, - hasVideoStream, - isAudioFile -}