@@ -55,8 +55,12 @@ const WEBSERVER = { | |||
WS: '', | |||
HOSTNAME: '', | |||
PORT: 0, | |||
RTMP_URL: '', | |||
RTMPS_URL: '' | |||
RTMPS_URL: '', | |||
RTMP_BASE_LIVE_URL: '', | |||
RTMPS_BASE_LIVE_URL: '' | |||
} | |||
// Sortable columns per schema | |||
@@ -1242,8 +1246,11 @@ function updateWebserverUrls () { | |||
const rtmpHostname = CONFIG.LIVE.RTMP.PUBLIC_HOSTNAME || CONFIG.WEBSERVER.HOSTNAME | |||
const rtmpsHostname = CONFIG.LIVE.RTMPS.PUBLIC_HOSTNAME || CONFIG.WEBSERVER.HOSTNAME | |||
WEBSERVER.RTMP_URL = 'rtmp://' + rtmpHostname + ':' + CONFIG.LIVE.RTMP.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH | |||
WEBSERVER.RTMPS_URL = 'rtmps://' + rtmpsHostname + ':' + CONFIG.LIVE.RTMPS.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH | |||
WEBSERVER.RTMP_URL = 'rtmp://' + rtmpHostname + ':' + CONFIG.LIVE.RTMP.PORT | |||
WEBSERVER.RTMPS_URL = 'rtmps://' + rtmpsHostname + ':' + CONFIG.LIVE.RTMPS.PORT | |||
WEBSERVER.RTMP_BASE_LIVE_URL = WEBSERVER.RTMP_URL + '/' + VIDEO_LIVE.RTMP.BASE_PATH | |||
WEBSERVER.RTMPS_BASE_LIVE_URL = WEBSERVER.RTMPS_URL + '/' + VIDEO_LIVE.RTMP.BASE_PATH | |||
} | |||
function updateWebserverConfig () { | |||
@@ -4,7 +4,7 @@ import { join } from 'path' | |||
import { createServer as createServerTLS, Server as ServerTLS } from 'tls' | |||
import { logger, loggerTagsFactory } from '@server/helpers/logger' | |||
import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' | |||
import { VIDEO_LIVE } from '@server/initializers/constants' | |||
import { VIDEO_LIVE, WEBSERVER } from '@server/initializers/constants' | |||
import { sequelizeTypescript } from '@server/initializers/database' | |||
import { RunnerJobModel } from '@server/models/runner/runner-job' | |||
import { UserModel } from '@server/models/user/user' | |||
@@ -73,8 +73,10 @@ class LiveManager { | |||
} | |||
const session = this.getContext().sessions.get(sessionId) | |||
const inputLocalUrl = session.inputOriginLocalUrl + streamPath | |||
const inputPublicUrl = session.inputOriginPublicUrl + streamPath | |||
this.handleSession(sessionId, session.inputOriginUrl + streamPath, splittedPath[2]) | |||
this.handleSession({ sessionId, inputPublicUrl, inputLocalUrl, streamKey: splittedPath[2] }) | |||
.catch(err => logger.error('Cannot handle sessions.', { err, ...lTags(sessionId) })) | |||
}) | |||
@@ -110,7 +112,8 @@ class LiveManager { | |||
this.rtmpServer = createServer(socket => { | |||
const session = new NodeRtmpSession(config, socket) | |||
session.inputOriginUrl = 'rtmp://127.0.0.1:' + CONFIG.LIVE.RTMP.PORT | |||
session.inputOriginLocalUrl = 'rtmp://127.0.0.1:' + CONFIG.LIVE.RTMP.PORT | |||
session.inputOriginPublicUrl = WEBSERVER.RTMP_URL | |||
session.run() | |||
}) | |||
@@ -133,7 +136,8 @@ class LiveManager { | |||
this.rtmpsServer = createServerTLS(serverOptions, socket => { | |||
const session = new NodeRtmpSession(config, socket) | |||
session.inputOriginUrl = 'rtmps://127.0.0.1:' + CONFIG.LIVE.RTMPS.PORT | |||
session.inputOriginLocalUrl = 'rtmps://127.0.0.1:' + CONFIG.LIVE.RTMPS.PORT | |||
session.inputOriginPublicUrl = WEBSERVER.RTMPS_URL | |||
session.run() | |||
}) | |||
@@ -210,7 +214,14 @@ class LiveManager { | |||
} | |||
} | |||
private async handleSession (sessionId: string, inputUrl: string, streamKey: string) { | |||
private async handleSession (options: { | |||
sessionId: string | |||
inputLocalUrl: string | |||
inputPublicUrl: string | |||
streamKey: string | |||
}) { | |||
const { inputLocalUrl, inputPublicUrl, sessionId, streamKey } = options | |||
const videoLive = await VideoLiveModel.loadByStreamKey(streamKey) | |||
if (!videoLive) { | |||
logger.warn('Unknown live video with stream key %s.', streamKey, lTags(sessionId)) | |||
@@ -239,18 +250,18 @@ class LiveManager { | |||
this.videoSessions.set(video.uuid, sessionId) | |||
const now = Date.now() | |||
const probe = await ffprobePromise(inputUrl) | |||
const probe = await ffprobePromise(inputLocalUrl) | |||
const [ { resolution, ratio }, fps, bitrate, hasAudio ] = await Promise.all([ | |||
getVideoStreamDimensionsInfo(inputUrl, probe), | |||
getVideoStreamFPS(inputUrl, probe), | |||
getVideoStreamBitrate(inputUrl, probe), | |||
hasAudioStream(inputUrl, probe) | |||
getVideoStreamDimensionsInfo(inputLocalUrl, probe), | |||
getVideoStreamFPS(inputLocalUrl, probe), | |||
getVideoStreamBitrate(inputLocalUrl, probe), | |||
hasAudioStream(inputLocalUrl, probe) | |||
]) | |||
logger.info( | |||
'%s probing took %d ms (bitrate: %d, fps: %d, resolution: %d)', | |||
inputUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) | |||
inputLocalUrl, Date.now() - now, bitrate, fps, resolution, lTags(sessionId, video.uuid) | |||
) | |||
const allResolutions = await Hooks.wrapObject( | |||
@@ -268,7 +279,8 @@ class LiveManager { | |||
sessionId, | |||
videoLive, | |||
inputUrl, | |||
inputLocalUrl, | |||
inputPublicUrl, | |||
fps, | |||
bitrate, | |||
ratio, | |||
@@ -281,7 +293,9 @@ class LiveManager { | |||
sessionId: string | |||
videoLive: MVideoLiveVideoWithSetting | |||
inputUrl: string | |||
inputLocalUrl: string | |||
inputPublicUrl: string | |||
fps: number | |||
bitrate: number | |||
ratio: number | |||
@@ -303,7 +317,7 @@ class LiveManager { | |||
videoLive, | |||
user, | |||
...pick(options, [ 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ]) | |||
...pick(options, [ 'inputLocalUrl', 'inputPublicUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ]) | |||
}) | |||
muxingSession.on('live-ready', () => this.publishAndFederateLive(videoLive, localLTags)) | |||
@@ -62,7 +62,10 @@ class MuxingSession extends EventEmitter { | |||
private readonly user: MUserId | |||
private readonly sessionId: string | |||
private readonly videoLive: MVideoLiveVideo | |||
private readonly inputUrl: string | |||
private readonly inputLocalUrl: string | |||
private readonly inputPublicUrl: string | |||
private readonly fps: number | |||
private readonly allResolutions: number[] | |||
@@ -107,7 +110,10 @@ class MuxingSession extends EventEmitter { | |||
user: MUserId | |||
sessionId: string | |||
videoLive: MVideoLiveVideo | |||
inputUrl: string | |||
inputLocalUrl: string | |||
inputPublicUrl: string | |||
fps: number | |||
bitrate: number | |||
ratio: number | |||
@@ -120,7 +126,10 @@ class MuxingSession extends EventEmitter { | |||
this.user = options.user | |||
this.sessionId = options.sessionId | |||
this.videoLive = options.videoLive | |||
this.inputUrl = options.inputUrl | |||
this.inputLocalUrl = options.inputLocalUrl | |||
this.inputPublicUrl = options.inputPublicUrl | |||
this.fps = options.fps | |||
this.bitrate = options.bitrate | |||
@@ -375,7 +384,7 @@ class MuxingSession extends EventEmitter { | |||
private onTranscodedEnded () { | |||
this.emit('transcoding-end', ({ videoUUID: this.videoUUID })) | |||
logger.info('RTMP transmuxing for video %s ended. Scheduling cleanup', this.inputUrl, this.lTags()) | |||
logger.info('RTMP transmuxing for video %s ended. Scheduling cleanup', this.inputLocalUrl, this.lTags()) | |||
setTimeout(() => { | |||
// Wait latest segments generation, and close watchers | |||
@@ -468,7 +477,8 @@ class MuxingSession extends EventEmitter { | |||
lTags: this.lTags, | |||
inputUrl: this.inputUrl, | |||
inputLocalUrl: this.inputLocalUrl, | |||
inputPublicUrl: this.inputPublicUrl, | |||
toTranscode: this.allResolutions.map(resolution => ({ | |||
resolution, | |||
@@ -25,7 +25,9 @@ interface AbstractTranscodingWrapperOptions { | |||
lTags: LoggerTagsFn | |||
inputUrl: string | |||
inputLocalUrl: string | |||
inputPublicUrl: string | |||
fps: number | |||
toTranscode: { | |||
resolution: number | |||
@@ -50,7 +52,9 @@ abstract class AbstractTranscodingWrapper extends EventEmitter { | |||
fps: number | |||
}[] | |||
protected readonly inputUrl: string | |||
protected readonly inputLocalUrl: string | |||
protected readonly inputPublicUrl: string | |||
protected readonly fps: number | |||
protected readonly bitrate: number | |||
protected readonly ratio: number | |||
@@ -76,7 +80,9 @@ abstract class AbstractTranscodingWrapper extends EventEmitter { | |||
this.videoUUID = options.videoLive.Video.uuid | |||
this.streamingPlaylist = options.streamingPlaylist | |||
this.inputUrl = options.inputUrl | |||
this.inputLocalUrl = options.inputLocalUrl | |||
this.inputPublicUrl = options.inputPublicUrl | |||
this.fps = options.fps | |||
this.toTranscode = options.toTranscode | |||
@@ -15,7 +15,7 @@ export class FFmpegTranscodingWrapper extends AbstractTranscodingWrapper { | |||
async run () { | |||
this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED | |||
? await this.buildFFmpegLive().getLiveTranscodingCommand({ | |||
inputUrl: this.inputUrl, | |||
inputUrl: this.inputLocalUrl, | |||
outPath: this.outDirectory, | |||
masterPlaylistName: this.streamingPlaylist.playlistFilename, | |||
@@ -31,7 +31,7 @@ export class FFmpegTranscodingWrapper extends AbstractTranscodingWrapper { | |||
hasAudio: this.hasAudio | |||
}) | |||
: this.buildFFmpegLive().getLiveMuxingCommand({ | |||
inputUrl: this.inputUrl, | |||
inputUrl: this.inputLocalUrl, | |||
outPath: this.outDirectory, | |||
masterPlaylistName: this.streamingPlaylist.playlistFilename, | |||
@@ -4,7 +4,7 @@ import { AbstractTranscodingWrapper } from './abstract-transcoding-wrapper' | |||
export class RemoteTranscodingWrapper extends AbstractTranscodingWrapper { | |||
async run () { | |||
await new LiveRTMPHLSTranscodingJobHandler().create({ | |||
rtmpUrl: this.inputUrl, | |||
rtmpUrl: this.inputPublicUrl, | |||
toTranscode: this.toTranscode, | |||
video: this.videoLive.Video, | |||
outputDirectory: this.outDirectory, | |||
@@ -159,11 +159,11 @@ export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel> | |||
streamKey: this.streamKey, | |||
rtmpUrl: CONFIG.LIVE.RTMP.ENABLED | |||
? WEBSERVER.RTMP_URL | |||
? WEBSERVER.RTMP_BASE_LIVE_URL | |||
: null, | |||
rtmpsUrl: CONFIG.LIVE.RTMPS.ENABLED | |||
? WEBSERVER.RTMPS_URL | |||
? WEBSERVER.RTMPS_BASE_LIVE_URL | |||
: null | |||
} | |||
} | |||