aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-11-24 14:08:23 +0100
committerChocobozzz <chocobozzz@cpy.re>2020-11-25 10:07:51 +0100
commit5a547f69d5dc5867e253f7721513479c754b4f15 (patch)
tree5ccad0e07d04e24d7a4c0b624a46d3b5a93ebce5 /server/lib
parent9252a33d115bba85adcfbc18ab3725924642871c (diff)
downloadPeerTube-5a547f69d5dc5867e253f7721513479c754b4f15.tar.gz
PeerTube-5a547f69d5dc5867e253f7721513479c754b4f15.tar.zst
PeerTube-5a547f69d5dc5867e253f7721513479c754b4f15.zip
Support encoding profiles
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/live-manager.ts12
-rw-r--r--server/lib/video-transcoding-profiles.ts95
-rw-r--r--server/lib/video-transcoding.ts91
3 files changed, 111 insertions, 87 deletions
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'
22import { PeerTubeSocket } from './peertube-socket' 22import { PeerTubeSocket } from './peertube-socket'
23import { isAbleToUploadVideo } from './user' 23import { isAbleToUploadVideo } from './user'
24import { getHLSDirectory } from './video-paths' 24import { getHLSDirectory } from './video-paths'
25import { availableEncoders } from './video-transcoding-profiles'
25 26
26import memoizee = require('memoizee') 27import memoizee = require('memoizee')
27const NodeRtmpServer = require('node-media-server/node_rtmp_server') 28const NodeRtmpServer = require('node-media-server/node_rtmp_server')
@@ -264,7 +265,16 @@ class LiveManager {
264 const deleteSegments = videoLive.saveReplay === false 265 const deleteSegments = videoLive.saveReplay === false
265 266
266 const ffmpegExec = CONFIG.LIVE.TRANSCODING.ENABLED 267 const ffmpegExec = CONFIG.LIVE.TRANSCODING.ENABLED
267 ? getLiveTranscodingCommand(rtmpUrl, outPath, allResolutions, fps, deleteSegments) 268 ? await getLiveTranscodingCommand({
269 rtmpUrl,
270 outPath,
271 resolutions:
272 allResolutions,
273 fps,
274 deleteSegments,
275 availableEncoders,
276 profile: 'default'
277 })
268 : getLiveMuxingCommand(rtmpUrl, outPath, deleteSegments) 278 : getLiveMuxingCommand(rtmpUrl, outPath, deleteSegments)
269 279
270 logger.info('Running live muxing/transcoding for %s.', videoUUID) 280 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 @@
1import { getTargetBitrate } from '../../shared/models/videos'
2import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils'
3import { ffprobePromise, getAudioStream, getMaxAudioBitrate, getVideoFileBitrate, getVideoStreamFromFile } from '../helpers/ffprobe-utils'
4import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
5
6// ---------------------------------------------------------------------------
7// Available encoders profiles
8// ---------------------------------------------------------------------------
9
10// Resources:
11// * https://slhck.info/video/2017/03/01/rate-control.html
12// * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
13
14const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, resolution, fps }) => {
15 let targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
16
17 const probe = await ffprobePromise(input)
18
19 const videoStream = await getVideoStreamFromFile(input, probe)
20 if (!videoStream) {
21 return { outputOptions: [ ] }
22 }
23
24 // Don't transcode to an higher bitrate than the original file
25 const fileBitrate = await getVideoFileBitrate(input, probe)
26 targetBitrate = Math.min(targetBitrate, fileBitrate)
27
28 return {
29 outputOptions: [
30 `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}`
31 ]
32 }
33}
34
35const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution, fps, streamNum }) => {
36 const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
37
38 return {
39 outputOptions: [
40 `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`,
41 `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}`
42 ]
43 }
44}
45
46const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => {
47 const parsedAudio = await getAudioStream(input)
48
49 // We try to reduce the ceiling bitrate by making rough matches of bitrates
50 // Of course this is far from perfect, but it might save some space in the end
51
52 const audioCodecName = parsedAudio.audioStream['codec_name']
53
54 const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
55
56 if (bitrate !== undefined && bitrate !== -1) {
57 return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] }
58 }
59
60 return { copy: true, outputOptions: [] }
61}
62
63const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
64 return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
65}
66
67const availableEncoders: AvailableEncoders = {
68 vod: {
69 libx264: {
70 default: defaultX264VODOptionsBuilder
71 },
72 aac: {
73 default: defaultAACOptionsBuilder
74 },
75 libfdk_aac: {
76 default: defaultLibFDKAACVODOptionsBuilder
77 }
78 },
79 live: {
80 libx264: {
81 default: defaultX264LiveOptionsBuilder
82 },
83 aac: {
84 default: defaultAACOptionsBuilder
85 }
86 }
87}
88
89// ---------------------------------------------------------------------------
90
91export {
92 availableEncoders
93}
94
95// ---------------------------------------------------------------------------
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'
2import { basename, extname as extnameUtil, join } from 'path' 2import { basename, extname as extnameUtil, join } from 'path'
3import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 3import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
4import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' 4import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models'
5import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' 5import { VideoResolution } from '../../shared/models/videos'
6import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' 6import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
7import { AvailableEncoders, EncoderOptionsBuilder, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' 7import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils'
8import { 8import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../helpers/ffprobe-utils'
9 canDoQuickTranscode,
10 getAudioStream,
11 getDurationFromVideoFile,
12 getMaxAudioBitrate,
13 getMetadataFromFile,
14 getVideoFileBitrate,
15 getVideoFileFPS
16} from '../helpers/ffprobe-utils'
17import { logger } from '../helpers/logger' 9import { logger } from '../helpers/logger'
18import { CONFIG } from '../initializers/config' 10import { CONFIG } from '../initializers/config'
19import { 11import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants'
20 HLS_STREAMING_PLAYLIST_DIRECTORY,
21 P2P_MEDIA_LOADER_PEER_VERSION,
22 VIDEO_TRANSCODING_FPS,
23 WEBSERVER
24} from '../initializers/constants'
25import { VideoFileModel } from '../models/video/video-file' 12import { VideoFileModel } from '../models/video/video-file'
26import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 13import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
27import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' 14import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls'
28import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths' 15import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths'
16import { availableEncoders } from './video-transcoding-profiles'
29 17
30/** 18/**
31 * Optimize the original video file and replace it. The resolution is not changed. 19 * Optimize the original video file and replace it. The resolution is not changed.
@@ -253,75 +241,6 @@ async function generateHlsPlaylist (options: {
253} 241}
254 242
255// --------------------------------------------------------------------------- 243// ---------------------------------------------------------------------------
256// Available encoders profiles
257// ---------------------------------------------------------------------------
258
259const defaultX264OptionsBuilder: EncoderOptionsBuilder = async ({ input, resolution, fps }) => {
260 if (!fps) return { outputOptions: [] }
261
262 let targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
263
264 // Don't transcode to an higher bitrate than the original file
265 const fileBitrate = await getVideoFileBitrate(input)
266 targetBitrate = Math.min(targetBitrate, fileBitrate)
267
268 return {
269 outputOptions: [
270 // Constrained Encoding (VBV)
271 // https://slhck.info/video/2017/03/01/rate-control.html
272 // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
273 `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}`
274 ]
275 }
276}
277
278const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input }) => {
279 const parsedAudio = await getAudioStream(input)
280
281 // we try to reduce the ceiling bitrate by making rough matches of bitrates
282 // of course this is far from perfect, but it might save some space in the end
283
284 const audioCodecName = parsedAudio.audioStream['codec_name']
285
286 const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
287
288 if (bitrate !== undefined && bitrate !== -1) {
289 return { outputOptions: [ '-b:a', bitrate + 'k' ] }
290 }
291
292 return { outputOptions: [] }
293}
294
295const defaultLibFDKAACOptionsBuilder: EncoderOptionsBuilder = () => {
296 return { outputOptions: [ '-aq', '5' ] }
297}
298
299const availableEncoders: AvailableEncoders = {
300 vod: {
301 libx264: {
302 default: defaultX264OptionsBuilder
303 },
304 aac: {
305 default: defaultAACOptionsBuilder
306 },
307 libfdkAAC: {
308 default: defaultLibFDKAACOptionsBuilder
309 }
310 },
311 live: {
312 libx264: {
313 default: defaultX264OptionsBuilder
314 },
315 aac: {
316 default: defaultAACOptionsBuilder
317 },
318 libfdkAAC: {
319 default: defaultLibFDKAACOptionsBuilder
320 }
321 }
322}
323
324// ---------------------------------------------------------------------------
325 244
326export { 245export {
327 generateHlsPlaylist, 246 generateHlsPlaylist,