diff --git a/websocket-relay.js b/websocket-relay.js index 1cb47f15..c2a1e548 100755 --- a/websocket-relay.js +++ b/websocket-relay.js @@ -1,89 +1,145 @@ +"use strict"; // Use the websocket-relay to serve a raw MPEG-TS over WebSockets. You can use // ffmpeg to feed the relay. ffmpeg -> websocket-relay -> browser // Example: -// node websocket-relay yoursecret 8081 8082 +// node websocket-relay // ffmpeg -i -f mpegts http://localhost:8081/yoursecret -var fs = require('fs'), - http = require('http'), - WebSocket = require('ws'); - -if (process.argv.length < 3) { - console.log( - 'Usage: \n' + - 'node websocket-relay.js [ ]' - ); - process.exit(); -} +//Configs +var cfg = { + ssl: true, + host: 'localhost', + input_port: 8081, + output_port: 8082, + ssl_key: 'privkey.key', + ssl_cert: 'pubcert.crt', + stream_secret: 'yoursecret', + record_stream: false, + stream_path: 'recordings/' +}; +process.title = 'Stream Server'; -var STREAM_SECRET = process.argv[2], - STREAM_PORT = process.argv[3] || 8081, - WEBSOCKET_PORT = process.argv[4] || 8082, - RECORD_STREAM = false; +// Node Configs (Don't touch) +var fs = require('fs'); +var http = require('http'); +var https = require('https'); +var websocket = require('ws'); +var ws_input = false; +var ws_output = false; -// Websocket Server -var socketServer = new WebSocket.Server({port: WEBSOCKET_PORT, perMessageDeflate: false}); -socketServer.connectionCount = 0; -socketServer.on('connection', function(socket, upgradeReq) { - socketServer.connectionCount++; - console.log( - 'New WebSocket Connection: ', - (upgradeReq || socket.upgradeReq).socket.remoteAddress, - (upgradeReq || socket.upgradeReq).headers['user-agent'], - '('+socketServer.connectionCount+' total)' - ); - socket.on('close', function(code, message){ - socketServer.connectionCount--; - console.log( - 'Disconnected WebSocket ('+socketServer.connectionCount+' total)' - ); - }); -}); -socketServer.broadcast = function(data) { - socketServer.clients.forEach(function each(client) { - if (client.readyState === WebSocket.OPEN) { - client.send(data); - } +// Input +var handleInputConnection = function(){ + var app = http.createServer(onRequestInput); + app.headersTimeout = 0; + app.listen(cfg.input_port, cfg.host); + + ws_input = new websocket.Server({server:app, perMessageDeflate:false}); + ws_input.on('connection', function(client){ + client.ip = client._socket.remoteAddress; + + client.on('close', function(reasonCode, description){ + console.log('['+client.ip+'] closed'); + }); + + console.log('Stream ['+client.ip+'] connected'); }); }; - -// HTTP Server to accept incomming MPEG-TS Stream from ffmpeg -var streamServer = http.createServer( function(request, response) { +var onRequestInput = function(request, response){ var params = request.url.substr(1).split('/'); - if (params[0] !== STREAM_SECRET) { - console.log( - 'Failed Stream Connection: '+ request.socket.remoteAddress + ':' + - request.socket.remotePort + ' - wrong secret.' - ); - response.end(); + if (params[0] !== cfg.stream_secret) { + console.log('Failed stream connection: '+request.socket.remoteAddress+':'+request.socket.remotePort+' - wrong secret'); + return response.end(); } response.connection.setTimeout(0); - console.log( - 'Stream Connected: ' + - request.socket.remoteAddress + ':' + - request.socket.remotePort - ); + request.on('data', function(data){ - socketServer.broadcast(data); - if (request.socket.recording) { + ws_output.broadcast(data); + if(request.socket.recording){ request.socket.recording.write(data); } }); request.on('end',function(){ - console.log('close'); + console.log('Stream end'); if (request.socket.recording) { request.socket.recording.close(); } }); - // Record the stream to a local file? - if (RECORD_STREAM) { - var path = 'recordings/' + Date.now() + '.ts'; + // Record the stream to a local file + if (cfg.record_stream) { + var path = stream_path + Date.now() + '.ts'; request.socket.recording = fs.createWriteStream(path); } -}).listen(STREAM_PORT); + + console.log('Stream connected'); +}; +handleInputConnection(); + +// Output +var handleOutputConnection = function(){ + var app = false; + if(cfg.ssl){ + app = https.createServer({ + key: fs.readFileSync(cfg.ssl_key), + cert: fs.readFileSync(cfg.ssl_cert) + }, onRequestOutput); + }else{ + app = http.createServer(onRequestOutput); + } + app.headersTimeout = 0; + app.listen(cfg.output_port, cfg.host); + + ws_output = new websocket.Server({server:app, perMessageDeflate:false}); + ws_output.on('connection', function(client){ + client.ip = client._socket.remoteAddress; + client.isAlive = true; //ping check + + client.on('close', function(reasonCode, description){ + console.log('['+client.ip+'] closed.'); + process.title = "("+ws_output.clients.size+") Stream Server"; + }); + + client.on('error', function(e){ + console.log('Error, client disconnected: '+e); + process.title = "("+ws_output.clients.size+") Stream Server"; + }); + + client.on('pong', function(e){ + client.isAlive = true; + }); + + console.log('Client ['+client.ip+'] connected'); + process.title = "("+ws_output.clients.size+") Stream Server"; + }); + + ws_output.broadcast = function(data) { + ws_output.clients.forEach(function each(client){ + if(client.readyState === websocket.OPEN){ + client.send(data); + } + }); + }; +}; +handleOutputConnection(); +var onRequestOutput = function(request,response){ + console.log('Client requesting connection'); + response.end(); +}; + +var nop = function(){}; +var pingInterval = setInterval(function ping(){ + ws_output.clients.forEach(function each(client){ + if(client.isAlive){ + client.isAlive = false; + client.ping(nop); + }else{ + console.log('Client ['+client.ip+'] close, no ping response'); + client.terminate(); + } + }); +}, 15000); -console.log('Listening for incomming MPEG-TS Stream on http://127.0.0.1:'+STREAM_PORT+'/'); -console.log('Awaiting WebSocket connections on ws://127.0.0.1:'+WEBSOCKET_PORT+'/'); +console.log('Listening for incomming MPEG-TS Stream'); +console.log('Awaiting WebSocket connections');