diff options
author | Chocobozzz <me@florianbigard.com> | 2021-11-05 11:36:03 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-11-05 11:38:17 +0100 |
commit | df1db951c512a58110171d046ef367789df02733 (patch) | |
tree | a8894b4a4864d9e378923f011b4ca9d206e2ee0b /server/lib/live/live-manager.ts | |
parent | 8dd754c76735417305c4b68e2ada6f623e9d7650 (diff) | |
download | PeerTube-df1db951c512a58110171d046ef367789df02733.tar.gz PeerTube-df1db951c512a58110171d046ef367789df02733.tar.zst PeerTube-df1db951c512a58110171d046ef367789df02733.zip |
Support RTMPS
Diffstat (limited to 'server/lib/live/live-manager.ts')
-rw-r--r-- | server/lib/live/live-manager.ts | 92 |
1 files changed, 62 insertions, 30 deletions
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts index d7dc841d9..c75cc3bda 100644 --- a/server/lib/live/live-manager.ts +++ b/server/lib/live/live-manager.ts | |||
@@ -1,5 +1,7 @@ | |||
1 | 1 | ||
2 | import { readFile } from 'fs-extra' | ||
2 | import { createServer, Server } from 'net' | 3 | import { createServer, Server } from 'net' |
4 | import { createServer as createServerTLS, Server as ServerTLS } from 'tls' | ||
3 | import { isTestInstance } from '@server/helpers/core-utils' | 5 | import { isTestInstance } from '@server/helpers/core-utils' |
4 | import { | 6 | import { |
5 | computeResolutionsToTranscode, | 7 | computeResolutionsToTranscode, |
@@ -19,8 +21,8 @@ import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/ | |||
19 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' | 21 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' |
20 | import { federateVideoIfNeeded } from '../activitypub/videos' | 22 | import { federateVideoIfNeeded } from '../activitypub/videos' |
21 | import { JobQueue } from '../job-queue' | 23 | import { JobQueue } from '../job-queue' |
22 | import { PeerTubeSocket } from '../peertube-socket' | ||
23 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../paths' | 24 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../paths' |
25 | import { PeerTubeSocket } from '../peertube-socket' | ||
24 | import { LiveQuotaStore } from './live-quota-store' | 26 | import { LiveQuotaStore } from './live-quota-store' |
25 | import { LiveSegmentShaStore } from './live-segment-sha-store' | 27 | import { LiveSegmentShaStore } from './live-segment-sha-store' |
26 | import { cleanupLive } from './live-utils' | 28 | import { cleanupLive } from './live-utils' |
@@ -40,9 +42,6 @@ const config = { | |||
40 | gop_cache: VIDEO_LIVE.RTMP.GOP_CACHE, | 42 | gop_cache: VIDEO_LIVE.RTMP.GOP_CACHE, |
41 | ping: VIDEO_LIVE.RTMP.PING, | 43 | ping: VIDEO_LIVE.RTMP.PING, |
42 | ping_timeout: VIDEO_LIVE.RTMP.PING_TIMEOUT | 44 | ping_timeout: VIDEO_LIVE.RTMP.PING_TIMEOUT |
43 | }, | ||
44 | transcoding: { | ||
45 | ffmpeg: 'ffmpeg' | ||
46 | } | 45 | } |
47 | } | 46 | } |
48 | 47 | ||
@@ -58,6 +57,9 @@ class LiveManager { | |||
58 | private readonly watchersPerVideo = new Map<number, number[]>() | 57 | private readonly watchersPerVideo = new Map<number, number[]>() |
59 | 58 | ||
60 | private rtmpServer: Server | 59 | private rtmpServer: Server |
60 | private rtmpsServer: ServerTLS | ||
61 | |||
62 | private running = false | ||
61 | 63 | ||
62 | private constructor () { | 64 | private constructor () { |
63 | } | 65 | } |
@@ -73,7 +75,9 @@ class LiveManager { | |||
73 | return this.abortSession(sessionId) | 75 | return this.abortSession(sessionId) |
74 | } | 76 | } |
75 | 77 | ||
76 | this.handleSession(sessionId, streamPath, splittedPath[2]) | 78 | const session = this.getContext().sessions.get(sessionId) |
79 | |||
80 | this.handleSession(sessionId, session.inputOriginUrl + streamPath, splittedPath[2]) | ||
77 | .catch(err => logger.error('Cannot handle sessions.', { err, ...lTags(sessionId) })) | 81 | .catch(err => logger.error('Cannot handle sessions.', { err, ...lTags(sessionId) })) |
78 | }) | 82 | }) |
79 | 83 | ||
@@ -82,12 +86,12 @@ class LiveManager { | |||
82 | }) | 86 | }) |
83 | 87 | ||
84 | registerConfigChangedHandler(() => { | 88 | registerConfigChangedHandler(() => { |
85 | if (!this.rtmpServer && CONFIG.LIVE.ENABLED === true) { | 89 | if (!this.running && CONFIG.LIVE.ENABLED === true) { |
86 | this.run() | 90 | this.run().catch(err => logger.error('Cannot run live server.', { err })) |
87 | return | 91 | return |
88 | } | 92 | } |
89 | 93 | ||
90 | if (this.rtmpServer && CONFIG.LIVE.ENABLED === false) { | 94 | if (this.running && CONFIG.LIVE.ENABLED === false) { |
91 | this.stop() | 95 | this.stop() |
92 | } | 96 | } |
93 | }) | 97 | }) |
@@ -99,23 +103,53 @@ class LiveManager { | |||
99 | setInterval(() => this.updateLiveViews(), VIEW_LIFETIME.LIVE) | 103 | setInterval(() => this.updateLiveViews(), VIEW_LIFETIME.LIVE) |
100 | } | 104 | } |
101 | 105 | ||
102 | run () { | 106 | async run () { |
103 | logger.info('Running RTMP server on port %d', config.rtmp.port, lTags()) | 107 | this.running = true |
104 | 108 | ||
105 | this.rtmpServer = createServer(socket => { | 109 | if (CONFIG.LIVE.RTMP.ENABLED) { |
106 | const session = new NodeRtmpSession(config, socket) | 110 | logger.info('Running RTMP server on port %d', CONFIG.LIVE.RTMP.PORT, lTags()) |
107 | 111 | ||
108 | session.run() | 112 | this.rtmpServer = createServer(socket => { |
109 | }) | 113 | const session = new NodeRtmpSession(config, socket) |
110 | 114 | ||
111 | this.rtmpServer.on('error', err => { | 115 | session.inputOriginUrl = 'rtmp://127.0.0.1:' + CONFIG.LIVE.RTMP.PORT |
112 | logger.error('Cannot run RTMP server.', { err, ...lTags() }) | 116 | session.run() |
113 | }) | 117 | }) |
118 | |||
119 | this.rtmpServer.on('error', err => { | ||
120 | logger.error('Cannot run RTMP server.', { err, ...lTags() }) | ||
121 | }) | ||
122 | |||
123 | this.rtmpServer.listen(CONFIG.LIVE.RTMP.PORT) | ||
124 | } | ||
114 | 125 | ||
115 | this.rtmpServer.listen(CONFIG.LIVE.RTMP.PORT) | 126 | if (CONFIG.LIVE.RTMPS.ENABLED) { |
127 | logger.info('Running RTMPS server on port %d', CONFIG.LIVE.RTMPS.PORT, lTags()) | ||
128 | |||
129 | const [ key, cert ] = await Promise.all([ | ||
130 | readFile(CONFIG.LIVE.RTMPS.KEY_FILE), | ||
131 | readFile(CONFIG.LIVE.RTMPS.CERT_FILE) | ||
132 | ]) | ||
133 | const serverOptions = { key, cert } | ||
134 | |||
135 | this.rtmpsServer = createServerTLS(serverOptions, socket => { | ||
136 | const session = new NodeRtmpSession(config, socket) | ||
137 | |||
138 | session.inputOriginUrl = 'rtmps://127.0.0.1:' + CONFIG.LIVE.RTMPS.PORT | ||
139 | session.run() | ||
140 | }) | ||
141 | |||
142 | this.rtmpsServer.on('error', err => { | ||
143 | logger.error('Cannot run RTMPS server.', { err, ...lTags() }) | ||
144 | }) | ||
145 | |||
146 | this.rtmpsServer.listen(CONFIG.LIVE.RTMPS.PORT) | ||
147 | } | ||
116 | } | 148 | } |
117 | 149 | ||
118 | stop () { | 150 | stop () { |
151 | this.running = false | ||
152 | |||
119 | logger.info('Stopping RTMP server.', lTags()) | 153 | logger.info('Stopping RTMP server.', lTags()) |
120 | 154 | ||
121 | this.rtmpServer.close() | 155 | this.rtmpServer.close() |
@@ -174,7 +208,7 @@ class LiveManager { | |||
174 | } | 208 | } |
175 | } | 209 | } |
176 | 210 | ||
177 | private async handleSession (sessionId: string, streamPath: string, streamKey: string) { | 211 | private async handleSession (sessionId: string, inputUrl: string, streamKey: string) { |
178 | const videoLive = await VideoLiveModel.loadByStreamKey(streamKey) | 212 | const videoLive = await VideoLiveModel.loadByStreamKey(streamKey) |
179 | if (!videoLive) { | 213 | if (!videoLive) { |
180 | logger.warn('Unknown live video with stream key %s.', streamKey, lTags(sessionId)) | 214 | logger.warn('Unknown live video with stream key %s.', streamKey, lTags(sessionId)) |
@@ -197,20 +231,18 @@ class LiveManager { | |||
197 | 231 | ||
198 | this.videoSessions.set(video.id, sessionId) | 232 | this.videoSessions.set(video.id, sessionId) |
199 | 233 | ||
200 | const rtmpUrl = 'rtmp://127.0.0.1:' + config.rtmp.port + streamPath | ||
201 | |||
202 | const now = Date.now() | 234 | const now = Date.now() |
203 | const probe = await ffprobePromise(rtmpUrl) | 235 | const probe = await ffprobePromise(inputUrl) |
204 | 236 | ||
205 | const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ | 237 | const [ { resolution, ratio }, fps, bitrate ] = await Promise.all([ |
206 | getVideoFileResolution(rtmpUrl, probe), | 238 | getVideoFileResolution(inputUrl, probe), |
207 | getVideoFileFPS(rtmpUrl, probe), | 239 | getVideoFileFPS(inputUrl, probe), |
208 | getVideoFileBitrate(rtmpUrl, probe) | 240 | getVideoFileBitrate(inputUrl, probe) |
209 | ]) | 241 | ]) |
210 | 242 | ||
211 | logger.info( | 243 | logger.info( |
212 | '%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', | 244 | '%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', |
213 | rtmpUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) | 245 | inputUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) |
214 | ) | 246 | ) |
215 | 247 | ||
216 | const allResolutions = this.buildAllResolutionsToTranscode(resolution) | 248 | const allResolutions = this.buildAllResolutionsToTranscode(resolution) |
@@ -226,7 +258,7 @@ class LiveManager { | |||
226 | sessionId, | 258 | sessionId, |
227 | videoLive, | 259 | videoLive, |
228 | streamingPlaylist, | 260 | streamingPlaylist, |
229 | rtmpUrl, | 261 | inputUrl, |
230 | fps, | 262 | fps, |
231 | bitrate, | 263 | bitrate, |
232 | ratio, | 264 | ratio, |
@@ -238,13 +270,13 @@ class LiveManager { | |||
238 | sessionId: string | 270 | sessionId: string |
239 | videoLive: MVideoLiveVideo | 271 | videoLive: MVideoLiveVideo |
240 | streamingPlaylist: MStreamingPlaylistVideo | 272 | streamingPlaylist: MStreamingPlaylistVideo |
241 | rtmpUrl: string | 273 | inputUrl: string |
242 | fps: number | 274 | fps: number |
243 | bitrate: number | 275 | bitrate: number |
244 | ratio: number | 276 | ratio: number |
245 | allResolutions: number[] | 277 | allResolutions: number[] |
246 | }) { | 278 | }) { |
247 | const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, rtmpUrl } = options | 279 | const { sessionId, videoLive, streamingPlaylist, allResolutions, fps, bitrate, ratio, inputUrl } = options |
248 | const videoUUID = videoLive.Video.uuid | 280 | const videoUUID = videoLive.Video.uuid |
249 | const localLTags = lTags(sessionId, videoUUID) | 281 | const localLTags = lTags(sessionId, videoUUID) |
250 | 282 | ||
@@ -257,7 +289,7 @@ class LiveManager { | |||
257 | sessionId, | 289 | sessionId, |
258 | videoLive, | 290 | videoLive, |
259 | streamingPlaylist, | 291 | streamingPlaylist, |
260 | rtmpUrl, | 292 | inputUrl, |
261 | bitrate, | 293 | bitrate, |
262 | ratio, | 294 | ratio, |
263 | fps, | 295 | fps, |