diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 7 | ||||
-rw-r--r-- | server/helpers/ffprobe-utils.ts | 6 | ||||
-rw-r--r-- | server/lib/video-transcoding-profiles.ts | 35 | ||||
-rw-r--r-- | server/lib/video-transcoding.ts | 13 | ||||
-rw-r--r-- | server/tests/api/live/live.ts | 12 |
5 files changed, 57 insertions, 16 deletions
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 712ec757e..69defccc4 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -9,6 +9,13 @@ import { getAudioStream, getClosestFramerateStandard, getVideoFileFPS } from './ | |||
9 | import { processImage } from './image-utils' | 9 | import { processImage } from './image-utils' |
10 | import { logger } from './logger' | 10 | import { logger } from './logger' |
11 | 11 | ||
12 | /** | ||
13 | * | ||
14 | * Functions that run transcoding/muxing ffmpeg processes | ||
15 | * Mainly called by lib/video-transcoding.ts and lib/live-manager.ts | ||
16 | * | ||
17 | */ | ||
18 | |||
12 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
13 | // Encoder options | 20 | // Encoder options |
14 | // --------------------------------------------------------------------------- | 21 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index d03ab91ac..16b295bbd 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts | |||
@@ -5,6 +5,12 @@ import { CONFIG } from '../initializers/config' | |||
5 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 5 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
6 | import { logger } from './logger' | 6 | import { logger } from './logger' |
7 | 7 | ||
8 | /** | ||
9 | * | ||
10 | * Helpers to run ffprobe and extract data from the JSON output | ||
11 | * | ||
12 | */ | ||
13 | |||
8 | function ffprobePromise (path: string) { | 14 | function ffprobePromise (path: string) { |
9 | return new Promise<ffmpeg.FfprobeData>((res, rej) => { | 15 | return new Promise<ffmpeg.FfprobeData>((res, rej) => { |
10 | ffmpeg.ffprobe(path, (err, data) => { | 16 | ffmpeg.ffprobe(path, (err, data) => { |
diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/video-transcoding-profiles.ts index 91a5c65f2..03c26f236 100644 --- a/server/lib/video-transcoding-profiles.ts +++ b/server/lib/video-transcoding-profiles.ts | |||
@@ -1,11 +1,22 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
1 | import { getTargetBitrate } from '../../shared/models/videos' | 2 | import { getTargetBitrate } from '../../shared/models/videos' |
2 | import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' | 3 | import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils' |
3 | import { ffprobePromise, getAudioStream, getMaxAudioBitrate, getVideoFileBitrate, getVideoStreamFromFile } from '../helpers/ffprobe-utils' | 4 | import { |
5 | canDoQuickAudioTranscode, | ||
6 | ffprobePromise, | ||
7 | getAudioStream, | ||
8 | getMaxAudioBitrate, | ||
9 | getVideoFileBitrate, | ||
10 | getVideoStreamFromFile | ||
11 | } from '../helpers/ffprobe-utils' | ||
4 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 12 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
5 | 13 | ||
6 | // --------------------------------------------------------------------------- | 14 | /** |
7 | // Available encoders profiles | 15 | * |
8 | // --------------------------------------------------------------------------- | 16 | * Available encoders and profiles for the transcoding jobs |
17 | * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile | ||
18 | * | ||
19 | */ | ||
9 | 20 | ||
10 | // Resources: | 21 | // Resources: |
11 | // * https://slhck.info/video/2017/03/01/rate-control.html | 22 | // * https://slhck.info/video/2017/03/01/rate-control.html |
@@ -27,7 +38,8 @@ const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, reso | |||
27 | 38 | ||
28 | return { | 39 | return { |
29 | outputOptions: [ | 40 | outputOptions: [ |
30 | `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` | 41 | `-maxrate ${targetBitrate}`, |
42 | `-bufsize ${targetBitrate * 2}` | ||
31 | ] | 43 | ] |
32 | } | 44 | } |
33 | } | 45 | } |
@@ -45,7 +57,14 @@ const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution | |||
45 | } | 57 | } |
46 | 58 | ||
47 | const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => { | 59 | const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => { |
48 | const parsedAudio = await getAudioStream(input) | 60 | const probe = await ffprobePromise(input) |
61 | |||
62 | if (await canDoQuickAudioTranscode(input, probe)) { | ||
63 | logger.debug('Copy audio stream %s by AAC encoder.', input) | ||
64 | return { copy: true, outputOptions: [] } | ||
65 | } | ||
66 | |||
67 | const parsedAudio = await getAudioStream(input, probe) | ||
49 | 68 | ||
50 | // We try to reduce the ceiling bitrate by making rough matches of bitrates | 69 | // We try to reduce the ceiling bitrate by making rough matches of bitrates |
51 | // Of course this is far from perfect, but it might save some space in the end | 70 | // Of course this is far from perfect, but it might save some space in the end |
@@ -54,11 +73,13 @@ const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNu | |||
54 | 73 | ||
55 | const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) | 74 | const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate) |
56 | 75 | ||
76 | logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName }) | ||
77 | |||
57 | if (bitrate !== undefined && bitrate !== -1) { | 78 | if (bitrate !== undefined && bitrate !== -1) { |
58 | return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] } | 79 | return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] } |
59 | } | 80 | } |
60 | 81 | ||
61 | return { copy: true, outputOptions: [] } | 82 | return { outputOptions: [ ] } |
62 | } | 83 | } |
63 | 84 | ||
64 | const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { | 85 | const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => { |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index aaad219dd..e022f2a68 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -16,8 +16,13 @@ import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath | |||
16 | import { availableEncoders } from './video-transcoding-profiles' | 16 | import { availableEncoders } from './video-transcoding-profiles' |
17 | 17 | ||
18 | /** | 18 | /** |
19 | * Optimize the original video file and replace it. The resolution is not changed. | 19 | * |
20 | * Functions that run transcoding functions, update the database, cleanup files, create torrent files... | ||
21 | * Mainly called by the job queue | ||
22 | * | ||
20 | */ | 23 | */ |
24 | |||
25 | // Optimize the original video file and replace it. The resolution is not changed. | ||
21 | async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { | 26 | async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { |
22 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 27 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
23 | const newExtname = '.mp4' | 28 | const newExtname = '.mp4' |
@@ -62,9 +67,7 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFileA | |||
62 | } | 67 | } |
63 | } | 68 | } |
64 | 69 | ||
65 | /** | 70 | // Transcode the original video file to a lower resolution. |
66 | * Transcode the original video file to a lower resolution. | ||
67 | */ | ||
68 | async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { | 71 | async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { |
69 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 72 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
70 | const extname = '.mp4' | 73 | const extname = '.mp4' |
@@ -110,6 +113,7 @@ async function transcodeNewResolution (video: MVideoWithFile, resolution: VideoR | |||
110 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) | 113 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) |
111 | } | 114 | } |
112 | 115 | ||
116 | // Merge an image with an audio file to create a video | ||
113 | async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) { | 117 | async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution) { |
114 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 118 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
115 | const newExtname = '.mp4' | 119 | const newExtname = '.mp4' |
@@ -159,6 +163,7 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video | |||
159 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) | 163 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) |
160 | } | 164 | } |
161 | 165 | ||
166 | // Generate an HLS playlist from an input file, and update the master playlist | ||
162 | async function generateHlsPlaylist (options: { | 167 | async function generateHlsPlaylist (options: { |
163 | video: MVideoWithFile | 168 | video: MVideoWithFile |
164 | videoInputPath: string | 169 | videoInputPath: string |
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts index e685be08e..0786db554 100644 --- a/server/tests/api/live/live.ts +++ b/server/tests/api/live/live.ts | |||
@@ -39,6 +39,7 @@ import { | |||
39 | viewVideo, | 39 | viewVideo, |
40 | wait, | 40 | wait, |
41 | waitJobs, | 41 | waitJobs, |
42 | waitUntilLivePublished, | ||
42 | waitUntilLiveStarts, | 43 | waitUntilLiveStarts, |
43 | waitUntilLog | 44 | waitUntilLog |
44 | } from '../../../../shared/extra-utils' | 45 | } from '../../../../shared/extra-utils' |
@@ -396,7 +397,7 @@ describe('Test live', function () { | |||
396 | }) | 397 | }) |
397 | 398 | ||
398 | it('Should enable transcoding with some resolutions and correctly save them', async function () { | 399 | it('Should enable transcoding with some resolutions and correctly save them', async function () { |
399 | this.timeout(60000) | 400 | this.timeout(120000) |
400 | 401 | ||
401 | const resolutions = [ 240, 360, 720 ] | 402 | const resolutions = [ 240, 360, 720 ] |
402 | 403 | ||
@@ -410,13 +411,14 @@ describe('Test live', function () { | |||
410 | await testVideoResolutions(liveVideoId, resolutions) | 411 | await testVideoResolutions(liveVideoId, resolutions) |
411 | 412 | ||
412 | await stopFfmpeg(command) | 413 | await stopFfmpeg(command) |
414 | await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId) | ||
413 | 415 | ||
414 | await waitJobs(servers) | 416 | await waitJobs(servers) |
415 | 417 | ||
416 | const bitrateLimits = { | 418 | const bitrateLimits = { |
417 | 720: 2800 * 1000, | 419 | 720: 3000 * 1000, |
418 | 360: 780 * 1000, | 420 | 360: 1100 * 1000, |
419 | 240: 320 * 1000 | 421 | 240: 600 * 1000 |
420 | } | 422 | } |
421 | 423 | ||
422 | for (const server of servers) { | 424 | for (const server of servers) { |
@@ -442,7 +444,7 @@ describe('Test live', function () { | |||
442 | 444 | ||
443 | const probe = await ffprobePromise(segmentPath) | 445 | const probe = await ffprobePromise(segmentPath) |
444 | const videoStream = await getVideoStreamFromFile(segmentPath, probe) | 446 | const videoStream = await getVideoStreamFromFile(segmentPath, probe) |
445 | console.log(videoStream) | 447 | |
446 | expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height]) | 448 | expect(probe.format.bit_rate).to.be.below(bitrateLimits[videoStream.height]) |
447 | 449 | ||
448 | await makeRawRequest(file.torrentUrl, 200) | 450 | await makeRawRequest(file.torrentUrl, 200) |