Add transcoding module comments

pull/3274/head
Chocobozzz 2020-11-24 16:29:39 +01:00 committed by Chocobozzz
parent 33ff70baa6
commit 6b67897e2e
6 changed files with 68 additions and 18 deletions

View File

@ -9,6 +9,13 @@ import { getAudioStream, getClosestFramerateStandard, getVideoFileFPS } from './
import { processImage } from './image-utils' import { processImage } from './image-utils'
import { logger } from './logger' 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 // Encoder options
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -5,6 +5,12 @@ import { CONFIG } from '../initializers/config'
import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
import { logger } from './logger' import { logger } from './logger'
/**
*
* Helpers to run ffprobe and extract data from the JSON output
*
*/
function ffprobePromise (path: string) { function ffprobePromise (path: string) {
return new Promise<ffmpeg.FfprobeData>((res, rej) => { return new Promise<ffmpeg.FfprobeData>((res, rej) => {
ffmpeg.ffprobe(path, (err, data) => { ffmpeg.ffprobe(path, (err, data) => {

View File

@ -1,11 +1,22 @@
import { logger } from '@server/helpers/logger'
import { getTargetBitrate } from '../../shared/models/videos' import { getTargetBitrate } from '../../shared/models/videos'
import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' 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' 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: // Resources:
// * https://slhck.info/video/2017/03/01/rate-control.html // * https://slhck.info/video/2017/03/01/rate-control.html
@ -27,7 +38,8 @@ const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, reso
return { return {
outputOptions: [ 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 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 // 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
@ -54,11 +73,13 @@ const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNu
const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) 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) { if (bitrate !== undefined && bitrate !== -1) {
return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] } return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] }
} }
return { copy: true, outputOptions: [] } return { outputOptions: [ ] }
} }
const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {

View File

@ -16,8 +16,13 @@ import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath
import { availableEncoders } from './video-transcoding-profiles' 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) { async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4' 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) { async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const extname = '.mp4' const extname = '.mp4'
@ -110,6 +113,7 @@ async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoR
return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
} }
// Merge an image with an audio file to create a video
async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) { async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) {
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4' const newExtname = '.mp4'
@ -159,6 +163,7 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video
return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
} }
// Generate an HLS playlist from an input file, and update the master playlist
async function generateHlsPlaylist (options: { async function generateHlsPlaylist (options: {
video: MVideoWithFile video: MVideoWithFile
videoInputPath: string videoInputPath: string

View File

@ -39,6 +39,7 @@ import {
viewVideo, viewVideo,
wait, wait,
waitJobs, waitJobs,
waitUntilLivePublished,
waitUntilLiveStarts, waitUntilLiveStarts,
waitUntilLog waitUntilLog
} from '../../../../shared/extra-utils' } 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 () { it('Should enable transcoding with some resolutions and correctly save them', async function () {
this.timeout(60000) this.timeout(120000)
const resolutions = [ 240, 360, 720 ] const resolutions = [ 240, 360, 720 ]
@ -410,13 +411,14 @@ describe('Test live', function () {
await testVideoResolutions(liveVideoId, resolutions) await testVideoResolutions(liveVideoId, resolutions)
await stopFfmpeg(command) await stopFfmpeg(command)
await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId)
await waitJobs(servers) await waitJobs(servers)
const bitrateLimits = { const bitrateLimits = {
720: 2800 * 1000, 720: 3000 * 1000,
360: 780 * 1000, 360: 1100 * 1000,
240: 320 * 1000 240: 600 * 1000
} }
for (const server of servers) { for (const server of servers) {
@ -442,7 +444,7 @@ describe('Test live', function () {
const probe = await ffprobePromise(segmentPath) const probe = await ffprobePromise(segmentPath)
const videoStream = await getVideoStreamFromFile(segmentPath, probe) const videoStream = await getVideoStreamFromFile(segmentPath, probe)
console.log(videoStream)
expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height]) expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height])
await makeRawRequest(file.torrentUrl, 200) await makeRawRequest(file.torrentUrl, 200)

View File

@ -128,7 +128,15 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
await wait(500) 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 let video: VideoDetails
do { do {
@ -136,7 +144,7 @@ async function waitUntilLiveStarts (url: string, token: string, videoId: number
video = res.body video = res.body
await wait(500) 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[] = []) { async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) {
@ -168,6 +176,7 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti
export { export {
getLive, getLive,
waitUntilLivePublished,
updateLive, updateLive,
waitUntilLiveStarts, waitUntilLiveStarts,
createLive, createLive,