Skip to content

Commit

Permalink
Added camera toggle
Browse files Browse the repository at this point in the history
  • Loading branch information
Finchiedev committed Oct 30, 2018
1 parent 7bd8a0a commit 939496f
Show file tree
Hide file tree
Showing 40 changed files with 7,766 additions and 3 deletions.
3 changes: 3 additions & 0 deletions App/JS/Resources/Player/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
public/_main.js

50 changes: 50 additions & 0 deletions App/JS/Resources/Player/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Motivation

This is a very simple h264 video player (that can run on live stream) for your browser.
You might use this with raspicam raw h264 stream.
This is a player around [Broadway](https://github.com/mbebenita/Broadway) Decoder, with very simple API.
NAL unit (h264 frames) are split on the server side, transported using websocket, and sent to the decoded (with frame dropping, if necessary)

[![Version](https://img.shields.io/npm/v/h264-live-player.svg)](https://www.npmjs.com/package/h264-live-player)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)


# History
* I was targetting a real-time camera video feedback (no audio/surveillance cam) in the browser
* There is no solution for "real time" mp4 video creation / playback (ffmpeg, mp4box.js, mp4parser - _boxing_ _takes_ _time_)
* Media Source Extension is a dead end (mp4 boxing is far too hard to re-create on the client side)
* [Broadway](https://github.com/mbebenita/Broadway) provide the crazy emscripten/asm build of a h264 encoder accelerated by webGL canvas
* Here is all the glue we need, enjoy ;-)


# Installation/demo
```
git clone [email protected]:131/h264-live-player.git player
cd player
npm install
node server-rpi.js # run on a rpi for a webcam demo
node server-static.js # for sample video (static) file delivery
node server-tcp.js # for a remote tcp (rpi video feed) sample
node server-ffmpeg # usefull on win32 to debug the live feed (use ffmpeg & your directshow device / webcam)
# browse to http://127.0.0.1:8080/ for a demo player
```

# Recommendations
* Broadway h264 Decoder can only work with **h264 baseline profile**
* [**Use a SANE birate**](https://www.dr-lex.be/info-stuff/videocalc.html)
* Browserify FTW
* Once you understand how to integrate the server-side, feel free to use [h264-live-player](https://www.npmjs.com/package/h264-live-player) npm package in your client side app (see vendor/)
* Use [uws](https://github.com/uWebSockets/uWebSockets) (instead of ws) as websocket server


# Credits
* [131](mailto:[email protected])
* [Broadway](https://github.com/mbebenita/Broadway)
* [urbenlegend/WebStreamer](https://github.com/urbenlegend/WebStreamer)


# Keywords / shout box
raspberry, mp4box, h264, nal, raspivid, mse, media source extension, iso, raspicam, bitrate, realtime, video, mp4, ffmpeg, websocket, ws, socket.io "Let's have a beer and talk in Paris"
88 changes: 88 additions & 0 deletions App/JS/Resources/Player/lib/_server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use strict";


const WebSocketServer = require('ws').Server;
const Splitter = require('stream-split');
const merge = require('mout/object/merge');

const NALseparator = new Buffer([0,0,0,1]);//NAL break


class _Server {

constructor(server, options) {

this.options = merge({
width : 960,
height: 540,
}, options);

this.wss = new WebSocketServer({ server });

this.new_client = this.new_client.bind(this);
this.start_feed = this.start_feed.bind(this);
this.broadcast = this.broadcast.bind(this);

this.wss.on('connection', this.new_client);
}


start_feed() {
var readStream = this.get_feed();
this.readStream = readStream;

readStream = readStream.pipe(new Splitter(NALseparator));
readStream.on("data", this.broadcast);
}

get_feed() {
throw new Error("to be implemented");
}

broadcast(data) {
this.wss.clients.forEach(function(socket) {

if(socket.buzy)
return;

socket.buzy = true;
socket.buzy = false;

socket.send(Buffer.concat([NALseparator, data]), { binary: true}, function ack(error) {
socket.buzy = false;
});
});
}

new_client(socket) {

var self = this;
console.log('New guy');

socket.send(JSON.stringify({
action : "init",
width : this.options.width,
height : this.options.height,
}));

socket.on("message", function(data){
var cmd = "" + data, action = data.split(' ')[0];
console.log("Incomming action '%s'", action);

if(action == "REQUESTSTREAM")
self.start_feed();
if(action == "STOPSTREAM")
self.readStream.pause();
});

socket.on('close', function() {
self.readStream.end();
console.log('stopping client interval');
});
}


};


module.exports = _Server;
68 changes: 68 additions & 0 deletions App/JS/Resources/Player/lib/ffmpeg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use strict";


const spawn = require('child_process').spawn;
const merge = require('mout/object/merge');

const Server = require('./_server');


class FFMpegServer extends Server {

constructor(server, opts) {
super(server, merge({
fps : 15,
}, opts));
}

get_feed() {

var args = [
"-f", "gdigrab",
"-framerate", this.options.fps,
"-offset_x", 10,
"-offset_y", 20,
"-video_size", this.options.width + 'x' + this.options.height,
'-i', 'desktop',
'-pix_fmt', 'yuv420p',
'-c:v', 'libx264',
'-vprofile', 'baseline',
'-tune', 'zerolatency',
'-f' ,'rawvideo',
'-'
];

//https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
var args = [
"-f", "dshow",
"-i", "video=Integrated Webcam" ,
"-framerate", this.options.fps,
"-video_size", this.options.width + 'x' + this.options.height,
'-pix_fmt', 'yuv420p',
'-c:v', 'libx264',
'-b:v', '600k',
'-bufsize', '600k',
'-vprofile', 'baseline',
'-tune', 'zerolatency',
'-f' ,'rawvideo',
'-'
];


console.log("ffmpeg " + args.join(' '));
var streamer = spawn('ffmpeg', args);
//streamer.stderr.pipe(process.stderr);

streamer.on("exit", function(code){
console.log("Failure", code);
});

return streamer.stdout;
}

};


module.exports = FFMpegServer;


34 changes: 34 additions & 0 deletions App/JS/Resources/Player/lib/raspivid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use strict";

const util = require('util');
const spawn = require('child_process').spawn;
const merge = require('mout/object/merge');

const Server = require('./_server');


class RpiServer extends Server {

constructor(server, opts) {
super(server, merge({
fps : 12,
}, opts));
}

get_feed() {
var msk = "raspivid -t 0 -o - -w %d -h %d -fps %d";
var cmd = util.format(msk, this.options.width, this.options.height, this.options.fps);
console.log(cmd);
var streamer = spawn('raspivid', ['-t', '0', '-o', '-', '-w', this.options.width, '-h', this.options.height, '-fps', this.options.fps, '-pf', 'baseline']);
streamer.on("exit", function(code){
console.log("Failure", code);
});

return streamer.stdout;
}

};



module.exports = RpiServer;
30 changes: 30 additions & 0 deletions App/JS/Resources/Player/lib/remotetcpfeed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use strict";

const net = require('net');
const merge = require('mout/object/merge');

const Server = require('./_server');

class TCPFeed extends Server {

constructor(server, opts) {
super(server, merge({
feed_ip : '127.0.0.1',
feed_port : 5001,
}, opts));
}

get_feed() {

var readStream = net.connect(this.options.feed_port, this.options.feed_ip, function(){
console.log("remote stream ready");
});

return readStream;
}

}



module.exports = TCPFeed;
37 changes: 37 additions & 0 deletions App/JS/Resources/Player/lib/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use strict";

const fs = require('fs');
const Throttle = require('stream-throttle').Throttle;
const merge = require('mout/object/merge');

const Server = require('./_server');

class StaticFeed extends Server {

constructor(server, opts) {
super(server, merge({
video_path : null,
video_duration : 0,
}, opts));
}

get_feed() {
var source = this.options.video_path;

//throttle for "real time simulation"
var sourceThrottleRate = Math.floor(fs.statSync(source)['size'] / this.options.video_duration);
console.log("Generate a throttle rate of %s kBps", Math.floor(sourceThrottleRate/1024));

var readStream = fs.createReadStream(source);
readStream = readStream.pipe(new Throttle({rate: sourceThrottleRate}));

console.log("Generate a static feed from ", source);
return readStream;
}

}




module.exports = StaticFeed;
22 changes: 22 additions & 0 deletions App/JS/Resources/Player/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "h264-live-stream-demo-server",
"private": true,
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"install": "cd vendor && npm install",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^3.21.2",
"mout": "^1.0.0",
"stream-split": "^1.1.0",
"stream-throttle": "^0.1.3",
"ws": "^0.8.0"
},
"devDependencies": {}
}
32 changes: 32 additions & 0 deletions App/JS/Resources/Player/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<html>
<head>
<title>h264-live-player web client demo</title>
</head>
<body>

<button type="button" onclick="wsavc.playStream()">Start Video</button>
<button type="button" onclick="wsavc.stopStream()">Stop Video</button>
<button type="button" onclick="wsavc.disconnect()">Disconnect</button>
<br/>
<br/>
<br/>

<!-- provide WSAvcPlayer -->
<script type="text/javascript" src="http-live-player.js">;</script>
<script type="text/javascript">

var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

// Create h264 player
var uri = "ws://" + document.location.host;
var wsavc = new WSAvcPlayer(canvas, "webgl", 1, 35);
wsavc.connect(uri);


//expose instance for button callbacks
window.wsavc = wsavc;

</script>
</body>
</html>
Binary file added App/JS/Resources/Player/samples/admiral.264
Binary file not shown.
Binary file added App/JS/Resources/Player/samples/out.h264
Binary file not shown.
Loading

0 comments on commit 939496f

Please sign in to comment.