// dtp-media-engine.js
// Copyright (C) 2022 DTP Technologies, LLC
// All Rights Reserved
'use strict' ;
require ( 'dotenv' ) . config ( ) ;
const path = require ( 'path' ) ;
const mongoose = require ( 'mongoose' ) ;
const mediasoup = require ( 'mediasoup' ) ;
const { SiteAsync , SiteCommon , SitePlatform , SiteLog } = require ( path . join ( __ dirname , 'lib' , 'site-lib' ) ) ;
module . rootPath = __ dirname ;
module . pkg = require ( path . join ( module . rootPath , 'package.json' ) ) ;
module . config = {
component : { logId : 'dtp-media-engine' , index : 'dtpMediaEngine' , className : 'DtpMediaEngine' } ,
root : module . rootPath ,
site : require ( path . join ( module . rootPath , 'config' , 'site' ) ) ,
webRtcServer : [
{
protocol : 'udp' ,
ip : process . env . MEDIASOUP_WEBRTC_BIND_ADDR || '127.0.0.1' ,
port : process . env . MEDIASOUP_WEBRTC_BIND_PORT || 20000 ,
}
]
} ;
module . log = new SiteLog ( module , module . config . component ) ;
class MediaEngineWorker extends SiteCommon {
constructor ( ) {
super ( module , { logId : 'dtp-media-worker' , index : 'dtpMediaWorker' , className : 'MediaEngineWorker' } ) ;
this . _ id = mongoose . Types . ObjectId ( ) ;
}
async start ( ) {
await super . start ( ) ;
try {
this . worker = await mediasoup . createWorker ( {
logLevel : 'warn' ,
dtlsCertificateFile : process . env . HTTPS_SSL_CRT ,
dtlsPrivateKeyFile : process . env . HTTPS_SSL_KEY ,
} ) ;
} catch ( error ) {
throw new Error ( ` failed to start mediasoup worker process: ${ error . message } ` ) ;
}
try {
const BIND_PORT = 20000 + module . nextWorkerIdx ++ ;
this . webRtcServer = await this . worker . createWebRtcServer ( {
listenInfos : [
{
protocol : 'udp' ,
ip : '127.0.0.1' ,
port : BIND_PORT ,
} ,
{
protocol : 'tcp' ,
ip : '127.0.0.1' ,
port : BIND_PORT ,
} ,
] ,
} ) ;
} catch ( error ) {
throw new Error ( ` failed to start mediasoup WebRTC Server: ${ error . message } ` ) ;
}
}
async stop ( ) {
if ( this . webRtcServer && ! this . webRtcServer . closed ) {
this . log . info ( 'closing mediasoup WebRTC server' ) ;
this . webRtcServer . close ( ) ;
delete this . webRtcServer ;
}
if ( this . worker && ! this . worker . closed ) {
this . log . info ( 'closing mediasoup worker process' ) ;
this . worker . close ( ) ;
delete this . worker ;
}
await super . stop ( ) ;
}
}
module . onNewWorker = async ( worker ) => {
module . log . info ( 'new worker created' , { worker : worker . pid } ) ;
worker . observer . on ( 'close' , ( ) => {
module . log . info ( 'worker shutting down' , { worker : worker . pid } ) ;
} ) ;
worker . observer . on ( 'newrouter' , ( router ) => {
module . log . info ( 'new router created' , { worker : worker . pid , router : router . id } ) ;
router . observer . on ( 'close' , ( ) => {
module . log . info ( 'router shutting down' , { worker : worker . pid , router : router . id } ) ;
} ) ;
} ) ;
} ;
module . createWorker = async ( ) => {
const worker = new MediaEngineWorker ( ) ;
module . workers . push ( worker ) ;
await worker . start ( ) ;
} ;
module . shutdown = async ( ) => {
await SiteAsync . each ( module . workers , async ( worker ) => {
try {
await worker . stop ( ) ;
} catch ( error ) {
module . log . error ( 'failed to stop worker' , { error } ) ;
}
} ) ;
} ;
/ *
* SERVER PROCESS INIT
* /
( async ( ) => {
process . on ( 'unhandledRejection' , ( error , p ) => {
module . log . error ( 'Unhandled rejection' , {
error : error ,
promise : p ,
stack : error . stack
} ) ;
} ) ;
process . on ( 'warning' , ( error ) => {
module . log . alert ( 'warning' , { error } ) ;
} ) ;
process . once ( 'SIGINT' , async ( ) => {
module . log . info ( 'SIGINT received' ) ;
module . log . info ( 'requesting shutdown...' ) ;
await module . shutdown ( ) ;
const exitCode = await SitePlatform . shutdown ( ) ;
process . nextTick ( ( ) => {
process . exit ( exitCode ) ;
} ) ;
} ) ;
process . once ( 'SIGUSR2' , async ( ) => {
await SitePlatform . shutdown ( ) ;
process . kill ( process . pid , 'SIGUSR2' ) ;
} ) ;
try {
await SitePlatform . start ( module ) ;
} catch ( error ) {
module . log . error ( ` failed to start DTP ${ module . config . component . className } process ` , { error } ) ;
return ;
}
try {
module . log . info ( 'registering mediasoup observer callbacks' ) ;
mediasoup . observer . on ( 'newworker' , module . onNewWorker ) ;
module . log . info ( 'creating mediasoup worker instance' ) ;
module . nextWorkerIdx = 0 ;
module . workers = [ ] ;
await module . createWorker ( ) ;
module . log . info ( 'DTP Media Engine online' ) ;
} catch ( error ) {
module . log . error ( 'failed to start DTP Media Engine' , { error } ) ;
process . exit ( - 1 ) ;
}
} ) ( ) ;