Skip to content

Commit f10becd

Browse files
Ihor LutsykIhor Lutsyk
authored andcommitted
Added OpenCV and WebRTC integration sample app
1 parent 3bbdcba commit f10becd

27 files changed

+4805
-0
lines changed

src/webrtc/samples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ if(HAVE_WEBRTC)
44
add_subdirectory(webrtcrecorder)
55
endif()
66
add_subdirectory(webrtccapturer)
7+
add_subdirectory(opencvanalyzer)
78
endif()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
include_dependency(WebRTC REQUIRED)
2+
include_dependency(OpenCV REQUIRED)
3+
4+
define_sourcey_module_sample(opencvanalyzer base crypto av net http util json socketio symple webrtc)
5+
6+
configure_file(network/mobilenetssd.caffemodel network/mobilenetssd.caffemodel COPYONLY)
7+
8+
configure_file(network/mobilenetssd.prototxt.txt network/mobilenetssd.prototxt.txt COPYONLY)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# WebRTC OpencCV Analyzer
2+
3+
This sample app showcases how to pass live WebRTC video stream to OpenCV to perform object detection.
4+
Demo has next actors:
5+
1. Client - web browser, which is WebRTC source and video player as well (local loopback)
6+
1. Libsourcey server app that connects as WebRTC peer to client's stream.
7+
8+
Libsourcey server app acts as WebRTC listener, OpenCV processor and WebSocket server that sends objects detection data back to a client.
9+
The client app draws received object detections (through separate WebSocket channel) using js to render detections over video loopback HTML element.
10+
This is also interesting demo how to avoid delays in video stream and deliver analytics by separate channel, without extra encoding on server side.
11+
12+
## Installation
13+
14+
1. Compile LibSourcey with WebRTC and OpenCV support (`WITH_WEBRTC=ON WITH_OPENCV=ON`) and samples (`BUILD_SAMPLES_webrtc=ON`) enabled. See the main README for help: https://github.com/sourcey/libsourcey#installing-on-linux
15+
1. Run the `opencvanalyzer` sample application from the `build` directory.
16+
You can rewrite default network settings by arguments, run `opencvanalyzer -h` for help dialog.
17+
1. Install and run the Node.js client application:
18+
19+
~~~ bash
20+
cd <libsourcey>/src/webrtc/samples/opencvanalyzer/client
21+
npm install
22+
node app
23+
~~~
24+
25+
4. Finally, point your browser to: `http://localhost:4499`
26+
27+
IMPORTANT NOTICE!
28+
Chrome: enable mixed content: `chrome --allow-running-insecure-content`
29+
Firefox: this fix should be applied, until it is not merged to upstream project:
30+
https://github.com/sourcey/libsourcey/pull/205
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
/// Setup the Symple server
3+
var fs = require('fs');
4+
var Symple = require('symple');
5+
var sy = new Symple();
6+
sy.loadConfig(__dirname + '/symple.json'); // see symple.json for options
7+
sy.init();
8+
console.log('Symple server listening on port ' + sy.config.port);
9+
10+
11+
//
12+
/// Setup the demo client web server
13+
14+
var express = require('express'),
15+
path = require('path'),
16+
app = express(),
17+
serverPort = parseInt(sy.config.port)
18+
clientPort = serverPort - 1;
19+
20+
app.set('port', clientPort);
21+
app.set('view engine', 'ejs');
22+
app.set('views', __dirname + '/');
23+
app.use(express.static(__dirname + '/assets'));
24+
25+
26+
app.get('/', function (req, res) {
27+
res.render('index', {
28+
port: serverPort,
29+
peer: {
30+
user: 'demo',
31+
name: 'Demo User',
32+
group: 'public'
33+
}
34+
});
35+
});
36+
37+
app.listen(app.get('port'), function () {
38+
console.log('Web server listening on port ' + app.get('port'));
39+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
var canvas, ctx;
2+
// hardcoded value for video of 640x480, TODO: make it generic
3+
var detectionSize = 480;
4+
var webSocketHost = 'ws://0.0.0.0:1234/';
5+
var clearingTimeout;
6+
function init() {
7+
// init canvas
8+
canvas = document.getElementById("detectionCanvas");
9+
ctx = canvas.getContext("2d");
10+
ctx.lineWidth="3";
11+
ctx.strokeStyle="red";
12+
ctx.font="18px Georgia";
13+
ctx.fillStyle = "red";
14+
15+
// init websocket
16+
initWebSocketConnection();
17+
}
18+
19+
function initWebSocketConnection() {
20+
websocket = new WebSocket(webSocketHost);
21+
websocket.onopen = function() { console.log('WebSocket Data Stream Connected'); };
22+
websocket.onmessage = function(evt) {
23+
console.log('Detection Data Received:', evt.data);
24+
drawDetections(JSON.parse(evt.data));
25+
};
26+
websocket.onerror = function(evt) {
27+
console.log('WebSocket ERROR: ' + evt.data);
28+
29+
// close websocket if opened
30+
if(websocket.readyState < 2){
31+
websocket.close();
32+
}
33+
34+
// try to reinitialize websocket connection in one second
35+
setTimeout(initWebSocketConnection, 1000);
36+
};
37+
}
38+
39+
function drawDetections(data) {
40+
// remote handler for clearing data if there are no detections from server
41+
if (clearingTimeout){
42+
clearTimeout(clearingTimeout);
43+
}
44+
45+
// clear previous drawings
46+
clearDetections();
47+
48+
//draw detections one by one
49+
ctx.beginPath();
50+
for(var i=0; i<data.detections.length; i++) {
51+
var detection = data.detections[i];
52+
53+
ctx.rect(detection.box.leftBottomX + 10,
54+
detection.box.leftBottomY - 10,
55+
detection.box.rightTopX - detection.box.leftBottomX - 30,
56+
detection.box.rightTopY - detection.box.leftBottomY - 10);
57+
ctx.fillText(detection.class + ' ' + detection.confidence,
58+
detection.box.leftBottomX + 15, detection.box.leftBottomY + 15);
59+
ctx.stroke();
60+
}
61+
ctx.closePath();
62+
63+
// set timer for clearing previous detection drawings
64+
// if there are no new detections from server
65+
clearingTimeout = setTimeout(clearDetections, 1000);
66+
}
67+
68+
function clearDetections() {
69+
ctx.clearRect(0, 0, canvas.width, canvas.height);
70+
}
71+
window.addEventListener("load", init, false);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
body > .container {
2+
padding-top: 30px;
3+
}
4+
5+
#webrtc-video .video-player {
6+
margin-bottom: 1rem;
7+
}
8+
9+
#webrtc-video .symple-player {
10+
min-height: 480px;
11+
}
12+
13+
/*This is hardcoded value for video of 640x480, TODO: make it generic*/
14+
#detectionCanvas {
15+
position:absolute;
16+
z-index: 1;
17+
margin-left:-240px;
18+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
$(document).ready(function() {
2+
var client, player, remotePeer, initialized = false;
3+
4+
//
5+
// Initialize the Symple WebRTC player
6+
7+
player = new Symple.Player({
8+
element: '#webrtc-video .video-player',
9+
engine: 'WebRTC',
10+
initiator: true,
11+
rtcConfig: WEBRTC_CONFIG,
12+
iceMediaConstraints: {
13+
'mandatory': {
14+
'OfferToReceiveAudio': false,
15+
'OfferToReceiveVideo': false
16+
}
17+
},
18+
onStateChange: function(player, state) {
19+
player.displayStatus(state);
20+
}
21+
});
22+
23+
function startPlaybackAndRecording() {
24+
// player.setup();
25+
player.play(); //{ localMedia: true, disableAudio: false, disableVideo: false }
26+
player.engine.sendLocalSDP = function(desc) {
27+
console.log('Send offer:', JSON.stringify(desc));
28+
client.send({
29+
to: remotePeer,
30+
type: 'message',
31+
offer: desc
32+
});
33+
};
34+
35+
player.engine.sendLocalCandidate = function(cand) {
36+
client.send({
37+
to: remotePeer,
38+
type: 'message',
39+
candidate: cand
40+
});
41+
}
42+
}
43+
44+
//
45+
// Initialize the Symple client
46+
47+
client = new Symple.Client(CLIENT_OPTIONS);
48+
49+
client.on('announce', function(peer) {
50+
console.log('Authentication success:', peer);
51+
});
52+
53+
client.on('addPeer', function(peer) {
54+
console.log('Adding peer:', peer);
55+
56+
if (peer.user == 'videorecorder' &&
57+
!initialized) {
58+
initialized = true;
59+
remotePeer = peer; //m.from;
60+
startPlaybackAndRecording();
61+
}
62+
});
63+
64+
client.on('removePeer', function(peer) {
65+
console.log('Removing peer:', peer);
66+
});
67+
68+
client.on('message', function(m) {
69+
// console.log('Recv message:', m)
70+
if (remotePeer && remotePeer.id != m.from.id) {
71+
console.log('Dropping message from unknown peer', m);
72+
return;
73+
}
74+
if (m.offer) {
75+
alert('Unexpected offer for one-way streaming');
76+
}
77+
else if (m.answer) {
78+
console.log('Reieve answer:', JSON.stringify(m.answer));
79+
player.engine.recvRemoteSDP(m.answer);
80+
}
81+
else if (m.candidate) {
82+
player.engine.recvRemoteCandidate(m.candidate);
83+
}
84+
});
85+
86+
client.on('disconnect', function() {
87+
console.log('Disconnected from server')
88+
});
89+
90+
client.on('error', function(error, message) {
91+
console.log('Peer error:', error, message)
92+
});
93+
94+
client.connect();
95+
});

0 commit comments

Comments
 (0)