X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fffprobe-utils.ts;h=e15628e2a7ff428251eab933e803e8cc1b800717;hb=795212f7acc690c88c86d0fab8772f6564d59cb8;hp=ef2aa3f890e0a36856ca9aa87b300e30f0a513cd;hpb=4d7ce9218a3f695bf3d013cbdce1c5c6a5221927;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index ef2aa3f89..e15628e2a 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts @@ -1,5 +1,6 @@ -import * as ffmpeg from 'fluent-ffmpeg' -import { getMaxBitrate, VideoFileMetadata, VideoResolution } from '../../shared/models/videos' +import { ffprobe, FfprobeData } from 'fluent-ffmpeg' +import { getMaxBitrate } from '@shared/core-utils' +import { VideoFileMetadata, VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos' import { CONFIG } from '../initializers/config' import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { logger } from './logger' @@ -11,8 +12,8 @@ import { logger } from './logger' */ function ffprobePromise (path: string) { - return new Promise((res, rej) => { - ffmpeg.ffprobe(path, (err, data) => { + return new Promise((res, rej) => { + ffprobe(path, (err, data) => { if (err) return rej(err) return res(data) @@ -20,7 +21,7 @@ function ffprobePromise (path: string) { }) } -async function getAudioStream (videoPath: string, existingProbe?: ffmpeg.FfprobeData) { +async function getAudioStream (videoPath: string, existingProbe?: FfprobeData) { // without position, ffprobe considers the last input only // we make it consider the first input only // if you pass a file path to pos, then ffprobe acts on that file directly @@ -75,7 +76,7 @@ function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) { } } -async function getVideoStreamSize (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> { const videoStream = await getVideoStreamFromFile(path, existingProbe) return videoStream === null @@ -126,7 +127,7 @@ async function getVideoStreamCodec (path: string) { return `${videoCodec}.${baseProfile}${level}` } -async function getAudioStreamCodec (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) { const { audioStream } = await getAudioStream(path, existingProbe) if (!audioStream) return '' @@ -142,16 +143,19 @@ async function getAudioStreamCodec (path: string, existingProbe?: ffmpeg.Ffprobe return 'mp4a.40.2' // Fallback } -async function getVideoFileResolution (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) { const size = await getVideoStreamSize(path, existingProbe) return { - videoFileResolution: Math.min(size.height, size.width), + width: size.width, + height: size.height, + ratio: Math.max(size.height, size.width) / Math.min(size.height, size.width), + resolution: Math.min(size.height, size.width), isPortraitMode: size.height > size.width } } -async function getVideoFileFPS (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getVideoFileFPS (path: string, existingProbe?: FfprobeData) { const videoStream = await getVideoStreamFromFile(path, existingProbe) if (videoStream === null) return 0 @@ -169,31 +173,40 @@ async function getVideoFileFPS (path: string, existingProbe?: ffmpeg.FfprobeData return 0 } -async function getMetadataFromFile (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) { const metadata = existingProbe || await ffprobePromise(path) return new VideoFileMetadata(metadata) } -async function getVideoFileBitrate (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise { const metadata = await getMetadataFromFile(path, existingProbe) - return metadata.format.bit_rate as number + let bitrate = metadata.format.bit_rate as number + if (bitrate && !isNaN(bitrate)) return bitrate + + const videoStream = await getVideoStreamFromFile(path, existingProbe) + if (!videoStream) return undefined + + bitrate = videoStream?.bit_rate + if (bitrate && !isNaN(bitrate)) return bitrate + + return undefined } -async function getDurationFromVideoFile (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getDurationFromVideoFile (path: string, existingProbe?: FfprobeData) { const metadata = await getMetadataFromFile(path, existingProbe) return Math.round(metadata.format.duration) } -async function getVideoStreamFromFile (path: string, existingProbe?: ffmpeg.FfprobeData) { +async function getVideoStreamFromFile (path: string, existingProbe?: FfprobeData) { const metadata = await getMetadataFromFile(path, existingProbe) return metadata.streams.find(s => s.codec_type === 'video') || null } -function computeResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') { +function computeLowerResolutionsToTranscode (videoFileResolution: number, type: 'vod' | 'live') { const configResolutions = type === 'vod' ? CONFIG.TRANSCODING.RESOLUTIONS : CONFIG.LIVE.TRANSCODING.RESOLUTIONS @@ -201,12 +214,13 @@ function computeResolutionsToTranscode (videoFileResolution: number, type: 'vod' const resolutionsEnabled: number[] = [] // Put in the order we want to proceed jobs - const resolutions = [ + const resolutions: VideoResolution[] = [ VideoResolution.H_NOVIDEO, VideoResolution.H_480P, VideoResolution.H_360P, VideoResolution.H_720P, VideoResolution.H_240P, + VideoResolution.H_144P, VideoResolution.H_1080P, VideoResolution.H_1440P, VideoResolution.H_4K @@ -230,11 +244,11 @@ async function canDoQuickTranscode (path: string): Promise { await canDoQuickAudioTranscode(path, probe) } -async function canDoQuickVideoTranscode (path: string, probe?: ffmpeg.FfprobeData): Promise { +async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Promise { const videoStream = await getVideoStreamFromFile(path, probe) const fps = await getVideoFileFPS(path, probe) const bitRate = await getVideoFileBitrate(path, probe) - const resolution = await getVideoFileResolution(path, probe) + const resolutionData = await getVideoFileResolution(path, probe) // If ffprobe did not manage to guess the bitrate if (!bitRate) return false @@ -244,12 +258,12 @@ async function canDoQuickVideoTranscode (path: string, probe?: ffmpeg.FfprobeDat 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(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false + if (bitRate > getMaxBitrate({ ...resolutionData, fps })) return false return true } -async function canDoQuickAudioTranscode (path: string, probe?: ffmpeg.FfprobeData): Promise { +async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise { const parsedAudio = await getAudioStream(path, probe) if (!parsedAudio.audioStream) return true @@ -269,7 +283,7 @@ async function canDoQuickAudioTranscode (path: string, probe?: ffmpeg.FfprobeDat return true } -function getClosestFramerateStandard (fps: number, type: 'HD_STANDARD' | 'STANDARD'): number { +function getClosestFramerateStandard > (fps: number, type: K) { return VIDEO_TRANSCODING_FPS[type].slice(0) .sort((a, b) => fps % a - fps % b)[0] } @@ -289,7 +303,10 @@ function computeFPS (fpsArg: number, resolution: VideoResolution) { // Hard FPS limits if (fps > VIDEO_TRANSCODING_FPS.MAX) fps = getClosestFramerateStandard(fps, 'HD_STANDARD') - else if (fps < VIDEO_TRANSCODING_FPS.MIN) fps = VIDEO_TRANSCODING_FPS.MIN + + if (fps < VIDEO_TRANSCODING_FPS.MIN) { + throw new Error(`Cannot compute FPS because ${fps} is lower than our minimum value ${VIDEO_TRANSCODING_FPS.MIN}`) + } return fps } @@ -310,7 +327,7 @@ export { getVideoFileFPS, ffprobePromise, getClosestFramerateStandard, - computeResolutionsToTranscode, + computeLowerResolutionsToTranscode, getVideoFileBitrate, canDoQuickTranscode, canDoQuickVideoTranscode,