aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/live-manager.ts8
-rw-r--r--server/lib/video-transcoding.ts114
2 files changed, 115 insertions, 7 deletions
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts
index 4d2e9b1b3..b9e5d4561 100644
--- a/server/lib/live-manager.ts
+++ b/server/lib/live-manager.ts
@@ -4,7 +4,7 @@ import { FfmpegCommand } from 'fluent-ffmpeg'
4import { ensureDir, stat } from 'fs-extra' 4import { ensureDir, stat } from 'fs-extra'
5import { basename } from 'path' 5import { basename } from 'path'
6import { isTestInstance } from '@server/helpers/core-utils' 6import { isTestInstance } from '@server/helpers/core-utils'
7import { runLiveMuxing, runLiveTranscoding } from '@server/helpers/ffmpeg-utils' 7import { getLiveMuxingCommand, getLiveTranscodingCommand } from '@server/helpers/ffmpeg-utils'
8import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' 8import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils'
9import { logger } from '@server/helpers/logger' 9import { logger } from '@server/helpers/logger'
10import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' 10import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
@@ -264,8 +264,8 @@ class LiveManager {
264 const deleteSegments = videoLive.saveReplay === false 264 const deleteSegments = videoLive.saveReplay === false
265 265
266 const ffmpegExec = CONFIG.LIVE.TRANSCODING.ENABLED 266 const ffmpegExec = CONFIG.LIVE.TRANSCODING.ENABLED
267 ? runLiveTranscoding(rtmpUrl, outPath, allResolutions, fps, deleteSegments) 267 ? getLiveTranscodingCommand(rtmpUrl, outPath, allResolutions, fps, deleteSegments)
268 : runLiveMuxing(rtmpUrl, outPath, deleteSegments) 268 : getLiveMuxingCommand(rtmpUrl, outPath, deleteSegments)
269 269
270 logger.info('Running live muxing/transcoding for %s.', videoUUID) 270 logger.info('Running live muxing/transcoding for %s.', videoUUID)
271 this.transSessions.set(sessionId, ffmpegExec) 271 this.transSessions.set(sessionId, ffmpegExec)
@@ -382,6 +382,8 @@ class LiveManager {
382 }) 382 })
383 383
384 ffmpegExec.on('end', () => onFFmpegEnded()) 384 ffmpegExec.on('end', () => onFFmpegEnded())
385
386 ffmpegExec.run()
385 } 387 }
386 388
387 private async onEndTransmuxing (videoId: number, cleanupNow = false) { 389 private async onEndTransmuxing (videoId: number, cleanupNow = false) {
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts
index ca969b235..0e6a0e984 100644
--- a/server/lib/video-transcoding.ts
+++ b/server/lib/video-transcoding.ts
@@ -2,13 +2,26 @@ 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 { VideoResolution } from '../../shared/models/videos' 5import { getTargetBitrate, 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 { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' 7import { AvailableEncoders, EncoderOptionsBuilder, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils'
8import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../helpers/ffprobe-utils' 8import {
9 canDoQuickTranscode,
10 getAudioStream,
11 getDurationFromVideoFile,
12 getMaxAudioBitrate,
13 getMetadataFromFile,
14 getVideoFileBitrate,
15 getVideoFileFPS
16} from '../helpers/ffprobe-utils'
9import { logger } from '../helpers/logger' 17import { logger } from '../helpers/logger'
10import { CONFIG } from '../initializers/config' 18import { CONFIG } from '../initializers/config'
11import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' 19import {
20 HLS_STREAMING_PLAYLIST_DIRECTORY,
21 P2P_MEDIA_LOADER_PEER_VERSION,
22 VIDEO_TRANSCODING_FPS,
23 WEBSERVER
24} from '../initializers/constants'
12import { VideoFileModel } from '../models/video/video-file' 25import { VideoFileModel } from '../models/video/video-file'
13import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 26import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
14import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' 27import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls'
@@ -31,8 +44,13 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileA
31 44
32 const transcodeOptions: TranscodeOptions = { 45 const transcodeOptions: TranscodeOptions = {
33 type: transcodeType, 46 type: transcodeType,
47
34 inputPath: videoInputPath, 48 inputPath: videoInputPath,
35 outputPath: videoTranscodedPath, 49 outputPath: videoTranscodedPath,
50
51 availableEncoders,
52 profile: 'default',
53
36 resolution: inputVideoFile.resolution 54 resolution: inputVideoFile.resolution
37 } 55 }
38 56
@@ -78,14 +96,23 @@ async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoR
78 const transcodeOptions = resolution === VideoResolution.H_NOVIDEO 96 const transcodeOptions = resolution === VideoResolution.H_NOVIDEO
79 ? { 97 ? {
80 type: 'only-audio' as 'only-audio', 98 type: 'only-audio' as 'only-audio',
99
81 inputPath: videoInputPath, 100 inputPath: videoInputPath,
82 outputPath: videoTranscodedPath, 101 outputPath: videoTranscodedPath,
102
103 availableEncoders,
104 profile: 'default',
105
83 resolution 106 resolution
84 } 107 }
85 : { 108 : {
86 type: 'video' as 'video', 109 type: 'video' as 'video',
87 inputPath: videoInputPath, 110 inputPath: videoInputPath,
88 outputPath: videoTranscodedPath, 111 outputPath: videoTranscodedPath,
112
113 availableEncoders,
114 profile: 'default',
115
89 resolution, 116 resolution,
90 isPortraitMode: isPortrait 117 isPortraitMode: isPortrait
91 } 118 }
@@ -111,8 +138,13 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video
111 138
112 const transcodeOptions = { 139 const transcodeOptions = {
113 type: 'merge-audio' as 'merge-audio', 140 type: 'merge-audio' as 'merge-audio',
141
114 inputPath: tmpPreviewPath, 142 inputPath: tmpPreviewPath,
115 outputPath: videoTranscodedPath, 143 outputPath: videoTranscodedPath,
144
145 availableEncoders,
146 profile: 'default',
147
116 audioPath: audioInputPath, 148 audioPath: audioInputPath,
117 resolution 149 resolution
118 } 150 }
@@ -156,8 +188,13 @@ async function generateHlsPlaylist (options: {
156 188
157 const transcodeOptions = { 189 const transcodeOptions = {
158 type: 'hls' as 'hls', 190 type: 'hls' as 'hls',
191
159 inputPath: videoInputPath, 192 inputPath: videoInputPath,
160 outputPath, 193 outputPath,
194
195 availableEncoders,
196 profile: 'default',
197
161 resolution, 198 resolution,
162 copyCodecs, 199 copyCodecs,
163 isPortraitMode, 200 isPortraitMode,
@@ -216,6 +253,75 @@ async function generateHlsPlaylist (options: {
216} 253}
217 254
218// --------------------------------------------------------------------------- 255// ---------------------------------------------------------------------------
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// ---------------------------------------------------------------------------
219 325
220export { 326export {
221 generateHlsPlaylist, 327 generateHlsPlaylist,