├── static ├── index_video.jade ├── layout.jade ├── index.html └── smoothie.js ├── README.md ├── webmgst.js ├── imagem.js ├── form.js ├── node-flv-streamer.js ├── node-streamer.js ├── node-augmented-reality-streamer.js ├── node-transcoder-ogg-mp3.js └── node-transcoder-ogg-theora.js /static/index_video.jade: -------------------------------------------------------------------------------- 1 | h1= LINK 2 | #container 3 | video(src= LINK, controls, autobuffer) 4 | -------------------------------------------------------------------------------- /static/layout.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html(lang="pt") 3 | head 4 | title='NodeStreamer - live transcoding with nodejs' 5 | script(type='text/javascript') 6 | if (foo) { 7 | bar() 8 | } 9 | body 10 | #wrapper 11 | != body 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nodestreamer 2 | ============ 3 | 4 | Nodestreamer is a set of experiments with multimedia live streaming using nodejs and gstreamer pipelines. All the streaming "magic" is done by the gstreamer element "tcpserversink" that can handle multiple socket connections gracefully. 5 | 6 | 7 | VIDEO STREAMING 8 | --------------- 9 | 10 | Simple experiment with ogg theora streaming. 11 | 12 | Command line: "node node-streamer.js" 13 | Live video url: http://localhost:8003/stream 14 | Monitoring webpage (need a borwser that supports websockets): http://localhost:8003/ 15 | 16 | 17 | 18 | TRANSCODING AND STREAMING 19 | --------------- 20 | 21 | Live transcoding of audio streams from RTMP, OGG, MP3, WMV to Ogg+Vorbis or MP3. 22 | 23 | Command line: "node node-transcoder-ogg-mp3.js URL STREAM_FILE_OUT[.mp3 or .ogg] PORT" 24 | Example: "node node-transcoder-ogg-mp3.js mms://karazhan.senado.gov.br/wmtencoder/radio.wmv senado.ogg 9100" 25 | The output url is: http://localhost:9100/senado.ogg 26 | 27 | Live transcoding of video streams from RTMP, OGG, WebM, WMV to Ogg+Theora or WebM. 28 | 29 | Command line: "node node-transcoder-ogg-thera.js URL STREAM_FILE_OUT[.webm or .ogg] PORT" 30 | Example: "node node-transcoder-ogg-theora.js mms://midia.al.rs.gov.br/tval assembeliars.ogg 1234" 31 | The output url is: http://localhost:1234/assembeliars.ogg 32 | 33 | 34 | TODO 35 | ---- 36 | 37 | - Improve fault tolerance. 38 | - Support output RTMP streaming. 39 | - When avaiable, use a javascript native gstreamer binding. 40 | 41 | DONE: Implement live transcoding from mp3 to ogg and vice versa. 42 | DONE: Live video transcoding. 43 | 44 | -------------------------------------------------------------------------------- /webmgst.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var net = require('net'); 3 | var child = require('child_process'); 4 | 5 | var cmd = 'gst-launch'; 6 | var args = ''; 7 | var options = null; 8 | // {env: {LD_LIBRARY_PATH: '/usr/local/lib', 9 | // PATH: '/usr/local/bin:/usr/bin:/bin'}}; 10 | 11 | var app = express.createServer(); 12 | 13 | app.get('/', function(req, res) { 14 | var date = new Date(); 15 | 16 | res.writeHead(200, { 17 | 'Date':date.toUTCString(), 18 | 'Connection':'close', 19 | 'Cache-Control':'private', 20 | 'Content-Type':'video/webm', 21 | 'Server':'CustomStreamer/0.0.1', 22 | }); 23 | 24 | var server = net.createServer(function (socket) { 25 | socket.on('data', function (data) { 26 | res.write(data); 27 | }); 28 | socket.on('close', function(had_error) { 29 | res.end(); 30 | }); 31 | }); 32 | 33 | server.listen(function() { 34 | args = 35 | [//'ximagesrc', 36 | //'v4l2src', 37 | 'videotestsrc', 'horizontal-speed=3', 'is-live=1', 38 | '!', 'video/x-raw-rgb,framerate=3/1', 39 | '!', 'ffmpegcolorspace', 40 | '!', 'vp8enc', 'speed=2', 41 | '!', 'queue2', 42 | '!', 'm.', 'audiotestsrc', 43 | '!', 'audioconvert', 44 | '!', 'vorbisenc', 45 | '!', 'queue2', 46 | '!', 'm.', 'webmmux', 'name=m', 'streamable=true', 47 | '!', 'tcpclientsink', 'host=localhost', 48 | 'port='+server.address().port]; 49 | console.log(args.toString()); 50 | 51 | var gstMuxer = child.spawn(cmd, args, options); 52 | gstMuxer.stderr.on('data', onSpawnError); 53 | gstMuxer.on('exit', onSpawnExit); 54 | 55 | res.connection.on('close', function() { 56 | gstMuxer.kill(); 57 | }); 58 | }); 59 | }); 60 | 61 | app.listen(9001); 62 | 63 | function onSpawnError(data) { 64 | console.log(data.toString()); 65 | } 66 | 67 | function onSpawnExit(code) { 68 | if (code != null) { 69 | console.error('GStreamer error, exit code ' + code); 70 | } 71 | } 72 | 73 | process.on('uncaughtException', function(err) { 74 | console.debug(err); 75 | }); 76 | -------------------------------------------------------------------------------- /imagem.js: -------------------------------------------------------------------------------- 1 | var URL = require('url'), 2 | sURL = 'http://softwarelivre.org/articles/0021/5721/wikipedia-Gnulinux.png?1276286225', 3 | oURL = URL.parse(sURL), 4 | http = require('http'), 5 | client = http.createClient(23456, oURL.hostname), 6 | request = client.request('GET', oURL.pathname, {'host': oURL.hostname}) 7 | ; 8 | //var Png = require('png'); 9 | 10 | http.createServer(function (req, res) { 11 | var url = req.url.substr(1); 12 | var oURL = URL.parse(url); 13 | var options = { 14 | host: oURL.hostname, 15 | port: 80, 16 | path: oURL.pathname 17 | }; 18 | //console.log(options); 19 | 20 | http.get(options, function(gres) { 21 | res.writeHead(200, {'Content-Type': 'text/html'}); 22 | var data = ''; 23 | 24 | console.log(options); 25 | console.log("Got response: " + gres.statusCode); 26 | 27 | gres.on('data', function (chunk) { 28 | //console.log(chunk); 29 | data += chunk; 30 | }); 31 | gres.on('end', function (chunk) { 32 | //if(chunk) 33 | // data += chunk; 34 | var prefix = "data:" + gres.headers["content-type"] + ";base64,"; 35 | //res.write("base64..."); 36 | var base64 = new Buffer(data, 'binary').toString('base64'); 37 | var img = prefix + base64; 38 | //console.log(data); 39 | //res.write(img); 40 | res.write("\n"); 41 | res.write("\n"); //http://upload.wikimedia.org/wikipedia/commons/3/31/Red-dot-5px.png 42 | res.end(); 43 | }); 44 | gres.on('error', function(e) { 45 | console.log("Got error on remote image loading: " + e.message); 46 | }); 47 | 48 | //console.log(gres); 49 | }).on('error', function(e) { 50 | console.log("Got error: " + e.message); 51 | }); 52 | }).listen(2345); 53 | 54 | function image(width, height){ 55 | var p = new Png(height, width, 50); 56 | var background = p.color(1, 1, 1, 1); 57 | return 'data:image/png;base64,'+p.getBase64(); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /form.js: -------------------------------------------------------------------------------- 1 | var http = require('http'), 2 | sys = require('sys'), 3 | querystring = require('querystring'); 4 | 5 | http.createServer(function(request, response) 6 | { 7 | sys.puts('Request for ' + request.url); 8 | 9 | switch (request.url) 10 | { 11 | case '/': 12 | response.writeHead(200, { 'Content-Type' : 'text/html' }); 13 | response.write( 14 | '
' + 15 | 'Nome:
' + 16 | 'Email:
' + 17 | 'Telefone:
' + 18 | '' + 19 | '
' 20 | ); 21 | response.end(); 22 | break; 23 | case '/cadastrar': 24 | response.writeHead(200, { 'Content-Type' : 'text/html' }); 25 | post_handler(request, function(request_data){ 26 | response.write( 27 | 'JSON object:
' + 28 | '
' + sys.inspect(request_data) + '
' + 29 | '
' + 30 | 'Dados:
' + 31 | 'Nome: ' + request_data.nome + '
' + 32 | 'Email: ' + request_data.email + '
'+ 33 | 'Telefone: ' + request_data.telefone + '
' 34 | ); 35 | response.end(); 36 | }); 37 | break; 38 | default: 39 | response.writeHead(400, { 'Content-Type' : 'text/html' }); 40 | response.write( 41 | 'Pagina nao encontrada!' 42 | ); 43 | response.end(); 44 | }; 45 | }).listen(8001); 46 | console.log("Servidor rodando na porta 8001"); 47 | 48 | function post_handler(request, callback) 49 | { 50 | var _REQUEST = { }; 51 | var _CONTENT = ''; 52 | 53 | if (request.method == 'POST') 54 | { 55 | request.addListener('data', function(chunk) 56 | { 57 | _CONTENT+= chunk; 58 | }); 59 | 60 | request.addListener('end', function() 61 | { 62 | _REQUEST = querystring.parse(_CONTENT); 63 | callback(_REQUEST); 64 | }); 65 | }; 66 | }; 67 | -------------------------------------------------------------------------------- /node-flv-streamer.js: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | var net = require("net"); 17 | var express = require("express"); 18 | var http = require("http"); 19 | var child = require('child_process'); 20 | 21 | var cmd = '/usr/bin/gst-launch-0.10'; 22 | var options = null; 23 | var gstMuxer = null; 24 | 25 | http.ServerResponse.prototype.getHashCode = (function() { 26 | var id = 0; 27 | return function() { 28 | if (!this["hashCode"]) { 29 | this["hashCode"] = ""; 30 | } 31 | return this["hashCode"]; 32 | } 33 | })(); 34 | 35 | spawGstreamer(9002); 36 | 37 | var total_data = 0; 38 | var clients = 0; 39 | var app = express.createServer(); 40 | var io = require('socket.io').listen(app); 41 | app.get('/stream', function(req, res) { 42 | console.log("new http client"); 43 | console.log("server writing header"); 44 | req.shouldKeepAlive = false; 45 | var date = new Date(); 46 | res.writeHead(200, { 47 | 'Date':date.toUTCString(), 48 | 'Connection':'close', 49 | 'Cache-Control':'private', 50 | 'Content-Type':'video/x-flv', 51 | 'Server':'NodeStreamer/0.0.1', 52 | }); 53 | 54 | console.log("trying to connect a socket to gstreamer tcp server."); 55 | var port = 9002; 56 | var socket = new net.createConnection(port); 57 | 58 | io.on('connection', function (s) { 59 | console.log('socket.io client connected'); 60 | setInterval(function() { 61 | s.volatile.emit({ "clients" : clients , "total" : total_data }); 62 | }, 1000); 63 | }); 64 | socket.on('connect', function() { 65 | clients++; 66 | console.log("new client socket connected to gstreamer tcp server "+port); 67 | }); 68 | socket.on('data', function (data) { 69 | total_data += data.length; 70 | res.write(data); 71 | }); 72 | socket.on('close', function(had_error) { 73 | if(socket && socket.end){ 74 | socket.end(); 75 | socket.destroy(); 76 | } 77 | }); 78 | socket.on('end', function () { 79 | socket.end(); 80 | }); 81 | res.connection.on('close', function() { 82 | console.log("http connecton closed"); 83 | if(socket && socket.end) { 84 | socket.end(); 85 | socket.destroy(); 86 | } 87 | clients--; 88 | }); 89 | 90 | }); 91 | app.configure(function () { 92 | app.use(express.logger()); 93 | app.use(express.errorHandler({ 94 | dumpExceptions: true, 95 | showStack: true 96 | })); 97 | app.use(express.static(__dirname+'/static')); 98 | }); 99 | app.listen(9000); 100 | 101 | console.log("HTTP server running"); 102 | 103 | function spawGstreamer(port) { 104 | args = 105 | ['--gst-debug-level=3', 106 | // 'ximagesrc', 107 | 'videotestsrc', 'is-live=1', //'horizontal-speed=2', 108 | // 'uridecodebin', "uri=htpp://", 109 | // 'filesrc', 'location=', '!', 'decodebin name=dec', 110 | '!', 'videorate', 111 | '!', 'video/x-raw-rgb,framerate=12/1', 112 | '!', 'ffmpegcolorspace', 113 | '!', 'timeoverlay', 114 | '!', 'ffmpegcolorspace', 115 | '!', 'queue', 116 | '!', 'ffenc_flv','bitrate=128', 117 | '!', 'queue', 118 | '!', 'flvmux', 'streamable=true', //'is-live=true', 119 | '!', 'queue', 120 | '!', 'tcpserversink', 'buffers-max=500', 'buffers-soft-max=450', 'recover-policy=1', 'protocol=none', 'blocksize='+(4096 * 5), 'sync=false', 'sync-method=1', 'port='+port]; 121 | 122 | gstMuxer = child.spawn(cmd, args, options); 123 | gstMuxer.stderr.on('data', onSpawnError); 124 | gstMuxer.on('exit', onSpawnExit); 125 | console.log(args.toString()); 126 | } 127 | 128 | function onSpawnError(data) { 129 | console.log(data.toString()); 130 | } 131 | 132 | function onSpawnExit(code) { 133 | if (code != null) { 134 | console.error('GStreamer error, exit code ' + code); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /node-streamer.js: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | var net = require("net"); 17 | var express = require("express"); 18 | var http = require("http"); 19 | var child = require('child_process'); 20 | 21 | var cmd = '/usr/bin/gst-launch-0.10'; 22 | var options = null; 23 | var gstMuxer = null; 24 | 25 | http.ServerResponse.prototype.getHashCode = (function() { 26 | var id = 0; 27 | return function() { 28 | if (!this["hashCode"]) { 29 | this["hashCode"] = ""; 30 | } 31 | return this["hashCode"]; 32 | } 33 | })(); 34 | 35 | spawGstreamer(11111); 36 | 37 | var total_data = 0; 38 | var clients = 0; 39 | var app = express.createServer(); 40 | var io = require('socket.io').listen(app); 41 | app.get('/stream', function(req, res) { 42 | req.shouldKeepAlive = false; 43 | var date = new Date(); 44 | res.writeHead(200, { 45 | 'Date':date.toUTCString(), 46 | 'Connection':'close', 47 | 'Cache-Control':'private', 48 | 'Content-Type':'video/ogg', 49 | 'Server':'NodeStreamer/0.0.1', 50 | }); 51 | console.log("new http client"); 52 | console.log("server writing header"); 53 | 54 | console.log("trying to connect a socket to gstreamer tcp server."); 55 | var port = 11111; 56 | var socket = new net.createConnection(port); 57 | 58 | io.on('connection', function (s) { 59 | console.log('socket.io client connected'); 60 | setInterval(function() { 61 | s.volatile.emit({ "clients" : clients , "total" : total_data }); 62 | }, 1000); 63 | }); 64 | socket.on('connect', function() { 65 | clients++; 66 | console.log("new client socket connected to gstreamer tcp server "+port); 67 | }); 68 | socket.on('data', function (data) { 69 | total_data += data.length; 70 | /*i++; 71 | if(i % 500 == 0) { 72 | console.log("socket ["+res.getHashCode()+"] total received data from gstreamer: "+ (total_data/(1024*1024))+" MB"); 73 | }*/ 74 | res.write(data); 75 | }); 76 | socket.on('close', function(had_error) { 77 | if(socket && socket.end){ 78 | socket.end(); 79 | socket.destroy(); 80 | } 81 | }); 82 | socket.on('end', function () { 83 | socket.end(); 84 | }); 85 | res.connection.on('close', function() { 86 | console.log("http connecton closed"); 87 | if(socket && socket.end) { 88 | socket.end(); 89 | socket.destroy(); 90 | } 91 | clients--; 92 | }); 93 | 94 | }); 95 | app.configure(function () { 96 | app.use(express.logger()); 97 | app.use(express.errorHandler({ 98 | dumpExceptions: true, 99 | showStack: true 100 | })); 101 | app.use(express.static(__dirname+'/static')); 102 | }); 103 | app.listen(8003); 104 | 105 | console.log("HTTP server running"); 106 | 107 | function spawGstreamer(port) { 108 | args = 109 | ['--gst-debug-level=3', 110 | // 'ximagesrc', 111 | 'videotestsrc', 'is-live=1', //'horizontal-speed=2', 112 | // 'uridecodebin', "uri=htpp://", 113 | // 'filesrc', 'location=', '!', 'decodebin name=dec', 114 | '!', 'videorate', 115 | '!', 'video/x-raw-rgb,framerate=12/1', 116 | '!', 'ffmpegcolorspace', 117 | '!', 'timeoverlay', 118 | '!', 'ffmpegcolorspace', 119 | // '!', 'videorate', 120 | '!', 'queue', 121 | '!', 'theoraenc','quality=10','speed-level=2', 122 | '!', 'queue', 123 | '!', 'oggmux', 'name=m', 124 | '!', 'queue', 125 | '!', 'tcpserversink', 'buffers-max=500', 'buffers-soft-max=450', 'recover-policy=1', 'protocol=none', 'blocksize='+(4096 * 2), 'sync=false', 'sync-method=1', 'port='+port]; 126 | 127 | gstMuxer = child.spawn(cmd, args, options); 128 | gstMuxer.stderr.on('data', onSpawnError); 129 | gstMuxer.on('exit', onSpawnExit); 130 | console.log(args.toString()); 131 | } 132 | 133 | function onSpawnError(data) { 134 | console.log(data.toString()); 135 | } 136 | 137 | function onSpawnExit(code) { 138 | if (code != null) { 139 | console.error('GStreamer error, exit code ' + code); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /node-augmented-reality-streamer.js: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | var net = require("net"); 17 | var express = require("express"); 18 | var http = require("http"); 19 | var child = require('child_process'); 20 | 21 | var cmd = '/usr/bin/gst-launch-0.10'; 22 | var options = null; 23 | var gstMuxer = null; 24 | 25 | var GSTREAMER_PORT_IN = 7001; 26 | var GSTREAMER_PORT_OUT = 7002; 27 | 28 | var DO_BURST_CONNECTION = false; 29 | 30 | http.ServerResponse.prototype.getHashCode = (function() { 31 | var id = 0; 32 | return function() { 33 | if (!this["hashCode"]) { 34 | this["hashCode"] = ""; 35 | } 36 | return this["hashCode"]; 37 | } 38 | })(); 39 | 40 | try { 41 | spawGstreamerServer(GSTREAMER_PORT_IN, GSTREAMER_PORT_OUT); 42 | } catch(err) { 43 | console.log("Error starting the gstreamer pipeline."); 44 | } 45 | 46 | var total_data = 0; 47 | var clients = 0; 48 | var app = express.createServer(); 49 | var io = require('socket.io').listen(app); 50 | app.get('/video.ogg', function(req, res) { 51 | req.shouldKeepAlive = false; 52 | var date = new Date(); 53 | res.writeHead(200, { 54 | 'Date':date.toUTCString(), 55 | 'Connection':'close', 56 | 'Cache-Control':'no-cache', 57 | 'Content-Type':'video/ogg', 58 | 'Server':'NodeTranscoder/0.0.1', 59 | }); 60 | console.log("new http client"); 61 | console.log("server writing header"); 62 | 63 | console.log("trying to connect a socket to gstreamer tcp server."); 64 | var socket = new net.createConnection(GSTREAMER_PORT_OUT); 65 | 66 | io.on('connection', function (s) { 67 | console.log('socket.io client connected'); 68 | setInterval(function() { 69 | s.volatile.emit({ "clients" : clients , "total" : total_data }); 70 | }, 1000); 71 | }); 72 | socket.on('connect', function() { 73 | clients++; 74 | console.log("new client socket connected to gstreamer tcp server "+GSTREAMER_PORT_OUT); 75 | }); 76 | 77 | console.log("now sending the live buffers direct from gstreamer"); 78 | socket.on('data', function (data) { 79 | if(res.writable) { 80 | total_data += data.length; 81 | res.write(data); 82 | } 83 | //console.log("sending "+data.length) 84 | }); 85 | socket.on('close', function(had_error) { 86 | if(socket && socket.end){ 87 | socket.end(); 88 | socket.destroy(); 89 | } 90 | }); 91 | socket.on('end', function () { 92 | socket.end(); 93 | }); 94 | res.connection.on('close', function() { 95 | console.log("http connecton closed"); 96 | if(socket && socket.end) { 97 | socket.end(); 98 | socket.destroy(); 99 | } 100 | clients--; 101 | }); 102 | 103 | }); 104 | app.configure(function () { 105 | app.use(express.logger()); 106 | app.use(express.errorHandler({ 107 | dumpExceptions: true, 108 | showStack: true 109 | })); 110 | app.use(express.static(__dirname+'/static')); 111 | }); 112 | app.listen(7000); 113 | 114 | console.log("HTTP server running"); 115 | 116 | function spawGstreamerServer(portIn, portOut) { 117 | args = 118 | ['--gst-debug-level=2', 119 | 'tcpserversrc', 'host=gonod.softwarelivre.org', "port="+portIn, 120 | '!', 'queue', 121 | '!', 'gdpdepay', 122 | '!', 'decodebin2', 123 | '!', 'queue', 124 | '!', 'videorate', 125 | '!', 'videoscale', 126 | '!', 'video/x-raw-yuv, framerate=2/1, width=640, height=480', 127 | '!', 'ffmpegcolorspace', 128 | '!', 'queue', 129 | '!', 'theoraenc', 'bitrate=196', 130 | '!', 'queue', 131 | '!', 'oggmux', 'name=m', 132 | '!', 'queue', 133 | '!', 'progressreport', 134 | '!', 'tcpserversink', 'buffers-max=500', 'buffers-soft-max=450', /*'burst-unit=3',*/ 'recover-policy=1', 'protocol=none', 'blocksize='+(4096 * 1), 'sync=false', 'sync-method=2', 'port='+portOut]; 135 | //http://www.flumotion.net/doc/flumotion/reference/trunk/flumotion.component.consumers.httpstreamer.httpstreamer-pysrc.html 136 | gstMuxer = child.spawn(cmd, args, options); 137 | gstMuxer.stderr.on('data', onSpawnError); 138 | gstMuxer.on('exit', onSpawnExit); 139 | console.log(args.toString()); 140 | } 141 | 142 | function onSpawnError(data) { 143 | console.log(data.toString()); 144 | } 145 | 146 | function onSpawnExit(code) { 147 | if (code == 0) { 148 | console.error('GStreamer error, exit code ' + code); 149 | /* setTimeout(function() { 150 | console.log("Caiu, tentando depois de 5 segundos."); 151 | spawGstreamer(12345); 152 | }, 5 * 1000);*/ 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /node-transcoder-ogg-mp3.js: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | */ 15 | 16 | var net = require("net"); 17 | var express = require("express"); 18 | var http = require("http"); 19 | var child = require('child_process'); 20 | 21 | var cmd = '/usr/bin/gst-launch-0.10'; 22 | var options = null; 23 | var gstMuxer = null; 24 | 25 | var GSTREAMER_PORT = new Number(1000+Math.random() * 9000).toFixed(0); //random port 26 | 27 | var DO_BURST_CONNECTION = false; 28 | 29 | http.ServerResponse.prototype.getHashCode = (function() { 30 | var id = 0; 31 | return function() { 32 | if (!this["hashCode"]) { 33 | this["hashCode"] = ""; 34 | } 35 | return this["hashCode"]; 36 | } 37 | })(); 38 | 39 | if(process.argv.length < 5) { 40 | console.log("Please inform all the 3 parameters: URL STREAM_FILE_OUT[.mp3 or .ogg] PORT"); 41 | return; 42 | } 43 | //var URL = "http://radiolivre.org:8000/muda.ogg"; 44 | var URL = process.argv[2]; 45 | var STREAM_OUT = process.argv[3]; 46 | var SERVER_PORT = new Number(process.argv[4]); 47 | 48 | try { 49 | console.log("Starting the gstreamer pipeline..."); 50 | spawGstreamer(GSTREAMER_PORT, URL, STREAM_OUT.indexOf(".mp3") != -1); 51 | console.log("Gstreamer pipeline started."); 52 | } catch(err) { 53 | console.log("Error starting the gstreamer pipeline."); 54 | } 55 | 56 | var buffer = []; 57 | // Implementing a simple "burts-on-connect" 58 | if(DO_BURST_CONNECTION) { 59 | var bufferSize = 1 * 1024 *1024; // 1mb in bytes 60 | setTimeout(function() { 61 | console.log("Buffering data from gstreamer"); 62 | var stream = net.createConnection(GSTREAMER_PORT); 63 | //stream.setTimeout(30 * 1000); 64 | stream.addListener("connect", function() { 65 | console.log("Connected to gstreamer"); 66 | stream.addListener("data", function(data){ 67 | var size = currentBufferSize(); 68 | console.log("Add data " + data.length + " bytes from gstreamer to buffer. Total: "+ size); 69 | while (size > bufferSize) { 70 | buffer.shift(); 71 | } 72 | buffer.push(data); 73 | }); 74 | }); 75 | 76 | }, 10 * 1000); 77 | function currentBufferSize() { 78 | var size = 0, i=0, l=buffer.length; 79 | for (; i. 14 | */ 15 | 16 | var net = require("net"); 17 | var express = require("express"); 18 | var http = require("http"); 19 | var child = require('child_process'); 20 | var jade = require('jade') 21 | var fs = require('fs'); 22 | 23 | var cmd = '/usr/bin/gst-launch-0.10'; 24 | var options = null; 25 | var gstMuxer = null; 26 | 27 | var GSTREAMER_PORT = new Number(1000+Math.random() * 9000).toFixed(0); //random port 28 | 29 | var DO_BURST_CONNECTION = false; 30 | 31 | http.ServerResponse.prototype.getHashCode = (function() { 32 | var id = 0; 33 | return function() { 34 | if (!this["hashCode"]) { 35 | this["hashCode"] = ""; 36 | } 37 | return this["hashCode"]; 38 | } 39 | })(); 40 | 41 | if(process.argv.length < 5) { 42 | console.log("Please inform all the 3 parameters: URL OGG_STREAM_LINK PORT"); 43 | return; 44 | } 45 | //var URL = "rtsp://stream.camara.gov.br/tvcamara1t200"; 46 | var URL = process.argv[2]; 47 | var STREAM_OUT = process.argv[3]; 48 | var SERVER_PORT = new Number(process.argv[4]); 49 | 50 | try { 51 | console.log("Starting the gstreamer pipeline..."); 52 | spawGstreamer(GSTREAMER_PORT, URL); 53 | console.log("Gstreamer pipeline started."); 54 | } catch(err) { 55 | console.log("Error starting the gstreamer pipeline."); 56 | } 57 | 58 | var buffer = []; 59 | // Implementing a simple "burts-on-connect" 60 | if(DO_BURST_CONNECTION) { 61 | var bufferSize = 1 * 1024 *1024; // 1mb in bytes 62 | setTimeout(function() { 63 | console.log("Buffering data from gstreamer"); 64 | var stream = net.createConnection(GSTREAMER_PORT); 65 | //stream.setTimeout(30 * 1000); 66 | stream.addListener("connect", function() { 67 | console.log("Connected to gstreamer"); 68 | stream.addListener("data", function(data){ 69 | var size = currentBufferSize(); 70 | console.log("Add data " + data.length + " bytes from gstreamer to buffer. Total: "+ size); 71 | while (size > bufferSize) { 72 | buffer.shift(); 73 | } 74 | buffer.push(data); 75 | }); 76 | }); 77 | 78 | }, 10 * 1000); 79 | function currentBufferSize() { 80 | var size = 0, i=0, l=buffer.length; 81 | for (; i 0) { 101 | for (var t = time - (time % options.grid.millisPerLine); t >= time - (dimensions.width * options.millisPerPixel); t -= options.grid.millisPerLine) { 102 | canvasContext.beginPath(); 103 | var gx = Math.round(dimensions.width - ((time - t) / options.millisPerPixel)); 104 | canvasContext.moveTo(gx, 0); 105 | canvasContext.lineTo(gx, dimensions.height); 106 | canvasContext.stroke(); 107 | canvasContext.closePath(); 108 | } 109 | } 110 | 111 | // Horizontal (value) dividers. 112 | for (var v = 1; v < options.grid.verticalSections; v++) { 113 | var gy = Math.round(v * dimensions.height / options.grid.verticalSections); 114 | canvasContext.beginPath(); 115 | canvasContext.moveTo(0, gy); 116 | canvasContext.lineTo(dimensions.width, gy); 117 | canvasContext.stroke(); 118 | canvasContext.closePath(); 119 | } 120 | // Bounding rectangle. 121 | canvasContext.beginPath(); 122 | canvasContext.strokeRect(0, 0, dimensions.width, dimensions.height); 123 | canvasContext.closePath(); 124 | canvasContext.restore(); 125 | 126 | // Calculate the current scale of the chart, from all time series. 127 | var maxValue = undefined; 128 | var minValue = undefined; 129 | 130 | for (var d = 0; d < this.seriesSet.length; d++) { 131 | // TODO(ndunn): We could calculate / track these values as they stream in. 132 | var timeSeries = this.seriesSet[d].timeSeries; 133 | if (timeSeries.maxValue) { 134 | maxValue = maxValue ? Math.max(maxValue, timeSeries.maxValue) : timeSeries.maxValue; 135 | } 136 | 137 | if (timeSeries.minValue) { 138 | minValue = minValue ? Math.min(minValue, timeSeries.minValue) : timeSeries.minValue; 139 | } 140 | } 141 | 142 | if (!maxValue && !minValue) { 143 | return; 144 | } 145 | 146 | var valueRange = maxValue - minValue; 147 | var num = null; 148 | 149 | // For each data set... 150 | for (var d = 0; d < this.seriesSet.length; d++) { 151 | canvasContext.save(); 152 | var timeSeries = this.seriesSet[d].timeSeries; 153 | var dataSet = timeSeries.data; 154 | var seriesOptions = this.seriesSet[d].options; 155 | 156 | // Delete old data that's moved off the left of the chart. 157 | // We must always keep the last expired data point as we need this to draw the 158 | // line that comes into the chart, but any points prior to that can be removed. 159 | while (dataSet.length >= 2 && dataSet[1][0] < time - (dimensions.width * options.millisPerPixel)) { 160 | dataSet.splice(0, 1); 161 | } 162 | 163 | // Set style for this dataSet. 164 | canvasContext.lineWidth = seriesOptions.lineWidth || 1; 165 | canvasContext.fillStyle = seriesOptions.fillStyle; 166 | canvasContext.strokeStyle = seriesOptions.strokeStyle || '#ffffff'; 167 | // Draw the line... 168 | canvasContext.beginPath(); 169 | // Retain lastX, lastY for calculating the control points of bezier curves. 170 | var firstX = 0, lastX = 0, lastY = 0; 171 | for (var i = 0; i < dataSet.length; i++) { 172 | // TODO: Deal with dataSet.length < 2. 173 | var x = Math.round(dimensions.width - ((time - dataSet[i][0]) / options.millisPerPixel)); 174 | var value = dataSet[i][1]; 175 | if(num == null) 176 | num = value; 177 | var offset = maxValue - value; 178 | var scaledValue = Math.round((offset / valueRange) * dimensions.height); 179 | var y = Math.max(Math.min(scaledValue, dimensions.height - 1), 1); // Ensure line is always on chart. 180 | 181 | if (i == 0) { 182 | firstX = x; 183 | } 184 | // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/B�zier_curve#Quadratic_curves 185 | // 186 | // Assuming A was the last point in the line plotted and B is the new point, 187 | // we draw a curve with control points P and Q as below. 188 | // 189 | // A---P 190 | // | 191 | // | 192 | // | 193 | // Q---B 194 | // 195 | // Importantly, A and P are at the same y coordinate, as are B and Q. This is 196 | // so adjacent curves appear to flow as one. 197 | // 198 | canvasContext.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop 199 | Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) 200 | Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) 201 | x, y); // endPoint (B) 202 | 203 | lastX = x, lastY = y; 204 | } 205 | if (dataSet.length > 0 && seriesOptions.fillStyle) { 206 | // Close up the fill region. 207 | canvasContext.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); 208 | canvasContext.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); 209 | canvasContext.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); 210 | canvasContext.fill(); 211 | } 212 | canvasContext.stroke(); 213 | canvasContext.closePath(); 214 | canvasContext.restore(); 215 | } 216 | 217 | // Draw the axis values on the chart. 218 | if (!options.labels.disabled) { 219 | canvasContext.fillStyle = options.labels.fillStyle; 220 | var maxValueString = maxValue.toFixed(2); 221 | var minValueString = minValue.toFixed(2); 222 | canvasContext.fillText(maxValueString, dimensions.width - canvasContext.measureText(maxValueString).width - 2, 10); 223 | canvasContext.fillText(minValueString, dimensions.width - canvasContext.measureText(minValueString).width - 2, dimensions.height - 2); 224 | canvasContext.fillText(value.toFixed(2), 20, 10); 225 | } 226 | 227 | canvasContext.restore(); // See .save() above. 228 | } 229 | --------------------------------------------------------------------------------