Comparar commits
17 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| a0a9a38427 | |||
| 270fb8fcbf | |||
| a4a4acf109 | |||
| 660206b3f4 | |||
| 9b9c29a2df | |||
| b1b2967654 | |||
| 7606a9f35c | |||
| e5a5960106 | |||
| 73e3b48356 | |||
| 91f60fc341 | |||
| 94dce52391 | |||
| 4feaaddbe7 | |||
| 2c0641547e | |||
| 9787c83ad0 | |||
| cb6b2014a4 | |||
| 35e84bb038 | |||
| 808079f780 |
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
.*.swp
|
||||
.DS_Store
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
0.1.0 / 2012-10-20
|
||||
==================
|
||||
|
||||
* Initial version, unreleased
|
||||
|
||||
0.2.0 / 2012-10-20
|
||||
==================
|
||||
|
||||
* released as npm package dronestream
|
||||
|
||||
1.0.0 / 2012-12-15
|
||||
==================
|
||||
|
||||
* add support for multiple browser clients
|
||||
* add support for browser reloads
|
||||
* reconnect to drone on failure
|
||||
* add support for bare http sockers
|
||||
* remove express dependency
|
||||
* export as a module
|
||||
* added examples for using in other applications
|
||||
|
||||
1.0.1 / 2012-12-16
|
||||
==================
|
||||
|
||||
* update documentation
|
||||
* add changelog
|
||||
+30
-4
@@ -10,6 +10,27 @@ This code uses web-sockets and the incredibly awesome
|
||||
[Broadway.js](https://github.com/mbebenita/Broadway) to render the video frames
|
||||
in your browser using a WebGL canvas.
|
||||
|
||||
## How to use
|
||||
|
||||
Please see the http.createServer and Express 3.0 examples in the 'examples' dir.
|
||||
You attach the stream to your server like this:
|
||||
```javascript
|
||||
// in node:
|
||||
//
|
||||
// note that the 'server' object points to a server instance and NOT an express app.
|
||||
require("dronestream").listen(server);
|
||||
```
|
||||
|
||||
We serve the client in the same manner as Socket.IO. Add a reference to
|
||||
**/dronestream/nodecopter-client.js** in your template. Then attach the stream to a DOM node:
|
||||
```html
|
||||
<!-- on the client -->
|
||||
<script src="/dronestream/nodecopter-client.js"></script>
|
||||
<script>
|
||||
// video canvas will auto-size to the DOM-node, or default to 640*360 if no size is set.
|
||||
new NodecopterStream(document.getElementById("droneStream"));
|
||||
</script>
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -22,10 +43,9 @@ In the browser broadway takes care of the rendering of the WebGL canvas.
|
||||
|
||||
## Status
|
||||
|
||||
For this release I was exclusively interested in the lowest possible latency.
|
||||
There is no error handling for the websockets, the connection to the drone or
|
||||
the video player what-so-ever. This may come eventually, or may not. I think it
|
||||
is enough to be used as a starting point for your own integration.
|
||||
Node-dronestream has gained some stability in the last release. It attempts
|
||||
to recover lost connections to the drone, and it handles multiple clients,
|
||||
disconnections, etc. See "How to use" for API.
|
||||
|
||||
## Thanks
|
||||
|
||||
@@ -45,3 +65,9 @@ is enough to be used as a starting point for your own integration.
|
||||
- Brian Leroux for being not content with the original solution and for
|
||||
cleaning up the predecessor, nodecopter-stream.
|
||||
|
||||
- @karlwestin for picking up where I was to lazy to actually make this usable.
|
||||
|
||||
## Demo
|
||||
|
||||
Watch @felixge demoing node-dronestream live at german user group cgnjs:
|
||||
http://www.youtube.com/watch?v=nwGNNMJt4mE&t=19m52
|
||||
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('express')
|
||||
, routes = require('./routes')
|
||||
, http = require('http')
|
||||
, path = require('path')
|
||||
, app = express()
|
||||
, server = http.createServer(app)
|
||||
, WebSocketServer = require('ws').Server
|
||||
, wss = new WebSocketServer({server: server})
|
||||
, sockets = []
|
||||
, Parser = require('./lib/PaVEParser')
|
||||
, arDrone = require('ar-drone')
|
||||
;
|
||||
|
||||
function init() {
|
||||
var tcpVideoStream = new arDrone.Client.PngStream.TcpVideoStream({timeout: 4000})
|
||||
, p = new Parser();
|
||||
|
||||
console.log("Connecting to stream");
|
||||
|
||||
tcpVideoStream.connect(function () {
|
||||
tcpVideoStream.pipe(p);
|
||||
});
|
||||
|
||||
tcpVideoStream.on("error", function(err) {
|
||||
console.log("There was an error: %s", err.message);
|
||||
tcpVideoStream.end();
|
||||
tcpVideoStream.emit("end");
|
||||
init();
|
||||
});
|
||||
|
||||
p.on('data', function (data) {
|
||||
sockets.forEach(function(socket) {
|
||||
socket.send(data, {binary: true});
|
||||
});
|
||||
});
|
||||
}
|
||||
init();
|
||||
|
||||
wss.on('connection', function (socket) {
|
||||
sockets.push(socket);
|
||||
|
||||
socket.on("close", function() {
|
||||
console.log("Closing socket");
|
||||
sockets = sockets.filter(function(el) {
|
||||
return el !== socket;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.configure(function () {
|
||||
app.set('port', process.env.PORT || 3000);
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade', { pretty: true });
|
||||
app.use(express.favicon());
|
||||
app.use(express.logger('dev'));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(app.router);
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
});
|
||||
|
||||
app.configure('development', function () {
|
||||
app.use(express.errorHandler());
|
||||
app.locals.pretty = true;
|
||||
});
|
||||
|
||||
|
||||
app.get('/', routes.index);
|
||||
|
||||
if (module.parent) {
|
||||
module.exports = server;
|
||||
} else {
|
||||
server.listen(app.get('port'), function () {
|
||||
console.log("Express server listening on port " + app.get('port'));
|
||||
});
|
||||
}
|
||||
Arquivo executável
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
cat dist/vendor/broadway/sylvester.js dist/vendor/broadway/glUtils.js dist/vendor/broadway/util.js dist/vendor/broadway/avc-codec.js dist/vendor/broadway/avc.js dist/vendor/broadway/canvas.js dist/nodecopter-stream.js > dist/nodecopter-client.js
|
||||
externo
+1162
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,10 @@
|
||||
var http = require("http"),
|
||||
drone = require("../../index");
|
||||
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
require("fs").createReadStream(__dirname + "/index.html").pipe(res);
|
||||
});
|
||||
|
||||
drone.listen(server);
|
||||
server.listen(5555);
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>Stream as module</title>
|
||||
<script src="/dronestream/nodecopter-client.js" type="text/javascript" charset="utf-8"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="heading">Stream through a normal require("http").createServer</h1>
|
||||
<div id="droneStream" style="width: 640px; height: 360px"> </div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
new NodecopterStream(document.getElementById("droneStream"));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
var express = require('express')
|
||||
, routes = require('./routes')
|
||||
, app = express()
|
||||
, path = require('path')
|
||||
, server = require("http").createServer(app)
|
||||
;
|
||||
|
||||
|
||||
app.configure(function () {
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade', { pretty: true });
|
||||
app.use(express.favicon());
|
||||
app.use(express.logger('dev'));
|
||||
app.use(app.router);
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
});
|
||||
|
||||
app.configure('development', function () {
|
||||
app.use(express.errorHandler());
|
||||
app.locals.pretty = true;
|
||||
});
|
||||
|
||||
app.get('/', routes.index);
|
||||
|
||||
/*
|
||||
* Important:
|
||||
*
|
||||
* pass in the server object to listen, not the express app
|
||||
* call 'listen' on the server, not the express app
|
||||
*/
|
||||
// should be require("node-dronestream").listen(server)
|
||||
require("../../index").listen(server);
|
||||
server.listen(3000);
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "dronestream-example",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"express": "3.0.0rc5",
|
||||
"jade": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app"
|
||||
},
|
||||
"author": "Bernhard K. Weisshuhn <bkw@codingforce.com>",
|
||||
"license": "BSD"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
extends layout
|
||||
|
||||
block append head
|
||||
script(type='text/javascript', src='/dronestream/nodecopter-client.js')
|
||||
|
||||
-# for developing the client, use those url:s
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/sylvester.js')
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/glUtils.js')
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/util.js')
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/avc-codec.js')
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/avc.js')
|
||||
-# script(type='text/javascript', src='/dronestream/vendor/broadway/canvas.js')
|
||||
-# script(type='text/javascript', src='/dronestream/nodecopter-stream.js')
|
||||
-# concatenated version of client
|
||||
|
||||
|
||||
block append bodyscripts
|
||||
script
|
||||
var copterStream = new NodecopterStream(document.querySelector('#dronestream'));
|
||||
|
||||
block content
|
||||
div#dronestream(width=640, height=360)
|
||||
@@ -7,7 +7,6 @@ html
|
||||
title= title
|
||||
link(rel='stylesheet', href='/css/normalize.min.css')
|
||||
link(rel='stylesheet', href='/css/style.css')
|
||||
script(src='/js/vendor/h5bp/modernizr-2.6.1-respond-1.1.0.min.js')
|
||||
body
|
||||
div.header-container
|
||||
header.wrapper.clearfix
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("./lib/server");
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Drone Stream listen:
|
||||
* Takes a) a port number or b) a server object (node http or express, etc);
|
||||
*/
|
||||
var staticDir = "dronestream"
|
||||
, check = new RegExp("^/" + staticDir, "i")
|
||||
, dist = __dirname + "/../dist"
|
||||
;
|
||||
|
||||
module.exports.listen = function listen(server) {
|
||||
if(typeof server == "number") {
|
||||
var port = server;
|
||||
server = require("http").createServer();
|
||||
server.listen(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Serving up the static files needed
|
||||
*/
|
||||
var oldHandlers = server.listeners("request").splice(0);
|
||||
|
||||
server.on("request", function(req, res) {
|
||||
if(handler(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i = 0; i < oldHandlers.length; i++) {
|
||||
oldHandlers[i].call(server, req, res);
|
||||
}
|
||||
});
|
||||
|
||||
function handler(req, res, next) {
|
||||
if(!check.test(req.url)) {
|
||||
return false;
|
||||
}
|
||||
var path = dist + req.url.replace(check, "");
|
||||
console.log("checking static path: %s", path);
|
||||
var read = require('fs').createReadStream(path);
|
||||
read.pipe(res);
|
||||
read.on("error", function(e) { console.log("Stream error: %s", e.message); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connecting stream + websocket server
|
||||
*/
|
||||
return require("./stream").attach(server);
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Sets up a real stream + attaches it to a server
|
||||
*/
|
||||
module.exports.attach = function droneStream(server) {
|
||||
var WebSocketServer = require('ws').Server
|
||||
, wss = new WebSocketServer({server: server})
|
||||
, sockets = []
|
||||
, Parser = require('./PaVEParser')
|
||||
, arDrone = require('ar-drone')
|
||||
;
|
||||
|
||||
function init() {
|
||||
var tcpVideoStream = new arDrone.Client.PngStream.TcpVideoStream({timeout: 4000})
|
||||
, p = new Parser();
|
||||
|
||||
console.log("Connecting to stream");
|
||||
|
||||
tcpVideoStream.connect(function () {
|
||||
tcpVideoStream.pipe(p);
|
||||
});
|
||||
|
||||
tcpVideoStream.on("error", function(err) {
|
||||
console.log("There was an error: %s", err.message);
|
||||
tcpVideoStream.end();
|
||||
tcpVideoStream.emit("end");
|
||||
init();
|
||||
});
|
||||
|
||||
p.on('data', function (data) {
|
||||
sockets.forEach(function(socket) {
|
||||
socket.send(data, {binary: true});
|
||||
});
|
||||
});
|
||||
}
|
||||
init();
|
||||
|
||||
wss.on('connection', function (socket) {
|
||||
sockets.push(socket);
|
||||
|
||||
socket.on("close", function() {
|
||||
console.log("Closing socket");
|
||||
sockets = sockets.filter(function(el) {
|
||||
return el !== socket;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
+8
-6
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "dronestream",
|
||||
"description": "video live stream from your parrot ar.drone 2.0 to your browser in pure javascript",
|
||||
"version": "0.2.0",
|
||||
"version": "1.0.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:bkw/node-dronestream.git"
|
||||
},
|
||||
"main": "index",
|
||||
"keywords": [
|
||||
"drone",
|
||||
"nodecopter",
|
||||
@@ -15,16 +16,17 @@
|
||||
"browser",
|
||||
"x264"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node app"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "3.0.0rc5",
|
||||
"jade": "*",
|
||||
"ws": "~0.4.22",
|
||||
"ar-drone": "0.0.3",
|
||||
"buffy": "0.0.4"
|
||||
},
|
||||
"author": "Bernhard K. Weisshuhn <bkw@codingforce.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Karl Westin",
|
||||
"email": "karl.westin@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "BSD"
|
||||
}
|
||||
|
||||
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -1,17 +0,0 @@
|
||||
extends layout
|
||||
|
||||
block append head
|
||||
script(type='text/javascript', src='/js/vendor/broadway/sylvester.js')
|
||||
script(type='text/javascript', src='/js/vendor/broadway/glUtils.js')
|
||||
script(type='text/javascript', src='/js/vendor/broadway/util.js')
|
||||
script(type='text/javascript', src='/js/vendor/broadway/avc-codec.js')
|
||||
script(type='text/javascript', src='/js/vendor/broadway/avc.js')
|
||||
script(type='text/javascript', src='/js/vendor/broadway/canvas.js')
|
||||
script(type='text/javascript', src='/js/nodecopter-stream.js')
|
||||
|
||||
block append bodyscripts
|
||||
script
|
||||
var copterStream = new NodecopterStream(document.querySelector('#dronestream'));
|
||||
|
||||
block content
|
||||
div#dronestream(width=640, height=360)
|
||||
Referência em uma Nova Issue
Bloquear um usuário