diff options
author | Chocobozzz <me@florianbigard.com> | 2022-09-28 10:23:03 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-09-28 10:26:28 +0200 |
commit | 1ce4256a6577d0607320a320d9c5d328bdd162f7 (patch) | |
tree | e531ab2ced0e874859794be0636d33aa04c81e6b | |
parent | 690bad52e186a0111359062743d9638b911026f2 (diff) | |
download | PeerTube-1ce4256a6577d0607320a320d9c5d328bdd162f7.tar.gz PeerTube-1ce4256a6577d0607320a320d9c5d328bdd162f7.tar.zst PeerTube-1ce4256a6577d0607320a320d9c5d328bdd162f7.zip |
Correctly handle RTMP streams without audio
-rw-r--r-- | server/helpers/ffmpeg/ffmpeg-live.ts | 24 | ||||
-rw-r--r-- | server/lib/live/live-manager.ts | 29 | ||||
-rw-r--r-- | server/lib/live/shared/muxing-session.ts | 7 |
3 files changed, 43 insertions, 17 deletions
diff --git a/server/helpers/ffmpeg/ffmpeg-live.ts b/server/helpers/ffmpeg/ffmpeg-live.ts index 8377dc7e2..379d7b1ad 100644 --- a/server/helpers/ffmpeg/ffmpeg-live.ts +++ b/server/helpers/ffmpeg/ffmpeg-live.ts | |||
@@ -23,11 +23,24 @@ async function getLiveTranscodingCommand (options: { | |||
23 | fps: number | 23 | fps: number |
24 | bitrate: number | 24 | bitrate: number |
25 | ratio: number | 25 | ratio: number |
26 | hasAudio: boolean | ||
26 | 27 | ||
27 | availableEncoders: AvailableEncoders | 28 | availableEncoders: AvailableEncoders |
28 | profile: string | 29 | profile: string |
29 | }) { | 30 | }) { |
30 | const { inputUrl, outPath, resolutions, fps, bitrate, availableEncoders, profile, masterPlaylistName, ratio, latencyMode } = options | 31 | const { |
32 | inputUrl, | ||
33 | outPath, | ||
34 | resolutions, | ||
35 | fps, | ||
36 | bitrate, | ||
37 | availableEncoders, | ||
38 | profile, | ||
39 | masterPlaylistName, | ||
40 | ratio, | ||
41 | latencyMode, | ||
42 | hasAudio | ||
43 | } = options | ||
31 | 44 | ||
32 | const command = getFFmpeg(inputUrl, 'live') | 45 | const command = getFFmpeg(inputUrl, 'live') |
33 | 46 | ||
@@ -47,6 +60,7 @@ async function getLiveTranscodingCommand (options: { | |||
47 | addDefaultEncoderGlobalParams(command) | 60 | addDefaultEncoderGlobalParams(command) |
48 | 61 | ||
49 | for (let i = 0; i < resolutions.length; i++) { | 62 | for (let i = 0; i < resolutions.length; i++) { |
63 | const streamMap: string[] = [] | ||
50 | const resolution = resolutions[i] | 64 | const resolution = resolutions[i] |
51 | const resolutionFPS = computeFPS(fps, resolution) | 65 | const resolutionFPS = computeFPS(fps, resolution) |
52 | 66 | ||
@@ -94,9 +108,11 @@ async function getLiveTranscodingCommand (options: { | |||
94 | options: `w=-2:h=${resolution}`, | 108 | options: `w=-2:h=${resolution}`, |
95 | outputs: `vout${resolution}` | 109 | outputs: `vout${resolution}` |
96 | }) | 110 | }) |
111 | |||
112 | streamMap.push(`v:${i}`) | ||
97 | } | 113 | } |
98 | 114 | ||
99 | { | 115 | if (hasAudio) { |
100 | const streamType: StreamType = 'audio' | 116 | const streamType: StreamType = 'audio' |
101 | const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) | 117 | const builderResult = await getEncoderBuilderResult({ ...baseEncoderBuilderParams, streamType }) |
102 | if (!builderResult) { | 118 | if (!builderResult) { |
@@ -114,9 +130,11 @@ async function getLiveTranscodingCommand (options: { | |||
114 | 130 | ||
115 | command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`) | 131 | command.outputOption(`${buildStreamSuffix('-c:a', i)} ${builderResult.encoder}`) |
116 | applyEncoderOptions(command, builderResult.result) | 132 | applyEncoderOptions(command, builderResult.result) |
133 | |||
134 | streamMap.push(`a:${i}`) | ||
117 | } | 135 | } |
118 | 136 | ||
119 | varStreamMap.push(`v:${i},a:${i}`) | 137 | varStreamMap.push(streamMap.join(',')) |
120 | } | 138 | } |
121 | 139 | ||
122 | command.complexFilter(complexFilter) | 140 | command.complexFilter(complexFilter) |
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts index aadd8e308..16715862b 100644 --- a/server/lib/live/live-manager.ts +++ b/server/lib/live/live-manager.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | import { readdir, readFile } from 'fs-extra' | 1 | import { readdir, readFile } from 'fs-extra' |
3 | import { createServer, Server } from 'net' | 2 | import { createServer, Server } from 'net' |
4 | import { join } from 'path' | 3 | import { join } from 'path' |
@@ -9,7 +8,8 @@ import { | |||
9 | getLiveSegmentTime, | 8 | getLiveSegmentTime, |
10 | getVideoStreamBitrate, | 9 | getVideoStreamBitrate, |
11 | getVideoStreamDimensionsInfo, | 10 | getVideoStreamDimensionsInfo, |
12 | getVideoStreamFPS | 11 | getVideoStreamFPS, |
12 | hasAudioStream | ||
13 | } from '@server/helpers/ffmpeg' | 13 | } from '@server/helpers/ffmpeg' |
14 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 14 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
15 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' | 15 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' |
@@ -20,7 +20,7 @@ import { VideoLiveModel } from '@server/models/video/video-live' | |||
20 | import { VideoLiveSessionModel } from '@server/models/video/video-live-session' | 20 | import { VideoLiveSessionModel } from '@server/models/video/video-live-session' |
21 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 21 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
22 | import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models' | 22 | import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models' |
23 | import { wait } from '@shared/core-utils' | 23 | import { pick, wait } from '@shared/core-utils' |
24 | import { LiveVideoError, VideoState, VideoStreamingPlaylistType } from '@shared/models' | 24 | import { LiveVideoError, VideoState, VideoStreamingPlaylistType } from '@shared/models' |
25 | import { federateVideoIfNeeded } from '../activitypub/videos' | 25 | import { federateVideoIfNeeded } from '../activitypub/videos' |
26 | import { JobQueue } from '../job-queue' | 26 | import { JobQueue } from '../job-queue' |
@@ -232,10 +232,11 @@ class LiveManager { | |||
232 | const now = Date.now() | 232 | const now = Date.now() |
233 | const probe = await ffprobePromise(inputUrl) | 233 | const probe = await ffprobePromise(inputUrl) |
234 | 234 | ||
235 | const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ | 235 | const [ { resolution, ratio }, fps, bitrate, hasAudio ] = await Promise.all([ |
236 | getVideoStreamDimensionsInfo(inputUrl, probe), | 236 | getVideoStreamDimensionsInfo(inputUrl, probe), |
237 | getVideoStreamFPS(inputUrl, probe), | 237 | getVideoStreamFPS(inputUrl, probe), |
238 | getVideoStreamBitrate(inputUrl, probe) | 238 | getVideoStreamBitrate(inputUrl, probe), |
239 | hasAudioStream(inputUrl, probe) | ||
239 | ]) | 240 | ]) |
240 | 241 | ||
241 | logger.info( | 242 | logger.info( |
@@ -259,26 +260,30 @@ class LiveManager { | |||
259 | return this.runMuxingSession({ | 260 | return this.runMuxingSession({ |
260 | sessionId, | 261 | sessionId, |
261 | videoLive, | 262 | videoLive, |
263 | |||
262 | streamingPlaylist, | 264 | streamingPlaylist, |
263 | inputUrl, | 265 | inputUrl, |
264 | fps, | 266 | fps, |
265 | bitrate, | 267 | bitrate, |
266 | ratio, | 268 | ratio, |
267 | allResolutions | 269 | allResolutions, |
270 | hasAudio | ||
268 | }) | 271 | }) |
269 | } | 272 | } |
270 | 273 | ||
271 | private async runMuxingSession (options: { | 274 | private async runMuxingSession (options: { |
272 | sessionId: string | 275 | sessionId: string |
273 | videoLive: MVideoLiveVideo | 276 | videoLive: MVideoLiveVideo |
277 | |||
274 | streamingPlaylist: MStreamingPlaylistVideo | 278 | streamingPlaylist: MStreamingPlaylistVideo |
275 | inputUrl: string | 279 | inputUrl: string |
276 | fps: number | 280 | fps: number |
277 | bitrate: number | 281 | bitrate: number |
278 | ratio: number | 282 | ratio: number |
279 | allResolutions: number[] | 283 | allResolutions: number[] |
284 | hasAudio: boolean | ||
280 | }) { | 285 | }) { |
281 | const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, inputUrl } = options | 286 | const { sessionId, videoLive } = options |
282 | const videoUUID = videoLive.Video.uuid | 287 | const videoUUID = videoLive.Video.uuid |
283 | const localLTags = lTags(sessionId, videoUUID) | 288 | const localLTags = lTags(sessionId, videoUUID) |
284 | 289 | ||
@@ -289,15 +294,11 @@ class LiveManager { | |||
289 | 294 | ||
290 | const muxingSession = new MuxingSession({ | 295 | const muxingSession = new MuxingSession({ |
291 | context: this.getContext(), | 296 | context: this.getContext(), |
292 | user, | ||
293 | sessionId, | 297 | sessionId, |
294 | videoLive, | 298 | videoLive, |
295 | streamingPlaylist, | 299 | user, |
296 | inputUrl, | 300 | |
297 | bitrate, | 301 | ...pick(options, [ 'streamingPlaylist', 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ]) |
298 | ratio, | ||
299 | fps, | ||
300 | allResolutions | ||
301 | }) | 302 | }) |
302 | 303 | ||
303 | muxingSession.on('master-playlist-created', () => this.publishAndFederateLive(videoLive, localLTags)) | 304 | muxingSession.on('master-playlist-created', () => this.publishAndFederateLive(videoLive, localLTags)) |
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts index 310a7026d..505717dce 100644 --- a/server/lib/live/shared/muxing-session.ts +++ b/server/lib/live/shared/muxing-session.ts | |||
@@ -59,6 +59,8 @@ class MuxingSession extends EventEmitter { | |||
59 | private readonly bitrate: number | 59 | private readonly bitrate: number |
60 | private readonly ratio: number | 60 | private readonly ratio: number |
61 | 61 | ||
62 | private readonly hasAudio: boolean | ||
63 | |||
62 | private readonly videoId: number | 64 | private readonly videoId: number |
63 | private readonly videoUUID: string | 65 | private readonly videoUUID: string |
64 | private readonly saveReplay: boolean | 66 | private readonly saveReplay: boolean |
@@ -94,6 +96,7 @@ class MuxingSession extends EventEmitter { | |||
94 | bitrate: number | 96 | bitrate: number |
95 | ratio: number | 97 | ratio: number |
96 | allResolutions: number[] | 98 | allResolutions: number[] |
99 | hasAudio: boolean | ||
97 | }) { | 100 | }) { |
98 | super() | 101 | super() |
99 | 102 | ||
@@ -108,6 +111,8 @@ class MuxingSession extends EventEmitter { | |||
108 | this.bitrate = options.bitrate | 111 | this.bitrate = options.bitrate |
109 | this.ratio = options.ratio | 112 | this.ratio = options.ratio |
110 | 113 | ||
114 | this.hasAudio = options.hasAudio | ||
115 | |||
111 | this.allResolutions = options.allResolutions | 116 | this.allResolutions = options.allResolutions |
112 | 117 | ||
113 | this.videoId = this.videoLive.Video.id | 118 | this.videoId = this.videoLive.Video.id |
@@ -140,6 +145,8 @@ class MuxingSession extends EventEmitter { | |||
140 | bitrate: this.bitrate, | 145 | bitrate: this.bitrate, |
141 | ratio: this.ratio, | 146 | ratio: this.ratio, |
142 | 147 | ||
148 | hasAudio: this.hasAudio, | ||
149 | |||
143 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 150 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
144 | profile: CONFIG.LIVE.TRANSCODING.PROFILE | 151 | profile: CONFIG.LIVE.TRANSCODING.PROFILE |
145 | }) | 152 | }) |