Fix audio transcoding copy
This commit is contained in:
parent
dd3f99434c
commit
e7d8e2b245
|
@ -11,7 +11,7 @@ export function getFFmpegCommandWrapperOptions (type: CommandType, availableEnco
|
|||
availableEncoders,
|
||||
profile: getProfile(type),
|
||||
|
||||
niceness: FFMPEG_NICE[type],
|
||||
niceness: FFMPEG_NICE[type.toUpperCase()],
|
||||
tmpDirectory: CONFIG.STORAGE.TMP_DIR,
|
||||
threads: getThreads(type),
|
||||
|
||||
|
|
|
@ -469,7 +469,7 @@ const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = {
|
|||
DISLIKE: 'dislike'
|
||||
}
|
||||
|
||||
const FFMPEG_NICE: { [ id: string ]: number } = {
|
||||
const FFMPEG_NICE = {
|
||||
// parent process defaults to niceness = 0
|
||||
// reminder: lower = higher priority, max value is 19, lowest is -20
|
||||
LIVE: 5, // prioritize over VOD and THUMBNAIL
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
import { FfprobeData } from 'fluent-ffmpeg'
|
||||
import { CONFIG } from '@server/initializers/config'
|
||||
import { VIDEO_TRANSCODING_FPS } from '@server/initializers/constants'
|
||||
import { getMaxBitrate } from '@shared/core-utils'
|
||||
import {
|
||||
ffprobePromise,
|
||||
getAudioStream,
|
||||
getMaxAudioBitrate,
|
||||
getVideoStream,
|
||||
getVideoStreamBitrate,
|
||||
getVideoStreamDimensionsInfo,
|
||||
getVideoStreamFPS
|
||||
} from '@shared/ffmpeg'
|
||||
import { canDoQuickAudioTranscode, canDoQuickVideoTranscode, ffprobePromise } from '@shared/ffmpeg'
|
||||
|
||||
export async function canDoQuickTranscode (path: string, existingProbe?: FfprobeData): Promise<boolean> {
|
||||
if (CONFIG.TRANSCODING.PROFILE !== 'default') return false
|
||||
|
@ -20,42 +10,3 @@ export async function canDoQuickTranscode (path: string, existingProbe?: Ffprobe
|
|||
return await canDoQuickVideoTranscode(path, probe) &&
|
||||
await canDoQuickAudioTranscode(path, probe)
|
||||
}
|
||||
|
||||
export async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
|
||||
const parsedAudio = await getAudioStream(path, probe)
|
||||
|
||||
if (!parsedAudio.audioStream) return true
|
||||
|
||||
if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
|
||||
|
||||
const audioBitrate = parsedAudio.bitrate
|
||||
if (!audioBitrate) return false
|
||||
|
||||
const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
|
||||
if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
|
||||
|
||||
const channelLayout = parsedAudio.audioStream['channel_layout']
|
||||
// Causes playback issues with Chrome
|
||||
if (!channelLayout || channelLayout === 'unknown' || channelLayout === 'quad') return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
|
||||
const videoStream = await getVideoStream(path, probe)
|
||||
const fps = await getVideoStreamFPS(path, probe)
|
||||
const bitRate = await getVideoStreamBitrate(path, probe)
|
||||
const resolutionData = await getVideoStreamDimensionsInfo(path, probe)
|
||||
|
||||
// If ffprobe did not manage to guess the bitrate
|
||||
if (!bitRate) return false
|
||||
|
||||
// check video params
|
||||
if (!videoStream) return false
|
||||
if (videoStream['codec_name'] !== 'h264') return false
|
||||
if (videoStream['pix_fmt'] !== 'yuv420p') return false
|
||||
if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false
|
||||
if (bitRate > getMaxBitrate({ ...resolutionData, fps })) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { expect } from 'chai'
|
||||
import { canDoQuickTranscode } from '@server/lib/transcoding/transcoding-quick-transcode'
|
||||
import { checkWebTorrentWorks, generateHighBitrateVideo, generateVideoWithFramerate } from '@server/tests/shared'
|
||||
import { buildAbsoluteFixturePath, getAllFiles, getMaxBitrate, getMinLimitBitrate, omit } from '@shared/core-utils'
|
||||
import { buildAbsoluteFixturePath, getAllFiles, getMaxTheoreticalBitrate, getMinTheoreticalBitrate, omit } from '@shared/core-utils'
|
||||
import {
|
||||
ffprobePromise,
|
||||
getAudioStream,
|
||||
|
@ -564,7 +564,7 @@ describe('Test video transcoding', function () {
|
|||
|
||||
expect(resolution).to.equal(resolution)
|
||||
|
||||
const maxBitrate = getMaxBitrate({ ...dataResolution, fps })
|
||||
const maxBitrate = getMaxTheoreticalBitrate({ ...dataResolution, fps })
|
||||
expect(bitrate).to.be.below(maxBitrate)
|
||||
}
|
||||
}
|
||||
|
@ -611,7 +611,7 @@ describe('Test video transcoding', function () {
|
|||
const bitrate = await getVideoStreamBitrate(path)
|
||||
|
||||
const inputBitrate = 60_000
|
||||
const limit = getMinLimitBitrate({ fps: 10, ratio: 1, resolution: r })
|
||||
const limit = getMinTheoreticalBitrate({ fps: 10, ratio: 1, resolution: r })
|
||||
let belowValue = Math.max(inputBitrate, limit)
|
||||
belowValue += belowValue * 0.20 // Apply 20% margin because bitrate control is not very precise
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { expect } from 'chai'
|
||||
import { snakeCase } from 'lodash'
|
||||
import validator from 'validator'
|
||||
import { getAverageBitrate, getMaxBitrate } from '@shared/core-utils'
|
||||
import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate } from '@shared/core-utils'
|
||||
import { VideoResolution } from '@shared/models'
|
||||
import { objectConverter, parseBytes, parseDurationToMs } from '../../helpers/core-utils'
|
||||
|
||||
|
@ -128,7 +128,7 @@ describe('Bitrate', function () {
|
|||
]
|
||||
|
||||
for (const test of tests) {
|
||||
expect(getMaxBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
|
||||
expect(getMaxTheoreticalBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -144,7 +144,7 @@ describe('Bitrate', function () {
|
|||
]
|
||||
|
||||
for (const test of tests) {
|
||||
expect(getAverageBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
|
||||
expect(getAverageTheoreticalBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -145,7 +145,7 @@ describe('Test Live transcoding in peertube-runner program', function () {
|
|||
const registrationToken = await servers[0].runnerRegistrationTokens.getFirstRegistrationToken()
|
||||
|
||||
peertubeRunner = new PeerTubeRunnerProcess()
|
||||
await peertubeRunner.runServer({ hideLogs: false })
|
||||
await peertubeRunner.runServer()
|
||||
await peertubeRunner.registerPeerTubeInstance({ server: servers[0], registrationToken, runnerName: 'runner' })
|
||||
})
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ describe('Test studio transcoding in peertube-runner program', function () {
|
|||
const registrationToken = await servers[0].runnerRegistrationTokens.getFirstRegistrationToken()
|
||||
|
||||
peertubeRunner = new PeerTubeRunnerProcess()
|
||||
await peertubeRunner.runServer({ hideLogs: false })
|
||||
await peertubeRunner.runServer()
|
||||
await peertubeRunner.registerPeerTubeInstance({ server: servers[0], registrationToken, runnerName: 'runner' })
|
||||
})
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { expect } from 'chai'
|
|||
import ffmpeg from 'fluent-ffmpeg'
|
||||
import { ensureDir, pathExists } from 'fs-extra'
|
||||
import { dirname } from 'path'
|
||||
import { buildAbsoluteFixturePath, getMaxBitrate } from '@shared/core-utils'
|
||||
import { buildAbsoluteFixturePath, getMaxTheoreticalBitrate } from '@shared/core-utils'
|
||||
import { getVideoStreamBitrate, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@shared/ffmpeg'
|
||||
|
||||
async function ensureHasTooBigBitrate (fixturePath: string) {
|
||||
|
@ -10,7 +10,7 @@ async function ensureHasTooBigBitrate (fixturePath: string) {
|
|||
const dataResolution = await getVideoStreamDimensionsInfo(fixturePath)
|
||||
const fps = await getVideoStreamFPS(fixturePath)
|
||||
|
||||
const maxBitrate = getMaxBitrate({ ...dataResolution, fps })
|
||||
const maxBitrate = getMaxTheoreticalBitrate({ ...dataResolution, fps })
|
||||
expect(bitrate).to.be.above(maxBitrate)
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ const maxBitPerPixel: BitPerPixel = {
|
|||
[VideoResolution.H_4K]: 0.14
|
||||
}
|
||||
|
||||
function getAverageBitrate (options: {
|
||||
function getAverageTheoreticalBitrate (options: {
|
||||
resolution: VideoResolution
|
||||
ratio: number
|
||||
fps: number
|
||||
|
@ -51,7 +51,7 @@ function getAverageBitrate (options: {
|
|||
return targetBitrate
|
||||
}
|
||||
|
||||
function getMaxBitrate (options: {
|
||||
function getMaxTheoreticalBitrate (options: {
|
||||
resolution: VideoResolution
|
||||
ratio: number
|
||||
fps: number
|
||||
|
@ -62,7 +62,7 @@ function getMaxBitrate (options: {
|
|||
return targetBitrate
|
||||
}
|
||||
|
||||
function getMinLimitBitrate (options: {
|
||||
function getMinTheoreticalBitrate (options: {
|
||||
resolution: VideoResolution
|
||||
ratio: number
|
||||
fps: number
|
||||
|
@ -76,9 +76,9 @@ function getMinLimitBitrate (options: {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getAverageBitrate,
|
||||
getMaxBitrate,
|
||||
getMinLimitBitrate
|
||||
getAverageTheoreticalBitrate,
|
||||
getMaxTheoreticalBitrate,
|
||||
getMinTheoreticalBitrate
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import { getAverageBitrate, getMinLimitBitrate } from '@shared/core-utils'
|
||||
import { buildStreamSuffix, ffprobePromise, getAudioStream, getMaxAudioBitrate } from '@shared/ffmpeg'
|
||||
import { FfprobeData } from 'fluent-ffmpeg'
|
||||
import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate, getMinTheoreticalBitrate } from '@shared/core-utils'
|
||||
import {
|
||||
buildStreamSuffix,
|
||||
ffprobePromise,
|
||||
getAudioStream,
|
||||
getMaxAudioBitrate,
|
||||
getVideoStream,
|
||||
getVideoStreamBitrate,
|
||||
getVideoStreamDimensionsInfo,
|
||||
getVideoStreamFPS
|
||||
} from '@shared/ffmpeg'
|
||||
import { EncoderOptionsBuilder, EncoderOptionsBuilderParams, VideoResolution } from '@shared/models'
|
||||
|
||||
const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
|
||||
|
@ -34,6 +44,10 @@ const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOp
|
|||
const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => {
|
||||
const probe = await ffprobePromise(input)
|
||||
|
||||
if (canCopyAudio && await canDoQuickAudioTranscode(input, probe)) {
|
||||
return { copy: true, outputOptions: [ ] }
|
||||
}
|
||||
|
||||
const parsedAudio = await getAudioStream(input, probe)
|
||||
|
||||
// We try to reduce the ceiling bitrate by making rough matches of bitrates
|
||||
|
@ -95,6 +109,45 @@ export function getDefaultEncodersToTry () {
|
|||
}
|
||||
}
|
||||
|
||||
export async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
|
||||
const parsedAudio = await getAudioStream(path, probe)
|
||||
|
||||
if (!parsedAudio.audioStream) return true
|
||||
|
||||
if (parsedAudio.audioStream['codec_name'] !== 'aac') return false
|
||||
|
||||
const audioBitrate = parsedAudio.bitrate
|
||||
if (!audioBitrate) return false
|
||||
|
||||
const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate)
|
||||
if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false
|
||||
|
||||
const channelLayout = parsedAudio.audioStream['channel_layout']
|
||||
// Causes playback issues with Chrome
|
||||
if (!channelLayout || channelLayout === 'unknown' || channelLayout === 'quad') return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Promise<boolean> {
|
||||
const videoStream = await getVideoStream(path, probe)
|
||||
const fps = await getVideoStreamFPS(path, probe)
|
||||
const bitRate = await getVideoStreamBitrate(path, probe)
|
||||
const resolutionData = await getVideoStreamDimensionsInfo(path, probe)
|
||||
|
||||
// If ffprobe did not manage to guess the bitrate
|
||||
if (!bitRate) return false
|
||||
|
||||
// check video params
|
||||
if (!videoStream) return false
|
||||
if (videoStream['codec_name'] !== 'h264') return false
|
||||
if (videoStream['pix_fmt'] !== 'yuv420p') return false
|
||||
if (fps < 2 || fps > 65) return false
|
||||
if (bitRate > getMaxTheoreticalBitrate({ ...resolutionData, fps })) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function getTargetBitrate (options: {
|
||||
|
@ -105,8 +158,8 @@ function getTargetBitrate (options: {
|
|||
}) {
|
||||
const { inputBitrate, resolution, ratio, fps } = options
|
||||
|
||||
const capped = capBitrate(inputBitrate, getAverageBitrate({ resolution, fps, ratio }))
|
||||
const limit = getMinLimitBitrate({ resolution, fps, ratio })
|
||||
const capped = capBitrate(inputBitrate, getAverageTheoreticalBitrate({ resolution, fps, ratio }))
|
||||
const limit = getMinTheoreticalBitrate({ resolution, fps, ratio })
|
||||
|
||||
return Math.max(limit, capped)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue