From 6b67897e2eab96978daee40aeaf716835856d65d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 24 Nov 2020 16:29:39 +0100 Subject: [PATCH] Add transcoding module comments --- server/helpers/ffmpeg-utils.ts | 7 +++++ server/helpers/ffprobe-utils.ts | 6 ++++ server/lib/video-transcoding-profiles.ts | 35 +++++++++++++++++++----- server/lib/video-transcoding.ts | 13 ++++++--- server/tests/api/live/live.ts | 12 ++++---- shared/extra-utils/videos/live.ts | 13 +++++++-- 6 files changed, 68 insertions(+), 18 deletions(-) diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 712ec757e..69defccc4 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -9,6 +9,13 @@ import { getAudioStream, getClosestFramerateStandard, getVideoFileFPS } from './ import { processImage } from './image-utils' import { logger } from './logger' +/** + * + * Functions that run transcoding/muxing ffmpeg processes + * Mainly called by lib/video-transcoding.ts and lib/live-manager.ts + * + */ + // --------------------------------------------------------------------------- // Encoder options // --------------------------------------------------------------------------- diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index d03ab91ac..16b295bbd 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts @@ -5,6 +5,12 @@ import { CONFIG } from '../initializers/config' import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { logger } from './logger' +/** + * + * Helpers to run ffprobe and extract data from the JSON output + * + */ + function ffprobePromise (path: string) { return new Promise((res, rej) => { ffmpeg.ffprobe(path, (err, data) => { diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/video-transcoding-profiles.ts index 91a5c65f2..03c26f236 100644 --- a/server/lib/video-transcoding-profiles.ts +++ b/server/lib/video-transcoding-profiles.ts @@ -1,11 +1,22 @@ +import { logger } from '@server/helpers/logger' 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 { + canDoQuickAudioTranscode, + ffprobePromise, + getAudioStream, + getMaxAudioBitrate, + getVideoFileBitrate, + getVideoStreamFromFile +} from '../helpers/ffprobe-utils' import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' -// --------------------------------------------------------------------------- -// Available encoders profiles -// --------------------------------------------------------------------------- +/** + * + * Available encoders and profiles for the transcoding jobs + * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile + * + */ // Resources: // * https://slhck.info/video/2017/03/01/rate-control.html @@ -27,7 +38,8 @@ const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, reso return { outputOptions: [ - `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` + `-maxrate ${targetBitrate}`, + `-bufsize ${targetBitrate * 2}` ] } } @@ -45,7 +57,14 @@ const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution } const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => { - const parsedAudio = await getAudioStream(input) + const probe = await ffprobePromise(input) + + if (await canDoQuickAudioTranscode(input, probe)) { + logger.debug('Copy audio stream %s by AAC encoder.', input) + return { copy: true, outputOptions: [] } + } + + const parsedAudio = await getAudioStream(input, probe) // 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 @@ -54,11 +73,13 @@ const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNu const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) + logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName }) + if (bitrate !== undefined && bitrate !== -1) { return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] } } - return { copy: true, outputOptions: [] } + return { outputOptions: [ ] } } const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index aaad219dd..e022f2a68 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts @@ -16,8 +16,13 @@ import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath import { availableEncoders } from './video-transcoding-profiles' /** - * Optimize the original video file and replace it. The resolution is not changed. + * + * Functions that run transcoding functions, update the database, cleanup files, create torrent files... + * Mainly called by the job queue + * */ + +// Optimize the original video file and replace it. The resolution is not changed. async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -62,9 +67,7 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileA } } -/** - * Transcode the original video file to a lower resolution. - */ +// Transcode the original video file to a lower resolution. async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const extname = '.mp4' @@ -110,6 +113,7 @@ async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoR return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) } +// Merge an image with an audio file to create a video async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) { const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -159,6 +163,7 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) } +// Generate an HLS playlist from an input file, and update the master playlist async function generateHlsPlaylist (options: { video: MVideoWithFile videoInputPath: string diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts index e685be08e..0786db554 100644 --- a/server/tests/api/live/live.ts +++ b/server/tests/api/live/live.ts @@ -39,6 +39,7 @@ import { viewVideo, wait, waitJobs, + waitUntilLivePublished, waitUntilLiveStarts, waitUntilLog } from '../../../../shared/extra-utils' @@ -396,7 +397,7 @@ describe('Test live', function () { }) it('Should enable transcoding with some resolutions and correctly save them', async function () { - this.timeout(60000) + this.timeout(120000) const resolutions = [ 240, 360, 720 ] @@ -410,13 +411,14 @@ describe('Test live', function () { await testVideoResolutions(liveVideoId, resolutions) await stopFfmpeg(command) + await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId) await waitJobs(servers) const bitrateLimits = { - 720: 2800 * 1000, - 360: 780 * 1000, - 240: 320 * 1000 + 720: 3000 * 1000, + 360: 1100 * 1000, + 240: 600 * 1000 } for (const server of servers) { @@ -442,7 +444,7 @@ describe('Test live', function () { const probe = await ffprobePromise(segmentPath) const videoStream = await getVideoStreamFromFile(segmentPath, probe) - console.log(videoStream) + expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height]) await makeRawRequest(file.torrentUrl, 200) diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts index 85c83c5bb..c8acb90da 100644 --- a/shared/extra-utils/videos/live.ts +++ b/shared/extra-utils/videos/live.ts @@ -128,7 +128,15 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) { await wait(500) } -async function waitUntilLiveStarts (url: string, token: string, videoId: number | string) { +function waitUntilLiveStarts (url: string, token: string, videoId: number | string) { + return waitWhileLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE) +} + +function waitUntilLivePublished (url: string, token: string, videoId: number | string) { + return waitWhileLiveState(url, token, videoId, VideoState.PUBLISHED) +} + +async function waitWhileLiveState (url: string, token: string, videoId: number | string, state: VideoState) { let video: VideoDetails do { @@ -136,7 +144,7 @@ async function waitUntilLiveStarts (url: string, token: string, videoId: number video = res.body await wait(500) - } while (video.state.id === VideoState.WAITING_FOR_LIVE) + } while (video.state.id === state) } async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) { @@ -168,6 +176,7 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti export { getLive, + waitUntilLivePublished, updateLive, waitUntilLiveStarts, createLive, -- 2.41.0