Comparar commits
54 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 40b51e056c | |||
| fc8754dc3e | |||
| 309376797d | |||
| 6397b10149 | |||
| 6b0f2e374a | |||
| 5cb04779a7 | |||
| 524f7ed21e | |||
| 47c3e2c57f | |||
| 2064227220 | |||
| bfd7471b87 | |||
| feca81242a | |||
| 6625c22291 | |||
| 1832c0296b | |||
| b57cea6d40 | |||
| 14c874c08d | |||
| 4b30275f4e | |||
| 6edf209575 | |||
| 6c89f533f1 | |||
| f396d4caee | |||
| 550a1b63d9 | |||
| 499d8737e4 | |||
| 3d05582d2f | |||
| 4310a9308f | |||
| 3c0abd5f16 | |||
| 57fade81c7 | |||
| cec02b0ca9 | |||
| 4cb48f1fd1 | |||
| ef1987f2a4 | |||
| ace272f349 | |||
| 54219e67ac | |||
| d0d150fb37 | |||
| 8add9fc12b | |||
| 2f19e20fd2 | |||
| d8eaacc2ad | |||
| cf23e02416 | |||
| 7163fb5262 | |||
| 93236bd275 | |||
| a0a9a38427 | |||
| 270fb8fcbf | |||
| a4a4acf109 | |||
| 660206b3f4 | |||
| 9b9c29a2df | |||
| b1b2967654 | |||
| 7606a9f35c | |||
| e5a5960106 | |||
| 73e3b48356 | |||
| 91f60fc341 | |||
| 94dce52391 | |||
| 4feaaddbe7 | |||
| 2c0641547e | |||
| 9787c83ad0 | |||
| cb6b2014a4 | |||
| 35e84bb038 | |||
| 808079f780 |
@@ -1,2 +1,4 @@
|
||||
node_modules
|
||||
dist/broadway.js
|
||||
.*.swp
|
||||
.DS_Store
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"browser" : false,
|
||||
"boss" : true,
|
||||
"curly": true,
|
||||
"debug": false,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"evil": false,
|
||||
"forin": true,
|
||||
"immed": true,
|
||||
"indent": 4,
|
||||
"jquery": true,
|
||||
"laxbreak": false,
|
||||
"laxcomma": true,
|
||||
"newcap": true,
|
||||
"noarg": false,
|
||||
"noempty": true,
|
||||
"nonew": true,
|
||||
"nomen": false,
|
||||
"onevar": true,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"trailing": true,
|
||||
"undef": true,
|
||||
"sub": false,
|
||||
"strict": true,
|
||||
"globalstrict": true,
|
||||
"white": true,
|
||||
"node" : true
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
module.exports = function (grunt) {
|
||||
'use strict';
|
||||
// load all grunt tasks
|
||||
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'lib/{,*/}*.js',
|
||||
'dist/nodecopter-stream.js'
|
||||
]
|
||||
},
|
||||
|
||||
uglify: {
|
||||
dist: {
|
||||
files: {
|
||||
'dist/broadway.js' : [
|
||||
'dist/vendor/broadway/glUtils.js',
|
||||
'dist/vendor/broadway/util.js',
|
||||
'dist/vendor/broadway/avc.js',
|
||||
'dist/vendor/broadway/canvas.js',
|
||||
'dist/nodecopter-stream.js'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
|
||||
},
|
||||
dist: {
|
||||
src: [
|
||||
'dist/vendor/broadway/sylvester.js',
|
||||
'dist/vendor/broadway/avc-codec.js',
|
||||
'dist/broadway.js'
|
||||
],
|
||||
dest: 'dist/nodecopter-client.js'
|
||||
}
|
||||
}
|
||||
});
|
||||
grunt.registerTask('default', ['jshint', 'uglify', 'concat']);
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
1.0.2 / 2012-12-27
|
||||
==================
|
||||
|
||||
* use requestAnimationFrame() for rendering video frames
|
||||
* cleaned up express example
|
||||
* update ar-drone and buffy dependencies to latest versions
|
||||
|
||||
1.0.3 / 2013-03-19
|
||||
==================
|
||||
|
||||
* add custom drone ip to server options
|
||||
* fix static files on webserver
|
||||
|
||||
1.1.0 / 2013-08-07
|
||||
==================
|
||||
|
||||
* add support for custom host and port to client
|
||||
* support passing an existing video stream
|
||||
* switch to grunt for building
|
||||
* express fixes
|
||||
* remove constants for strict mode
|
||||
* bump ws and ar-drone dependencies
|
||||
* add hook for postprocessors
|
||||
|
||||
1.1.1 / 2013-08-31
|
||||
==================
|
||||
|
||||
* use cleaned up version of broadways avc.js, fixes errors about 'clip'
|
||||
* switch to ardrone 0.2.0
|
||||
|
||||
+32
-4
@@ -10,6 +10,29 @@ 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);
|
||||
// if your drone is on a different IP
|
||||
require("dronestream").listen(server, { ip: "192.168.2.155" });
|
||||
```
|
||||
|
||||
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 +45,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 +67,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'));
|
||||
});
|
||||
}
|
||||
externo
+5
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+129
@@ -0,0 +1,129 @@
|
||||
/*jshint browser:true */
|
||||
/*global Avc:true, YUVWebGLCanvas: true, Size: true, requestAnimationFrame:true */
|
||||
|
||||
/* requestAnimationFrame polyfill: */
|
||||
(function (window) {
|
||||
'use strict';
|
||||
var lastTime = 0,
|
||||
vendors = ['ms', 'moz', 'webkit', 'o'],
|
||||
x,
|
||||
length,
|
||||
currTime,
|
||||
timeToCall;
|
||||
|
||||
for (x = 0, length = vendors.length; x < length && !window.requestAnimationFrame; ++x) {
|
||||
window.requestAnimationFrame = window[
|
||||
vendors[x] + 'RequestAnimationFrame'
|
||||
];
|
||||
window.cancelAnimationFrame = window[
|
||||
vendors[x] + 'CancelAnimationFrame'
|
||||
] || window[vendors[x] + 'CancelRequestAnimationFrame'];
|
||||
}
|
||||
|
||||
if (!window.requestAnimationFrame) {
|
||||
window.requestAnimationFrame = function (callback, element) {
|
||||
currTime = new Date().getTime();
|
||||
timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
lastTime = currTime + timeToCall;
|
||||
return window.setTimeout(function () {
|
||||
callback(currTime + timeToCall);
|
||||
}, timeToCall);
|
||||
};
|
||||
}
|
||||
|
||||
if (!window.cancelAnimationFrame) {
|
||||
window.cancelAnimationFrame = function (id) {
|
||||
clearTimeout(id);
|
||||
};
|
||||
}
|
||||
}(window));
|
||||
|
||||
|
||||
/* NodeCopterStream: */
|
||||
(function (window, document, undefined) {
|
||||
'use strict';
|
||||
var NS,
|
||||
socket,
|
||||
avc,
|
||||
webGLCanvas,
|
||||
width,
|
||||
height,
|
||||
callbackOnce = null;
|
||||
|
||||
function setupAvc() {
|
||||
avc = new Avc();
|
||||
avc.configure({
|
||||
filter: 'original',
|
||||
filterHorLuma: 'optimized',
|
||||
filterVerLumaEdge: 'optimized',
|
||||
getBoundaryStrengthsA: 'optimized'
|
||||
});
|
||||
avc.onPictureDecoded = handleDecodedFrame;
|
||||
}
|
||||
|
||||
function handleNalUnits(message) {
|
||||
avc.decode(new Uint8Array(message.data));
|
||||
}
|
||||
|
||||
function handleDecodedFrame(buffer, bufWidth, bufHeight) {
|
||||
var callback;
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
var lumaSize = bufWidth * bufHeight,
|
||||
chromaSize = lumaSize >> 2;
|
||||
|
||||
webGLCanvas.YTexture.fill(buffer.subarray(0, lumaSize));
|
||||
webGLCanvas.UTexture.fill(buffer.subarray(lumaSize, lumaSize + chromaSize));
|
||||
webGLCanvas.VTexture.fill(buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize));
|
||||
webGLCanvas.drawScene();
|
||||
});
|
||||
|
||||
// call callback with Y portion (grayscale image)
|
||||
if (null !== callbackOnce && width) {
|
||||
callback = callbackOnce;
|
||||
callbackOnce = null;
|
||||
// decoded buffer size may be larger,
|
||||
// so use subarray with actual dimensions
|
||||
callback(buffer.subarray(0, width * height));
|
||||
}
|
||||
}
|
||||
|
||||
function setupCanvas(div) {
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
width = div.attributes.width ? div.attributes.width.value : 640;
|
||||
height = div.attributes.height ? div.attributes.height.value : 360;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.style.backgroundColor = "#333333";
|
||||
div.appendChild(canvas);
|
||||
|
||||
webGLCanvas = new YUVWebGLCanvas(canvas, new Size(width, height));
|
||||
}
|
||||
|
||||
|
||||
NS = function (div, options) {
|
||||
var hostname, port;
|
||||
options = options || {};
|
||||
hostname = options.hostname || window.document.location.hostname;
|
||||
port = options.port || window.document.location.port;
|
||||
|
||||
setupCanvas(div);
|
||||
setupAvc();
|
||||
|
||||
socket = new WebSocket(
|
||||
'ws://' + hostname + ':' + port + '/dronestream'
|
||||
);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
socket.onmessage = handleNalUnits;
|
||||
};
|
||||
|
||||
// enqueue callback oto be called with next (black&white) frame
|
||||
NS.prototype.onNextFrame = function (callback) {
|
||||
callbackOnce = callback;
|
||||
};
|
||||
|
||||
window.NodecopterStream = NS;
|
||||
|
||||
}(window, document, undefined));
|
||||
externo
+285
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Requires: avc-codec.js
|
||||
**/
|
||||
|
||||
assert (Module);
|
||||
|
||||
var Avc = (function avc() {
|
||||
'use strict';
|
||||
|
||||
var MAX_STREAM_BUFFER_LENGTH = 1024 * 1024,
|
||||
HEAP8 = Module.HEAP8,
|
||||
HEAPU8 = Module.HEAPU8,
|
||||
HEAP16 = Module.HEAP16,
|
||||
HEAP32 = Module.HEAP32,
|
||||
_h264bsdClip = Module._get_h264bsdClip(),
|
||||
patches = {
|
||||
"filter": {
|
||||
name: "_h264bsdFilterPicture",
|
||||
display: "Filter Picture",
|
||||
original: "Original_h264bsdFilterPicture",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
}
|
||||
},
|
||||
"filterHorLuma": {
|
||||
name: "_FilterHorLuma",
|
||||
display: "Filter Hor Luma",
|
||||
original: "OriginalFilterHorLuma",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedFilterHorLuma}
|
||||
}
|
||||
},
|
||||
"filterVerLumaEdge": {
|
||||
name: "_FilterVerLumaEdge",
|
||||
display: "Filter Ver Luma Edge",
|
||||
original: "OriginalFilterVerLumaEdge",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedFilterVerLumaEdge}
|
||||
}
|
||||
},
|
||||
"getBoundaryStrengthsA": {
|
||||
name: "_GetBoundaryStrengthsA",
|
||||
display: "Get Boundary Strengths",
|
||||
original: "OriginalGetBoundaryStrengthsA",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedGetBoundaryStrengthsA}
|
||||
}
|
||||
}
|
||||
},
|
||||
origFn = {};
|
||||
|
||||
function clip(x, y, z) {
|
||||
return z < x ? x : (z > y ? y : z);
|
||||
}
|
||||
|
||||
function OptimizedGetBoundaryStrengthsA($mb, $bS) {
|
||||
var $totalCoeff = $mb + 28;
|
||||
|
||||
var tc0 = HEAP16[$totalCoeff + 0 >> 1];
|
||||
var tc1 = HEAP16[$totalCoeff + 2 >> 1];
|
||||
var tc2 = HEAP16[$totalCoeff + 4 >> 1];
|
||||
var tc3 = HEAP16[$totalCoeff + 6 >> 1];
|
||||
var tc4 = HEAP16[$totalCoeff + 8 >> 1];
|
||||
var tc5 = HEAP16[$totalCoeff + 10 >> 1];
|
||||
var tc6 = HEAP16[$totalCoeff + 12 >> 1];
|
||||
var tc7 = HEAP16[$totalCoeff + 14 >> 1];
|
||||
var tc8 = HEAP16[$totalCoeff + 16 >> 1];
|
||||
var tc9 = HEAP16[$totalCoeff + 18 >> 1];
|
||||
var tc10 = HEAP16[$totalCoeff + 20 >> 1];
|
||||
var tc11 = HEAP16[$totalCoeff + 22 >> 1];
|
||||
var tc12 = HEAP16[$totalCoeff + 24 >> 1];
|
||||
var tc13 = HEAP16[$totalCoeff + 26 >> 1];
|
||||
var tc14 = HEAP16[$totalCoeff + 28 >> 1];
|
||||
var tc15 = HEAP16[$totalCoeff + 30 >> 1];
|
||||
|
||||
HEAP32[$bS + 32 >> 2] = tc2 || tc0 ? 2 : 0;
|
||||
HEAP32[$bS + 40 >> 2] = tc3 || tc1 ? 2 : 0;
|
||||
HEAP32[$bS + 48 >> 2] = tc6 || tc4 ? 2 : 0;
|
||||
HEAP32[$bS + 56 >> 2] = tc7 || tc5 ? 2 : 0;
|
||||
HEAP32[$bS + 64 >> 2] = tc8 || tc2 ? 2 : 0;
|
||||
HEAP32[$bS + 72 >> 2] = tc9 || tc3 ? 2 : 0;
|
||||
HEAP32[$bS + 80 >> 2] = tc12 || tc6 ? 2 : 0;
|
||||
HEAP32[$bS + 88 >> 2] = tc13 || tc7 ? 2 : 0;
|
||||
HEAP32[$bS + 96 >> 2] = tc10 || tc8 ? 2 : 0;
|
||||
HEAP32[$bS + 104 >> 2] = tc11 || tc9 ? 2 : 0;
|
||||
HEAP32[$bS + 112 >> 2] = tc14 || tc12 ? 2 : 0;
|
||||
HEAP32[$bS + 120 >> 2] = tc15 || tc13 ? 2 : 0;
|
||||
|
||||
HEAP32[$bS + 12 >> 2] = tc1 || tc0 ? 2 : 0;
|
||||
HEAP32[$bS + 20 >> 2] = tc4 || tc1 ? 2 : 0;
|
||||
HEAP32[$bS + 28 >> 2] = tc5 || tc4 ? 2 : 0;
|
||||
HEAP32[$bS + 44 >> 2] = tc3 || tc2 ? 2 : 0;
|
||||
HEAP32[$bS + 52 >> 2] = tc6 || tc3 ? 2 : 0;
|
||||
HEAP32[$bS + 60 >> 2] = tc7 || tc6 ? 2 : 0;
|
||||
HEAP32[$bS + 76 >> 2] = tc9 || tc8 ? 2 : 0;
|
||||
HEAP32[$bS + 84 >> 2] = tc12 || tc9 ? 2 : 0;
|
||||
HEAP32[$bS + 92 >> 2] = tc13 || tc12 ? 2 : 0;
|
||||
HEAP32[$bS + 108 >> 2] = tc11 || tc10 ? 2 : 0;
|
||||
HEAP32[$bS + 116 >> 2] = tc14 || tc11 ? 2 : 0;
|
||||
HEAP32[$bS + 124 >> 2] = tc15 || tc14 ? 2 : 0;
|
||||
}
|
||||
|
||||
function OptimizedFilterVerLumaEdge ($data, bS, $thresholds, imageWidth) {
|
||||
var delta, tc, tmp;
|
||||
var p0, q0, p1, q1, p2, q2;
|
||||
var tmpFlag;
|
||||
var $clp = _h264bsdClip + 512;
|
||||
var alpha = HEAP32[$thresholds + 4 >> 2];
|
||||
var beta = HEAP32[$thresholds + 8 >> 2];
|
||||
var val;
|
||||
|
||||
if (bS < 4) {
|
||||
tmp = tc = HEAPU8[HEAP32[$thresholds >> 2] + (bS - 1)] & 255;
|
||||
for (var i = 4; i > 0; i--) {
|
||||
p1 = HEAPU8[$data + -2] & 255;
|
||||
p0 = HEAPU8[$data + -1] & 255;
|
||||
q0 = HEAPU8[$data] & 255;
|
||||
q1 = HEAPU8[$data + 1] & 255;
|
||||
if (
|
||||
(Math.abs(p0 - q0) < alpha) &&
|
||||
(Math.abs(p1 - p0) < beta) &&
|
||||
(Math.abs(q1 - q0) < beta)
|
||||
) {
|
||||
p2 = HEAPU8[$data - 3] & 255;
|
||||
if (Math.abs(p2 - p0) < beta) {
|
||||
val = (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1;
|
||||
HEAP8[$data - 2] = p1 + clip(-tc, tc, val);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
q2 = HEAPU8[$data + 2] & 255;
|
||||
if (Math.abs(q2 - q0) < beta) {
|
||||
val = (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1;
|
||||
HEAP8[$data + 1] = (q1 + clip(-tc, tc, val));
|
||||
tmp++;
|
||||
}
|
||||
|
||||
val = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
|
||||
delta = clip(-tmp, tmp, val);
|
||||
|
||||
p0 = HEAPU8[$clp + (p0 + delta)] & 255;
|
||||
q0 = HEAPU8[$clp + (q0 - delta)] & 255;
|
||||
tmp = tc;
|
||||
HEAP8[$data - 1] = p0;
|
||||
HEAP8[$data] = q0;
|
||||
|
||||
$data += imageWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
origFn.OriginalFilterVerLumaEdge($data, bS, $thresholds, imageWidth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all four successive horizontal 4-pixel luma edges. This can be
|
||||
* done when bS is equal to all four edges.
|
||||
*/
|
||||
function OptimizedFilterHorLuma ($data, bS, $thresholds, imageWidth) {
|
||||
var delta, tc, tmp;
|
||||
var p0, q0, p1, q1, p2, q2;
|
||||
var tmpFlag;
|
||||
var $clp = _h264bsdClip + 512;
|
||||
var alpha = HEAP32[$thresholds + 4 >> 2];
|
||||
var beta = HEAP32[$thresholds + 8 >> 2];
|
||||
var val;
|
||||
|
||||
if (bS < 4) {
|
||||
tmp = tc = HEAPU8[HEAP32[$thresholds >> 2] + (bS - 1)] & 255;
|
||||
for (var i = 16; i > 0; i--) {
|
||||
p1 = HEAPU8[$data + (-imageWidth << 1)] & 255;
|
||||
p0 = HEAPU8[$data + -imageWidth] & 255;
|
||||
q0 = HEAPU8[$data] & 255;
|
||||
q1 = HEAPU8[$data + imageWidth] & 255;
|
||||
|
||||
if (
|
||||
(Math.abs(p0 - q0) < alpha) &&
|
||||
(Math.abs(p1 - p0) < beta) &&
|
||||
(Math.abs(q1 - q0) < beta)
|
||||
) {
|
||||
p2 = HEAPU8[$data + (-imageWidth * 3)] & 255;
|
||||
if (Math.abs(p2 - p0) < beta) {
|
||||
val = (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1;
|
||||
HEAP8[$data + (-imageWidth << 1)] = p1 + clip(-tc, tc, val);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
q2 = HEAPU8[$data + (imageWidth << 2)] & 255;
|
||||
if (Math.abs(q2 - q0) < beta) {
|
||||
val = (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1;
|
||||
HEAP8[$data + imageWidth] = (q1 + clip(-tc, tc, val));
|
||||
tmp++;
|
||||
}
|
||||
|
||||
val = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
|
||||
delta = clip(-tmp, tmp, val);
|
||||
|
||||
p0 = HEAPU8[$clp + (p0 + delta)] & 255;
|
||||
q0 = HEAPU8[$clp + (q0 - delta)] & 255;
|
||||
tmp = tc;
|
||||
HEAP8[$data - imageWidth] = p0;
|
||||
HEAP8[$data] = q0;
|
||||
|
||||
$data ++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
origFn.OriginalFilterHorLuma($data, bS, $thresholds, imageWidth);
|
||||
}
|
||||
}
|
||||
|
||||
function patchOptimizations(config, patches) {
|
||||
for (var name in patches) {
|
||||
var patch = patches[name];
|
||||
if (patch) {
|
||||
var option = config[name];
|
||||
if (!option) option = "original";
|
||||
console.info(name + ": " + option);
|
||||
assert (option in patch.options);
|
||||
var fn = patch.options[option].fn;
|
||||
if (fn) {
|
||||
origFn[patch.original] = Module.patch(null, patch.name, fn);
|
||||
console.info("Patching: " + patch.name + ", with: " + option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function constructor() {
|
||||
Module._broadwayInit();
|
||||
this.streamBuffer = toU8Array(Module._broadwayCreateStream(MAX_STREAM_BUFFER_LENGTH), MAX_STREAM_BUFFER_LENGTH);
|
||||
this.pictureBuffers = {};
|
||||
|
||||
this.onPictureDecoded = function (buffer, width, height) {
|
||||
// console.info(buffer.length);
|
||||
};
|
||||
|
||||
Module.patch(null, "_broadwayOnHeadersDecoded", function (x,y,z) {});
|
||||
|
||||
|
||||
Module.patch(null, "_broadwayOnPictureDecoded", function ($buffer, width, height) {
|
||||
var buffer = this.pictureBuffers[$buffer];
|
||||
if (!buffer) {
|
||||
buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2);
|
||||
}
|
||||
this.onPictureDecoded(buffer, width, height);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a typed array from a HEAP8 pointer.
|
||||
*/
|
||||
function toU8Array(ptr, length) {
|
||||
return HEAPU8.subarray(ptr, ptr + length);
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
/**
|
||||
* Decodes a stream buffer. This may be one single (unframed) NAL unit
|
||||
* without the start code, or a sequence of NAL units with framing start
|
||||
* code prefixes. This function overwrites stream buffer allocated by
|
||||
* the codec with the supplied buffer.
|
||||
*/
|
||||
decode: function decode(buffer) {
|
||||
// console.info("Decoding: " + buffer.length);
|
||||
this.streamBuffer.set(buffer);
|
||||
Module._broadwaySetStreamLength(buffer.length);
|
||||
Module._broadwayPlayStream();
|
||||
},
|
||||
configure: function (config) {
|
||||
patchOptimizations(config, patches);
|
||||
console.info("Broadway Configured: " + JSON.stringify(config));
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
})();
|
||||
@@ -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("dronestream").listen(server);
|
||||
require("../../index").listen(server);
|
||||
server.listen(3000);
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "dronestream-example",
|
||||
"version": "0.1.1",
|
||||
"dependencies": {
|
||||
"express": "3.0.x",
|
||||
"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,56 @@
|
||||
/*
|
||||
* 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, options) {
|
||||
'use strict';
|
||||
var port, oldHandlers;
|
||||
|
||||
if (typeof server === 'number') {
|
||||
port = server;
|
||||
server = require('http').createServer();
|
||||
server.listen(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Serving up the static files needed
|
||||
*/
|
||||
oldHandlers = server.listeners('request').splice(0);
|
||||
server.removeAllListeners('request');
|
||||
|
||||
server.on('request', function (req, res) {
|
||||
var i = 0;
|
||||
if (handler(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (; i < oldHandlers.length; ++i) {
|
||||
oldHandlers[i].call(server, req, res);
|
||||
}
|
||||
});
|
||||
|
||||
function handler(req, res, next) {
|
||||
var path, read;
|
||||
if (!check.test(req.url)) {
|
||||
return false;
|
||||
}
|
||||
path = dist + req.url.replace(check, '');
|
||||
console.log('checking static path: %s', path);
|
||||
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, options);
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*jshint node:true*/
|
||||
/*
|
||||
* Sets up a real stream + attaches it to a server
|
||||
*/
|
||||
module.exports.attach = function droneStream(server, options) {
|
||||
'use strict';
|
||||
var WebSocketServer = require('ws').Server,
|
||||
wss = new WebSocketServer({server: server, path: '/dronestream'}),
|
||||
sockets = [],
|
||||
Parser = require('./PaVEParser'),
|
||||
arDrone = require('ar-drone');
|
||||
|
||||
options = options || {};
|
||||
options.timeout = options.timeout || 4000;
|
||||
|
||||
function init() {
|
||||
|
||||
var tcpVideoStream, parser;
|
||||
|
||||
if (!options.tcpVideoStream) {
|
||||
tcpVideoStream = new arDrone.Client.PngStream.TcpVideoStream(
|
||||
options
|
||||
);
|
||||
|
||||
console.log(
|
||||
"Connecting to drone on %s", options.ip || "192.168.1.1"
|
||||
);
|
||||
|
||||
tcpVideoStream.connect();
|
||||
tcpVideoStream.on('error', function (err) {
|
||||
console.log('There was an error: %s', err.message);
|
||||
tcpVideoStream.end();
|
||||
tcpVideoStream.emit("end");
|
||||
init();
|
||||
});
|
||||
} else {
|
||||
tcpVideoStream = options.tcpVideoStream;
|
||||
}
|
||||
|
||||
parser = new Parser();
|
||||
tcpVideoStream.on('data', function (data) {
|
||||
parser.write(data);
|
||||
});
|
||||
parser.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;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
+18
-9
@@ -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.1.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:bkw/node-dronestream.git"
|
||||
},
|
||||
"main": "index",
|
||||
"keywords": [
|
||||
"drone",
|
||||
"nodecopter",
|
||||
@@ -15,16 +16,24 @@
|
||||
"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"
|
||||
"ws": "~0.4.27",
|
||||
"ar-drone": "0.2.0",
|
||||
"buffy": "0.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-contrib-uglify": "~0.2.2",
|
||||
"grunt-contrib-jshint": "~0.6.2",
|
||||
"matchdep": "~0.1.2",
|
||||
"grunt-contrib-concat": "~0.3.0"
|
||||
},
|
||||
"author": "Bernhard K. Weisshuhn <bkw@codingforce.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Karl Westin",
|
||||
"email": "karl.westin@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "BSD"
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*jshint browser:true */
|
||||
/*global Avc:true, YUVWebGLCanvas: true, Size: true */
|
||||
(function (window, document, undefined) {
|
||||
'use strict';
|
||||
var NS,
|
||||
socket,
|
||||
avc,
|
||||
webGLCanvas;
|
||||
|
||||
function setupAvc() {
|
||||
avc = new Avc();
|
||||
avc.configure({
|
||||
filter: 'original',
|
||||
filterHorLuma: 'optimized',
|
||||
filterVerLumaEdge: 'optimized',
|
||||
getBoundaryStrengthsA: 'optimized'
|
||||
});
|
||||
avc.onPictureDecoded = handleDecodedFrame;
|
||||
}
|
||||
|
||||
function handleNalUnits(message) {
|
||||
avc.decode(new Uint8Array(message.data));
|
||||
}
|
||||
|
||||
function handleDecodedFrame(buffer, width, height) {
|
||||
var lumaSize = width * height,
|
||||
chromaSize = lumaSize >> 2;
|
||||
webGLCanvas.YTexture.fill(buffer.subarray(0, lumaSize));
|
||||
webGLCanvas.UTexture.fill(buffer.subarray(lumaSize, lumaSize + chromaSize));
|
||||
webGLCanvas.VTexture.fill(buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize));
|
||||
webGLCanvas.drawScene();
|
||||
}
|
||||
|
||||
function setupCanvas(div) {
|
||||
var width = div.attributes.width ? div.attributes.width.value : 640,
|
||||
height = div.attributes.height ? div.attributes.height.value : 360,
|
||||
canvas = document.createElement('canvas');
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.style.backgroundColor = "#333333";
|
||||
div.appendChild(canvas);
|
||||
|
||||
webGLCanvas = new YUVWebGLCanvas(canvas, new Size(width, height));
|
||||
}
|
||||
|
||||
|
||||
NS = function (div) {
|
||||
setupCanvas(div);
|
||||
setupAvc();
|
||||
|
||||
socket = new WebSocket(
|
||||
'ws://' +
|
||||
window.document.location.hostname + ':' +
|
||||
window.document.location.port
|
||||
);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
socket.onmessage = handleNalUnits;
|
||||
};
|
||||
|
||||
window.NodecopterStream = NS;
|
||||
|
||||
}(window, document, undefined));
|
||||
externo
-283
@@ -1,283 +0,0 @@
|
||||
/**
|
||||
* Requires: avc-codec.js
|
||||
**/
|
||||
|
||||
assert (Module);
|
||||
|
||||
HEAP8 = Module.HEAP8;
|
||||
HEAPU8 = Module.HEAPU8;
|
||||
HEAP16 = Module.HEAP16;
|
||||
HEAP32 = Module.HEAP32;
|
||||
_h264bsdClip = Module._get_h264bsdClip();
|
||||
|
||||
var Avc = (function avc() {
|
||||
const MAX_STREAM_BUFFER_LENGTH = 1024 * 1024;
|
||||
|
||||
function constructor() {
|
||||
Module._broadwayInit();
|
||||
this.streamBuffer = toU8Array(Module._broadwayCreateStream(MAX_STREAM_BUFFER_LENGTH), MAX_STREAM_BUFFER_LENGTH);
|
||||
this.pictureBuffers = {};
|
||||
|
||||
this.onPictureDecoded = function (buffer, width, height) {
|
||||
// console.info(buffer.length);
|
||||
}
|
||||
|
||||
Module.patch(null, "_broadwayOnHeadersDecoded", function () {
|
||||
|
||||
});
|
||||
|
||||
|
||||
Module.patch(null, "_broadwayOnPictureDecoded", function ($buffer, width, height) {
|
||||
var buffer = this.pictureBuffers[$buffer];
|
||||
if (!buffer) {
|
||||
buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2);
|
||||
}
|
||||
this.onPictureDecoded(buffer, width, height);
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a typed array from a HEAP8 pointer.
|
||||
*/
|
||||
function toU8Array(ptr, length) {
|
||||
return HEAPU8.subarray(ptr, ptr + length);
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
/**
|
||||
* Decodes a stream buffer. This may be one single (unframed) NAL unit without the
|
||||
* start code, or a sequence of NAL units with framing start code prefixes. This
|
||||
* function overwrites stream buffer allocated by the codec with the supplied buffer.
|
||||
*/
|
||||
decode: function decode(buffer) {
|
||||
// console.info("Decoding: " + buffer.length);
|
||||
this.streamBuffer.set(buffer);
|
||||
Module._broadwaySetStreamLength(buffer.length);
|
||||
Module._broadwayPlayStream();
|
||||
},
|
||||
configure: function (config) {
|
||||
patchOptimizations(config, patches);
|
||||
console.info("Broadway Configured: " + JSON.stringify(config));
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
})();
|
||||
|
||||
function patchOptimizations(config, patches) {
|
||||
var scope = getGlobalScope();
|
||||
for (var name in patches) {
|
||||
var patch = patches[name];
|
||||
if (patch) {
|
||||
var option = config[name];
|
||||
if (!option) option = "original";
|
||||
console.info(name + ": " + option);
|
||||
assert (option in patch.options);
|
||||
var fn = patch.options[option].fn;
|
||||
if (fn) {
|
||||
scope[patch.original] = Module.patch(null, patch.name, fn);
|
||||
console.info("Patching: " + patch.name + ", with: " + option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var patches = {
|
||||
"filter": {
|
||||
name: "_h264bsdFilterPicture",
|
||||
display: "Filter Picture",
|
||||
original: "Original_h264bsdFilterPicture",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
}
|
||||
},
|
||||
"filterHorLuma": {
|
||||
name: "_FilterHorLuma",
|
||||
display: "Filter Hor Luma",
|
||||
original: "OriginalFilterHorLuma",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedFilterHorLuma}
|
||||
}
|
||||
},
|
||||
"filterVerLumaEdge": {
|
||||
name: "_FilterVerLumaEdge",
|
||||
display: "Filter Ver Luma Edge",
|
||||
original: "OriginalFilterVerLumaEdge",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedFilterVerLumaEdge}
|
||||
}
|
||||
},
|
||||
"getBoundaryStrengthsA": {
|
||||
name: "_GetBoundaryStrengthsA",
|
||||
display: "Get Boundary Strengths",
|
||||
original: "OriginalGetBoundaryStrengthsA",
|
||||
options: {
|
||||
none: {display: "None", fn: function () {}},
|
||||
original: {display: "Original", fn: null},
|
||||
optimized: {display: "Optimized", fn: OptimizedGetBoundaryStrengthsA}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getGlobalScope() {
|
||||
return function () { return this; }.call(null);
|
||||
}
|
||||
|
||||
/* Optimizations */
|
||||
|
||||
function clip(x, y, z) {
|
||||
return z < x ? x : (z > y ? y : z);
|
||||
}
|
||||
|
||||
function OptimizedGetBoundaryStrengthsA($mb, $bS) {
|
||||
var $totalCoeff = $mb + 28;
|
||||
|
||||
var tc0 = HEAP16[$totalCoeff + 0 >> 1];
|
||||
var tc1 = HEAP16[$totalCoeff + 2 >> 1];
|
||||
var tc2 = HEAP16[$totalCoeff + 4 >> 1];
|
||||
var tc3 = HEAP16[$totalCoeff + 6 >> 1];
|
||||
var tc4 = HEAP16[$totalCoeff + 8 >> 1];
|
||||
var tc5 = HEAP16[$totalCoeff + 10 >> 1];
|
||||
var tc6 = HEAP16[$totalCoeff + 12 >> 1];
|
||||
var tc7 = HEAP16[$totalCoeff + 14 >> 1];
|
||||
var tc8 = HEAP16[$totalCoeff + 16 >> 1];
|
||||
var tc9 = HEAP16[$totalCoeff + 18 >> 1];
|
||||
var tc10 = HEAP16[$totalCoeff + 20 >> 1];
|
||||
var tc11 = HEAP16[$totalCoeff + 22 >> 1];
|
||||
var tc12 = HEAP16[$totalCoeff + 24 >> 1];
|
||||
var tc13 = HEAP16[$totalCoeff + 26 >> 1];
|
||||
var tc14 = HEAP16[$totalCoeff + 28 >> 1];
|
||||
var tc15 = HEAP16[$totalCoeff + 30 >> 1];
|
||||
|
||||
HEAP32[$bS + 32 >> 2] = tc2 || tc0 ? 2 : 0;
|
||||
HEAP32[$bS + 40 >> 2] = tc3 || tc1 ? 2 : 0;
|
||||
HEAP32[$bS + 48 >> 2] = tc6 || tc4 ? 2 : 0;
|
||||
HEAP32[$bS + 56 >> 2] = tc7 || tc5 ? 2 : 0;
|
||||
HEAP32[$bS + 64 >> 2] = tc8 || tc2 ? 2 : 0;
|
||||
HEAP32[$bS + 72 >> 2] = tc9 || tc3 ? 2 : 0;
|
||||
HEAP32[$bS + 80 >> 2] = tc12 || tc6 ? 2 : 0;
|
||||
HEAP32[$bS + 88 >> 2] = tc13 || tc7 ? 2 : 0;
|
||||
HEAP32[$bS + 96 >> 2] = tc10 || tc8 ? 2 : 0;
|
||||
HEAP32[$bS + 104 >> 2] = tc11 || tc9 ? 2 : 0;
|
||||
HEAP32[$bS + 112 >> 2] = tc14 || tc12 ? 2 : 0;
|
||||
HEAP32[$bS + 120 >> 2] = tc15 || tc13 ? 2 : 0;
|
||||
|
||||
HEAP32[$bS + 12 >> 2] = tc1 || tc0 ? 2 : 0;
|
||||
HEAP32[$bS + 20 >> 2] = tc4 || tc1 ? 2 : 0;
|
||||
HEAP32[$bS + 28 >> 2] = tc5 || tc4 ? 2 : 0;
|
||||
HEAP32[$bS + 44 >> 2] = tc3 || tc2 ? 2 : 0;
|
||||
HEAP32[$bS + 52 >> 2] = tc6 || tc3 ? 2 : 0;
|
||||
HEAP32[$bS + 60 >> 2] = tc7 || tc6 ? 2 : 0;
|
||||
HEAP32[$bS + 76 >> 2] = tc9 || tc8 ? 2 : 0;
|
||||
HEAP32[$bS + 84 >> 2] = tc12 || tc9 ? 2 : 0;
|
||||
HEAP32[$bS + 92 >> 2] = tc13 || tc12 ? 2 : 0;
|
||||
HEAP32[$bS + 108 >> 2] = tc11 || tc10 ? 2 : 0;
|
||||
HEAP32[$bS + 116 >> 2] = tc14 || tc11 ? 2 : 0;
|
||||
HEAP32[$bS + 124 >> 2] = tc15 || tc14 ? 2 : 0;
|
||||
}
|
||||
|
||||
function OptimizedFilterVerLumaEdge ($data, bS, $thresholds, imageWidth) {
|
||||
var delta, tc, tmp;
|
||||
var p0, q0, p1, q1, p2, q2;
|
||||
var tmpFlag;
|
||||
var $clp = _h264bsdClip + 512;
|
||||
var alpha = HEAP32[$thresholds + 4 >> 2];
|
||||
var beta = HEAP32[$thresholds + 8 >> 2];
|
||||
var val;
|
||||
|
||||
if (bS < 4) {
|
||||
tmp = tc = HEAPU8[HEAP32[$thresholds >> 2] + (bS - 1)] & 255;
|
||||
for (var i = 4; i > 0; i--) {
|
||||
p1 = HEAPU8[$data + -2] & 255;
|
||||
p0 = HEAPU8[$data + -1] & 255;
|
||||
q0 = HEAPU8[$data] & 255;
|
||||
q1 = HEAPU8[$data + 1] & 255;
|
||||
if ((Math.abs(p0 - q0) < alpha) && (Math.abs(p1 - p0) < beta) && (Math.abs(q1 - q0) < beta)) {
|
||||
p2 = HEAPU8[$data - 3] & 255;
|
||||
if (Math.abs(p2 - p0) < beta) {
|
||||
val = (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1;
|
||||
HEAP8[$data - 2] = p1 + clip(-tc, tc, val);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
q2 = HEAPU8[$data + 2] & 255;
|
||||
if (Math.abs(q2 - q0) < beta) {
|
||||
val = (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1;
|
||||
HEAP8[$data + 1] = (q1 + clip(-tc, tc, val));
|
||||
tmp++;
|
||||
}
|
||||
|
||||
val = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
|
||||
delta = clip(-tmp, tmp, val);
|
||||
|
||||
p0 = HEAPU8[$clp + (p0 + delta)] & 255;
|
||||
q0 = HEAPU8[$clp + (q0 - delta)] & 255;
|
||||
tmp = tc;
|
||||
HEAP8[$data - 1] = p0;
|
||||
HEAP8[$data] = q0;
|
||||
|
||||
$data += imageWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OriginalFilterVerLumaEdge($data, bS, $thresholds, imageWidth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all four successive horizontal 4-pixel luma edges. This can be done when bS is equal to all four edges.
|
||||
*/
|
||||
function OptimizedFilterHorLuma ($data, bS, $thresholds, imageWidth) {
|
||||
var delta, tc, tmp;
|
||||
var p0, q0, p1, q1, p2, q2;
|
||||
var tmpFlag;
|
||||
var $clp = _h264bsdClip + 512;
|
||||
var alpha = HEAP32[$thresholds + 4 >> 2];
|
||||
var beta = HEAP32[$thresholds + 8 >> 2];
|
||||
var val;
|
||||
|
||||
if (bS < 4) {
|
||||
tmp = tc = HEAPU8[HEAP32[$thresholds >> 2] + (bS - 1)] & 255;
|
||||
for (var i = 16; i > 0; i--) {
|
||||
p1 = HEAPU8[$data + (-imageWidth << 1)] & 255;
|
||||
p0 = HEAPU8[$data + -imageWidth] & 255;
|
||||
q0 = HEAPU8[$data] & 255;
|
||||
q1 = HEAPU8[$data + imageWidth] & 255;
|
||||
|
||||
if ((Math.abs(p0 - q0) < alpha) && (Math.abs(p1 - p0) < beta) && (Math.abs(q1 - q0) < beta)) {
|
||||
p2 = HEAPU8[$data + (-imageWidth * 3)] & 255;
|
||||
if (Math.abs(p2 - p0) < beta) {
|
||||
val = (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1;
|
||||
HEAP8[$data + (-imageWidth << 1)] = p1 + clip(-tc, tc, val);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
q2 = HEAPU8[$data + (imageWidth << 2)] & 255;
|
||||
if (Math.abs(q2 - q0) < beta) {
|
||||
val = (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1;
|
||||
HEAP8[$data + imageWidth] = (q1 + clip(-tc, tc, val));
|
||||
tmp++;
|
||||
}
|
||||
|
||||
val = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
|
||||
delta = clip(-tmp, tmp, val);
|
||||
|
||||
p0 = HEAPU8[$clp + (p0 + delta)] & 255;
|
||||
q0 = HEAPU8[$clp + (q0 - delta)] & 255;
|
||||
tmp = tc;
|
||||
HEAP8[$data - imageWidth] = p0;
|
||||
HEAP8[$data] = q0;
|
||||
|
||||
$data ++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OriginalFilterHorLuma($data, bS, $thresholds, imageWidth);
|
||||
}
|
||||
}
|
||||
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