diff options
author | Chocobozzz <me@florianbigard.com> | 2020-11-24 14:08:23 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-11-25 10:07:51 +0100 |
commit | 5a547f69d5dc5867e253f7721513479c754b4f15 (patch) | |
tree | 5ccad0e07d04e24d7a4c0b624a46d3b5a93ebce5 /server/lib | |
parent | 9252a33d115bba85adcfbc18ab3725924642871c (diff) | |
download | PeerTube-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.ts | 12 | ||||
-rw-r--r-- | server/lib/video-transcoding-profiles.ts | 95 | ||||
-rw-r--r-- | server/lib/video-transcoding.ts | 91 |
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' | |||
22 | import { PeerTubeSocket } from './peertube-socket' | 22 | import { PeerTubeSocket } from './peertube-socket' |
23 | import { isAbleToUploadVideo } from './user' | 23 | import { isAbleToUploadVideo } from './user' |
24 | import { getHLSDirectory } from './video-paths' | 24 | import { getHLSDirectory } from './video-paths' |
25 | import { availableEncoders } from './video-transcoding-profiles' | ||
25 | 26 | ||
26 | import memoizee = require('memoizee') | 27 | import memoizee = require('memoizee') |
27 | const NodeRtmpServer = require('node-media-server/node_rtmp_server') | 28 | const 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 @@ | |||
1 | import { getTargetBitrate } from '../../shared/models/videos' | ||
2 | import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' | ||
3 | import { ffprobePromise, getAudioStream, getMaxAudioBitrate, getVideoFileBitrate, getVideoStreamFromFile } from '../helpers/ffprobe-utils' | ||
4 | import { 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 | |||
14 | const 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 | |||
35 | const 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 | |||
46 | const 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 | |||
63 | const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { | ||
64 | return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] } | ||
65 | } | ||
66 | |||
67 | const 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 | |||
91 | export { | ||
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' | |||
2 | import { basename, extname as extnameUtil, join } from 'path' | 2 | import { basename, extname as extnameUtil, join } from 'path' |
3 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 3 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
4 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' | 4 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' |
5 | import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' | 5 | import { VideoResolution } from '../../shared/models/videos' |
6 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 6 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' |
7 | import { AvailableEncoders, EncoderOptionsBuilder, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' | 7 | import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' |
8 | import { | 8 | import { 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' | ||
17 | import { logger } from '../helpers/logger' | 9 | import { logger } from '../helpers/logger' |
18 | import { CONFIG } from '../initializers/config' | 10 | import { CONFIG } from '../initializers/config' |
19 | import { | 11 | import { 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' | ||
25 | import { VideoFileModel } from '../models/video/video-file' | 12 | import { VideoFileModel } from '../models/video/video-file' |
26 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 13 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
27 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' | 14 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' |
28 | import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths' | 15 | import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths' |
16 | import { 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 | |||
259 | const 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 | |||
278 | const 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 | |||
295 | const defaultLibFDKAACOptionsBuilder: EncoderOptionsBuilder = () => { | ||
296 | return { outputOptions: [ '-aq', '5' ] } | ||
297 | } | ||
298 | |||
299 | const 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 | ||
326 | export { | 245 | export { |
327 | generateHlsPlaylist, | 246 | generateHlsPlaylist, |