mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 01:39:37 +02:00
Improve audio transcoding
ffmpeg aac encoder is not very good so prefer to keep the same bitrate as mp3 we also use the max available bitrate with a flac input that has an unknown bitrate
This commit is contained in:
parent
445866967f
commit
29a88c0dde
2 changed files with 28 additions and 56 deletions
|
@ -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']
|
||||
|
|
|
@ -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<FfprobeData>((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<number> {
|
||||
export async function getVideoStreamBitrate (path: string, existingProbe?: FfprobeData): Promise<number> {
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue