Optimize transcoding profile building

This commit is contained in:
Chocobozzz 2024-03-19 09:53:59 +01:00
parent 4e98d843da
commit c09e27d77a
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
10 changed files with 48 additions and 26 deletions

View File

@ -107,7 +107,8 @@ export class ProcessLiveRTMPHLSTranscoding {
bitrate, bitrate,
ratio, ratio,
hasAudio hasAudio,
probe
}) })
logger.info(`Running live transcoding for ${payload.input.rtmpUrl}`) logger.info(`Running live transcoding for ${payload.input.rtmpUrl}`)

View File

@ -73,7 +73,7 @@ export class FFmpegCommandWrapper {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
debugLog (msg: string, meta: any) { debugLog (msg: string, meta: any = {}) {
this.logger.debug(msg, { ...meta, ...this.lTags }) this.logger.debug(msg, { ...meta, ...this.lTags })
} }
@ -199,6 +199,7 @@ export class FFmpegCommandWrapper {
'canCopyVideo', 'canCopyVideo',
'resolution', 'resolution',
'inputBitrate', 'inputBitrate',
'inputProbe',
'fps', 'fps',
'inputRatio', 'inputRatio',
'streamNum' 'streamNum'

View File

@ -1,8 +1,6 @@
import { FfprobeData } from 'fluent-ffmpeg'
import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate, getMinTheoreticalBitrate } from '@peertube/peertube-core-utils' import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate, getMinTheoreticalBitrate } from '@peertube/peertube-core-utils'
import { import {
buildStreamSuffix, buildStreamSuffix,
ffprobePromise,
getAudioStream, getAudioStream,
getMaxAudioBitrate, getMaxAudioBitrate,
getVideoStream, getVideoStream,
@ -11,6 +9,7 @@ import {
getVideoStreamFPS getVideoStreamFPS
} from '@peertube/peertube-ffmpeg' } from '@peertube/peertube-ffmpeg'
import { EncoderOptionsBuilder, EncoderOptionsBuilderParams } from '@peertube/peertube-models' import { EncoderOptionsBuilder, EncoderOptionsBuilderParams } from '@peertube/peertube-models'
import { FfprobeData } from 'fluent-ffmpeg'
const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => { const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOptionsBuilderParams) => {
const { fps, inputRatio, inputBitrate, resolution } = options const { fps, inputRatio, inputBitrate, resolution } = options
@ -41,14 +40,12 @@ const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = (options: EncoderOp
} }
} }
const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio }) => { const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum, canCopyAudio, inputProbe }) => {
const probe = await ffprobePromise(input) if (canCopyAudio && await canDoQuickAudioTranscode(input, inputProbe)) {
if (canCopyAudio && await canDoQuickAudioTranscode(input, probe)) {
return { copy: true, outputOptions: [ ] } return { copy: true, outputOptions: [ ] }
} }
const parsedAudio = await getAudioStream(input, probe) const parsedAudio = await getAudioStream(input, inputProbe)
// We try to reduce the ceiling bitrate by making rough matches of bitrates // 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 // Of course this is far from perfect, but it might save some space in the end

View File

@ -1,8 +1,8 @@
import { FilterSpecification } from 'fluent-ffmpeg'
import { join } from 'path'
import { pick } from '@peertube/peertube-core-utils' import { pick } from '@peertube/peertube-core-utils'
import { FfprobeData, FilterSpecification } from 'fluent-ffmpeg'
import { join } from 'path'
import { FFmpegCommandWrapper, FFmpegCommandWrapperOptions } from './ffmpeg-command-wrapper.js' import { FFmpegCommandWrapper, FFmpegCommandWrapperOptions } from './ffmpeg-command-wrapper.js'
import { buildStreamSuffix, getScaleFilter, StreamType } from './ffmpeg-utils.js' import { StreamType, buildStreamSuffix, getScaleFilter } from './ffmpeg-utils.js'
import { addDefaultEncoderGlobalParams, addDefaultEncoderParams, applyEncoderOptions } from './shared/index.js' import { addDefaultEncoderGlobalParams, addDefaultEncoderParams, applyEncoderOptions } from './shared/index.js'
export class FFmpegLive { export class FFmpegLive {
@ -27,6 +27,7 @@ export class FFmpegLive {
bitrate: number bitrate: number
ratio: number ratio: number
hasAudio: boolean hasAudio: boolean
probe: FfprobeData
segmentListSize: number segmentListSize: number
segmentDuration: number segmentDuration: number
@ -38,7 +39,8 @@ export class FFmpegLive {
bitrate, bitrate,
masterPlaylistName, masterPlaylistName,
ratio, ratio,
hasAudio hasAudio,
probe
} = options } = options
const command = this.commandWrapper.buildCommand(inputUrl) const command = this.commandWrapper.buildCommand(inputUrl)
@ -69,6 +71,7 @@ export class FFmpegLive {
inputBitrate: bitrate, inputBitrate: bitrate,
inputRatio: ratio, inputRatio: ratio,
inputProbe: probe,
resolution, resolution,
fps, fps,
@ -79,6 +82,7 @@ export class FFmpegLive {
{ {
const streamType: StreamType = 'video' const streamType: StreamType = 'video'
const builderResult = await this.commandWrapper.getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) const builderResult = await this.commandWrapper.getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType })
if (!builderResult) { if (!builderResult) {
throw new Error('No available live video encoder found') throw new Error('No available live video encoder found')
@ -108,6 +112,7 @@ export class FFmpegLive {
if (hasAudio) { if (hasAudio) {
const streamType: StreamType = 'audio' const streamType: StreamType = 'audio'
const builderResult = await this.commandWrapper.getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) const builderResult = await this.commandWrapper.getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType })
if (!builderResult) { if (!builderResult) {
throw new Error('No available live audio encoder found') throw new Error('No available live audio encoder found')

View File

@ -45,6 +45,7 @@ export async function presetVOD (options: {
input, input,
inputBitrate: bitrate, inputBitrate: bitrate,
inputRatio: videoStreamDimensions?.ratio || 0, inputRatio: videoStreamDimensions?.ratio || 0,
inputProbe: probe,
resolution, resolution,
fps, fps,

View File

@ -1,5 +1,7 @@
// Types used by plugins and ffmpeg-utils // Types used by plugins and ffmpeg-utils
import { FfprobeData } from 'fluent-ffmpeg'
export type EncoderOptionsBuilderParams = { export type EncoderOptionsBuilderParams = {
input: string input: string
@ -14,6 +16,7 @@ export type EncoderOptionsBuilderParams = {
// Could be undefined if we could not get input bitrate (some RTMP streams for example) // Could be undefined if we could not get input bitrate (some RTMP streams for example)
inputBitrate: number inputBitrate: number
inputRatio: number inputRatio: number
inputProbe: FfprobeData
// For lives // For lives
streamNum?: number streamNum?: number

View File

@ -36,6 +36,7 @@ import { computeResolutionsToTranscode } from '../transcoding/transcoding-resolu
import { LiveQuotaStore } from './live-quota-store.js' import { LiveQuotaStore } from './live-quota-store.js'
import { cleanupAndDestroyPermanentLive, getLiveSegmentTime } from './live-utils.js' import { cleanupAndDestroyPermanentLive, getLiveSegmentTime } from './live-utils.js'
import { MuxingSession } from './shared/index.js' import { MuxingSession } from './shared/index.js'
import { FfprobeData } from 'fluent-ffmpeg'
// Disable node media server logs // Disable node media server logs
nodeMediaServerLogger.setLogType(0) nodeMediaServerLogger.setLogType(0)
@ -319,7 +320,8 @@ class LiveManager {
bitrate, bitrate,
ratio, ratio,
allResolutions, allResolutions,
hasAudio hasAudio,
probe
}) })
} }
@ -337,6 +339,7 @@ class LiveManager {
ratio: number ratio: number
allResolutions: number[] allResolutions: number[]
hasAudio: boolean hasAudio: boolean
probe: FfprobeData
}) { }) {
const { sessionId, videoLive, user, ratio } = options const { sessionId, videoLive, user, ratio } = options
const videoUUID = videoLive.Video.uuid const videoUUID = videoLive.Video.uuid
@ -352,7 +355,7 @@ class LiveManager {
videoLive, videoLive,
user, user,
...pick(options, [ 'inputLocalUrl', 'inputPublicUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ]) ...pick(options, [ 'inputLocalUrl', 'inputPublicUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio', 'probe' ])
}) })
muxingSession.on('live-ready', () => this.publishAndFederateLive({ live: videoLive, ratio, localLTags })) muxingSession.on('live-ready', () => this.publishAndFederateLive({ live: videoLive, ratio, localLTags }))

View File

@ -1,20 +1,22 @@
import Bluebird from 'bluebird' import { wait } from '@peertube/peertube-core-utils'
import { FSWatcher, watch } from 'chokidar' import { FileStorage, LiveVideoError, VideoStreamingPlaylistType } from '@peertube/peertube-models'
import { EventEmitter } from 'events'
import { ensureDir } from 'fs-extra/esm'
import { appendFile, readFile, stat } from 'fs/promises'
import memoizee from 'memoizee'
import PQueue from 'p-queue'
import { basename, join } from 'path'
import { computeOutputFPS } from '@server/helpers/ffmpeg/index.js' import { computeOutputFPS } from '@server/helpers/ffmpeg/index.js'
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger.js' import { LoggerTagsFn, logger, loggerTagsFactory } from '@server/helpers/logger.js'
import { CONFIG } from '@server/initializers/config.js' import { CONFIG } from '@server/initializers/config.js'
import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants.js' import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants.js'
import { removeHLSFileObjectStorageByPath, storeHLSFileFromContent, storeHLSFileFromPath } from '@server/lib/object-storage/index.js' import { removeHLSFileObjectStorageByPath, storeHLSFileFromContent, storeHLSFileFromPath } from '@server/lib/object-storage/index.js'
import { VideoFileModel } from '@server/models/video/video-file.js' import { VideoFileModel } from '@server/models/video/video-file.js'
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist.js' import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist.js'
import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models/index.js' import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models/index.js'
import { LiveVideoError, FileStorage, VideoStreamingPlaylistType } from '@peertube/peertube-models' import Bluebird from 'bluebird'
import { FSWatcher, watch } from 'chokidar'
import { EventEmitter } from 'events'
import { FfprobeData } from 'fluent-ffmpeg'
import { ensureDir } from 'fs-extra/esm'
import { appendFile, readFile, stat } from 'fs/promises'
import memoizee from 'memoizee'
import PQueue from 'p-queue'
import { basename, join } from 'path'
import { import {
generateHLSMasterPlaylistFilename, generateHLSMasterPlaylistFilename,
generateHlsSha256SegmentsFilename, generateHlsSha256SegmentsFilename,
@ -26,7 +28,6 @@ import { LiveQuotaStore } from '../live-quota-store.js'
import { LiveSegmentShaStore } from '../live-segment-sha-store.js' import { LiveSegmentShaStore } from '../live-segment-sha-store.js'
import { buildConcatenatedName, getLiveSegmentTime } from '../live-utils.js' import { buildConcatenatedName, getLiveSegmentTime } from '../live-utils.js'
import { AbstractTranscodingWrapper, FFmpegTranscodingWrapper, RemoteTranscodingWrapper } from './transcoding-wrapper/index.js' import { AbstractTranscodingWrapper, FFmpegTranscodingWrapper, RemoteTranscodingWrapper } from './transcoding-wrapper/index.js'
import { wait } from '@peertube/peertube-core-utils'
interface MuxingSessionEvents { interface MuxingSessionEvents {
'live-ready': (options: { videoUUID: string }) => void 'live-ready': (options: { videoUUID: string }) => void
@ -71,6 +72,8 @@ class MuxingSession extends EventEmitter {
private readonly hasAudio: boolean private readonly hasAudio: boolean
private readonly probe: FfprobeData
private readonly videoUUID: string private readonly videoUUID: string
private readonly saveReplay: boolean private readonly saveReplay: boolean
@ -116,6 +119,7 @@ class MuxingSession extends EventEmitter {
ratio: number ratio: number
allResolutions: number[] allResolutions: number[]
hasAudio: boolean hasAudio: boolean
probe: FfprobeData
}) { }) {
super() super()
@ -131,6 +135,7 @@ class MuxingSession extends EventEmitter {
this.bitrate = options.bitrate this.bitrate = options.bitrate
this.ratio = options.ratio this.ratio = options.ratio
this.probe = options.probe
this.hasAudio = options.hasAudio this.hasAudio = options.hasAudio
@ -510,6 +515,7 @@ class MuxingSession extends EventEmitter {
bitrate: this.bitrate, bitrate: this.bitrate,
ratio: this.ratio, ratio: this.ratio,
hasAudio: this.hasAudio, hasAudio: this.hasAudio,
probe: this.probe,
segmentListSize: VIDEO_LIVE.SEGMENTS_LIST_SIZE, segmentListSize: VIDEO_LIVE.SEGMENTS_LIST_SIZE,
segmentDuration: getLiveSegmentTime(this.videoLive.latencyMode), segmentDuration: getLiveSegmentTime(this.videoLive.latencyMode),

View File

@ -2,6 +2,7 @@ import { LiveVideoErrorType } from '@peertube/peertube-models'
import { LoggerTagsFn } from '@server/helpers/logger.js' import { LoggerTagsFn } from '@server/helpers/logger.js'
import { MStreamingPlaylistVideo, MVideoLiveVideo } from '@server/types/models/index.js' import { MStreamingPlaylistVideo, MVideoLiveVideo } from '@server/types/models/index.js'
import EventEmitter from 'events' import EventEmitter from 'events'
import { FfprobeData } from 'fluent-ffmpeg'
interface TranscodingWrapperEvents { interface TranscodingWrapperEvents {
'end': () => void 'end': () => void
@ -38,6 +39,7 @@ interface AbstractTranscodingWrapperOptions {
bitrate: number bitrate: number
ratio: number ratio: number
hasAudio: boolean hasAudio: boolean
probe: FfprobeData
segmentListSize: number segmentListSize: number
segmentDuration: number segmentDuration: number
@ -61,6 +63,7 @@ abstract class AbstractTranscodingWrapper extends EventEmitter {
protected readonly bitrate: number protected readonly bitrate: number
protected readonly ratio: number protected readonly ratio: number
protected readonly hasAudio: boolean protected readonly hasAudio: boolean
protected readonly probe: FfprobeData
protected readonly segmentListSize: number protected readonly segmentListSize: number
protected readonly segmentDuration: number protected readonly segmentDuration: number
@ -92,6 +95,7 @@ abstract class AbstractTranscodingWrapper extends EventEmitter {
this.bitrate = options.bitrate this.bitrate = options.bitrate
this.ratio = options.ratio this.ratio = options.ratio
this.hasAudio = options.hasAudio this.hasAudio = options.hasAudio
this.probe = options.probe
this.segmentListSize = options.segmentListSize this.segmentListSize = options.segmentListSize
this.segmentDuration = options.segmentDuration this.segmentDuration = options.segmentDuration

View File

@ -30,6 +30,7 @@ export class FFmpegTranscodingWrapper extends AbstractTranscodingWrapper {
bitrate: this.bitrate, bitrate: this.bitrate,
ratio: this.ratio, ratio: this.ratio,
probe: this.probe,
hasAudio: this.hasAudio hasAudio: this.hasAudio
}) })