// 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); } })();