From 5a547f69d5dc5867e253f7721513479c754b4f15 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 24 Nov 2020 14:08:23 +0100 Subject: Support encoding profiles --- server/lib/live-manager.ts | 12 +++- server/lib/video-transcoding-profiles.ts | 95 ++++++++++++++++++++++++++++++++ server/lib/video-transcoding.ts | 91 ++---------------------------- 3 files changed, 111 insertions(+), 87 deletions(-) create mode 100644 server/lib/video-transcoding-profiles.ts (limited to 'server/lib') diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts index b9e5d4561..ee0e4de37 100644 --- a/server/lib/live-manager.ts +++ b/server/lib/live-manager.ts @@ -22,6 +22,7 @@ import { JobQueue } from './job-queue' import { PeerTubeSocket } from './peertube-socket' import { isAbleToUploadVideo } from './user' import { getHLSDirectory } from './video-paths' +import { availableEncoders } from './video-transcoding-profiles' import memoizee = require('memoizee') const NodeRtmpServer = require('node-media-server/node_rtmp_server') @@ -264,7 +265,16 @@ class LiveManager { const deleteSegments = videoLive.saveReplay === false const ffmpegExec = CONFIG.LIVE.TRANSCODING.ENABLED - ? getLiveTranscodingCommand(rtmpUrl, outPath, allResolutions, fps, deleteSegments) + ? await getLiveTranscodingCommand({ + rtmpUrl, + outPath, + resolutions: + allResolutions, + fps, + deleteSegments, + availableEncoders, + profile: 'default' + }) : getLiveMuxingCommand(rtmpUrl, outPath, deleteSegments) logger.info('Running live muxing/transcoding for %s.', videoUUID) diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/video-transcoding-profiles.ts new file mode 100644 index 000000000..12e22a19d --- /dev/null +++ b/server/lib/video-transcoding-profiles.ts @@ -0,0 +1,95 @@ +import { getTargetBitrate } from '../../shared/models/videos' +import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' +import { ffprobePromise, getAudioStream, getMaxAudioBitrate, getVideoFileBitrate, getVideoStreamFromFile } from '../helpers/ffprobe-utils' +import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' + +// --------------------------------------------------------------------------- +// Available encoders profiles +// --------------------------------------------------------------------------- + +// Resources: +// * https://slhck.info/video/2017/03/01/rate-control.html +// * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate + +const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, resolution, fps }) => { + let targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) + + const probe = await ffprobePromise(input) + + const videoStream = await getVideoStreamFromFile(input, probe) + if (!videoStream) { + return { outputOptions: [ ] } + } + + // Don't transcode to an higher bitrate than the original file + const fileBitrate = await getVideoFileBitrate(input, probe) + targetBitrate = Math.min(targetBitrate, fileBitrate) + + return { + outputOptions: [ + `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` + ] + } +} + +const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution, fps, streamNum }) => { + const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) + + return { + outputOptions: [ + `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`, + `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` + ] + } +} + +const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => { + const parsedAudio = await getAudioStream(input) + + // 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 + + const audioCodecName = parsedAudio.audioStream['codec_name'] + + const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) + + if (bitrate !== undefined && bitrate !== -1) { + return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] } + } + + return { copy: true, outputOptions: [] } +} + +const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { + return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] } +} + +const availableEncoders: AvailableEncoders = { + vod: { + libx264: { + default: defaultX264VODOptionsBuilder + }, + aac: { + default: defaultAACOptionsBuilder + }, + libfdk_aac: { + default: defaultLibFDKAACVODOptionsBuilder + } + }, + live: { + libx264: { + default: defaultX264LiveOptionsBuilder + }, + aac: { + default: defaultAACOptionsBuilder + } + } +} + +// --------------------------------------------------------------------------- + +export { + availableEncoders +} + +// --------------------------------------------------------------------------- diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index 0e6a0e984..aaad219dd 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts @@ -2,30 +2,18 @@ import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' import { basename, extname as extnameUtil, join } from 'path' import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' -import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' +import { VideoResolution } from '../../shared/models/videos' import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' -import { AvailableEncoders, EncoderOptionsBuilder, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' -import { - canDoQuickTranscode, - getAudioStream, - getDurationFromVideoFile, - getMaxAudioBitrate, - getMetadataFromFile, - getVideoFileBitrate, - getVideoFileFPS -} from '../helpers/ffprobe-utils' +import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' +import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../helpers/ffprobe-utils' import { logger } from '../helpers/logger' import { CONFIG } from '../initializers/config' -import { - HLS_STREAMING_PLAYLIST_DIRECTORY, - P2P_MEDIA_LOADER_PEER_VERSION, - VIDEO_TRANSCODING_FPS, - WEBSERVER -} from '../initializers/constants' +import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' import { VideoFileModel } from '../models/video/video-file' import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths' +import { availableEncoders } from './video-transcoding-profiles' /** * Optimize the original video file and replace it. The resolution is not changed. @@ -252,75 +240,6 @@ async function generateHlsPlaylist (options: { return video } -// --------------------------------------------------------------------------- -// Available encoders profiles -// --------------------------------------------------------------------------- - -const defaultX264OptionsBuilder: EncoderOptionsBuilder = async ({ input, resolution, fps }) => { - if (!fps) return { outputOptions: [] } - - let targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) - - // Don't transcode to an higher bitrate than the original file - const fileBitrate = await getVideoFileBitrate(input) - targetBitrate = Math.min(targetBitrate, fileBitrate) - - return { - outputOptions: [ - // Constrained Encoding (VBV) - // https://slhck.info/video/2017/03/01/rate-control.html - // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate - `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` - ] - } -} - -const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input }) => { - const parsedAudio = await getAudioStream(input) - - // 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 - - const audioCodecName = parsedAudio.audioStream['codec_name'] - - const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) - - if (bitrate !== undefined && bitrate !== -1) { - return { outputOptions: [ '-b:a', bitrate + 'k' ] } - } - - return { outputOptions: [] } -} - -const defaultLibFDKAACOptionsBuilder: EncoderOptionsBuilder = () => { - return { outputOptions: [ '-aq', '5' ] } -} - -const availableEncoders: AvailableEncoders = { - vod: { - libx264: { - default: defaultX264OptionsBuilder - }, - aac: { - default: defaultAACOptionsBuilder - }, - libfdkAAC: { - default: defaultLibFDKAACOptionsBuilder - } - }, - live: { - libx264: { - default: defaultX264OptionsBuilder - }, - aac: { - default: defaultAACOptionsBuilder - }, - libfdkAAC: { - default: defaultLibFDKAACOptionsBuilder - } - } -} - // --------------------------------------------------------------------------- export { -- cgit v1.2.3