X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fffprobe-utils.ts;h=595112bce602b321a9a1c19ced49c5d81ad9d183;hb=c7c6afc66d7611d4c02572d63801beca26c45204;hp=907f13651535bccb388faceb5fc899f2d37e0153;hpb=8dd754c76735417305c4b68e2ada6f623e9d7650;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index 907f13651..595112bce 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts @@ -1,9 +1,22 @@ -import { ffprobe, FfprobeData } from 'fluent-ffmpeg' +import { FfprobeData } from 'fluent-ffmpeg' import { getMaxBitrate } from '@shared/core-utils' -import { VideoFileMetadata, VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos' +import { VideoResolution, VideoTranscodingFPS } from '../../shared/models/videos' import { CONFIG } from '../initializers/config' import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { logger } from './logger' +import { + canDoQuickAudioTranscode, + ffprobePromise, + getDurationFromVideoFile, + getAudioStream, + getMaxAudioBitrate, + getMetadataFromFile, + getVideoFileBitrate, + getVideoFileFPS, + getVideoFileResolution, + getVideoStreamFromFile, + getVideoStreamSize +} from '@shared/extra-utils/ffprobe' /** * @@ -11,79 +24,6 @@ import { logger } from './logger' * */ -function ffprobePromise (path: string) { - return new Promise((res, rej) => { - ffprobe(path, (err, data) => { - if (err) return rej(err) - - return res(data) - }) - }) -} - -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 - const data = existingProbe || await ffprobePromise(videoPath) - - if (Array.isArray(data.streams)) { - const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio') - - if (audioStream) { - return { - absolutePath: data.format.filename, - audioStream, - bitrate: parseInt(audioStream['bit_rate'] + '', 10) - } - } - } - - return { absolutePath: data.format.filename } -} - -function getMaxAudioBitrate (type: 'aac' | 'mp3' | string, bitrate: number) { - const maxKBitrate = 384 - const kToBits = (kbits: number) => kbits * 1000 - - // If we did not manage to get the bitrate, use an average value - if (!bitrate) return 256 - - if (type === 'aac') { - switch (true) { - case bitrate > kToBits(maxKBitrate): - return maxKBitrate - - default: - return -1 // we interpret it as a signal to copy the audio stream as is - } - } - - /* - a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac. - That's why, when using aac, we can go to lower kbit/sec. The equivalences - made here are not made to be accurate, especially with good mp3 encoders. - */ - switch (true) { - case bitrate <= kToBits(192): - return 128 - - case bitrate <= kToBits(384): - return 256 - - default: - return maxKBitrate - } -} - -async function getVideoStreamSize (path: string, existingProbe?: FfprobeData): Promise<{ width: number, height: number }> { - const videoStream = await getVideoStreamFromFile(path, existingProbe) - - return videoStream === null - ? { width: 0, height: 0 } - : { width: videoStream.width, height: videoStream.height } -} - async function getVideoStreamCodec (path: string) { const videoStream = await getVideoStreamFromFile(path) @@ -143,70 +83,7 @@ async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) { return 'mp4a.40.2' // Fallback } -async function getVideoFileResolution (path: string, existingProbe?: FfprobeData) { - const size = await getVideoStreamSize(path, existingProbe) - - return { - 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?: FfprobeData) { - const videoStream = await getVideoStreamFromFile(path, existingProbe) - if (videoStream === null) return 0 - - for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { - const valuesText: string = videoStream[key] - if (!valuesText) continue - - const [ frames, seconds ] = valuesText.split('/') - if (!frames || !seconds) continue - - const result = parseInt(frames, 10) / parseInt(seconds, 10) - if (result > 0) return Math.round(result) - } - - return 0 -} - -async function getMetadataFromFile (path: string, existingProbe?: FfprobeData) { - const metadata = existingProbe || await ffprobePromise(path) - - return new VideoFileMetadata(metadata) -} - -async function getVideoFileBitrate (path: string, existingProbe?: FfprobeData): Promise { - const metadata = await getMetadataFromFile(path, existingProbe) - - 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?: FfprobeData) { - const metadata = await getMetadataFromFile(path, existingProbe) - - return Math.round(metadata.format.duration) -} - -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 @@ -214,7 +91,7 @@ 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, @@ -263,26 +140,6 @@ async function canDoQuickVideoTranscode (path: string, probe?: FfprobeData): Pro return true } -async function canDoQuickAudioTranscode (path: string, probe?: FfprobeData): Promise { - const parsedAudio = await getAudioStream(path, probe) - - if (!parsedAudio.audioStream) return true - - if (parsedAudio.audioStream['codec_name'] !== 'aac') return false - - const audioBitrate = parsedAudio.bitrate - if (!audioBitrate) return false - - const maxAudioBitrate = getMaxAudioBitrate('aac', audioBitrate) - if (maxAudioBitrate !== -1 && audioBitrate > maxAudioBitrate) return false - - const channelLayout = parsedAudio.audioStream['channel_layout'] - // Causes playback issues with Chrome - if (!channelLayout || channelLayout === 'unknown') return false - - return true -} - function getClosestFramerateStandard > (fps: number, type: K) { return VIDEO_TRANSCODING_FPS[type].slice(0) .sort((a, b) => fps % a - fps % b)[0] @@ -327,7 +184,7 @@ export { getVideoFileFPS, ffprobePromise, getClosestFramerateStandard, - computeResolutionsToTranscode, + computeLowerResolutionsToTranscode, getVideoFileBitrate, canDoQuickTranscode, canDoQuickVideoTranscode,