Merge branch 'master' of https://git.xpub.nl/XPUB/SI21
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="https://p5livemedia.itp.io/simplepeer.min.js"></script>
|
||||||
|
<script type="text/javascript" src="https://p5livemedia.itp.io/socket.io.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/addons/p5.sound.min.js"></script>
|
||||||
|
<script type="text/javascript" src="https://p5livemedia.itp.io/p5livemedia.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="sketch.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,380 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @class p5LiveMedia
|
||||||
|
* @constructor
|
||||||
|
* @param {p5.sketch} [something] blah blah blah.
|
||||||
|
* @param {p5LiveMedia.MEDIA TYPE}
|
||||||
|
* @param {WebRTC stream}
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
function setup() {
|
||||||
|
// Stream Audio/Video
|
||||||
|
createCanvas(400, 300);
|
||||||
|
// For A/V streams, we need to use the createCapture callback method to get the "stream" object
|
||||||
|
video = createCapture(VIDEO, function(stream) {
|
||||||
|
let p5lm = new p5LiveMedia(this,"CAPTURE",stream)
|
||||||
|
p5lm.on('stream', gotStream);
|
||||||
|
p5lm.on('data', gotData);
|
||||||
|
p5lm.on('disconnect', gotDisconnect);
|
||||||
|
});
|
||||||
|
video.muted = true;
|
||||||
|
video.hide();
|
||||||
|
|
||||||
|
// OR //
|
||||||
|
|
||||||
|
// Stream Canvas as Video
|
||||||
|
let c = createCanvas(400, 300);
|
||||||
|
video = createCapture(VIDEO);
|
||||||
|
video.muted = true;
|
||||||
|
video.hide();
|
||||||
|
let p5lm = new p5LiveMedia(this,"CANVAS",c);
|
||||||
|
p5lm.on('stream', gotStream);
|
||||||
|
p5lm.on('data', gotData);
|
||||||
|
p5lm.on('disconnect', gotDisconnect);
|
||||||
|
|
||||||
|
|
||||||
|
// OR //
|
||||||
|
|
||||||
|
// Just Data
|
||||||
|
createCanvas(400, 300);
|
||||||
|
let p5lm = new p5LiveMedia(this,"DATA");
|
||||||
|
p5lm.on('data', gotData);
|
||||||
|
p5lm.on('disconnect', gotDisconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
image(video,0,0,width/2,height);
|
||||||
|
ellipse(mouseX,mouseY,100,100);
|
||||||
|
if (ovideo != null) {
|
||||||
|
rect(10,10,10,10);
|
||||||
|
image(ovideo,width/2,0,width/2,height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We got a new stream!
|
||||||
|
function gotStream(stream, id) {
|
||||||
|
print("New Stream from " + id);
|
||||||
|
// This is just like a video/stream from createCapture(VIDEO)
|
||||||
|
ovideo = stream;
|
||||||
|
//ovideo.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotData(data, id) {
|
||||||
|
print("New Data from " + id);
|
||||||
|
// Got some data from a peer
|
||||||
|
print(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotDisconnect(id) {
|
||||||
|
print(id + " disconnected");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
class p5LiveMedia {
|
||||||
|
|
||||||
|
constructor(sketch, type, elem, room, host) {
|
||||||
|
|
||||||
|
this.sketch = sketch;
|
||||||
|
//sketch.disableFriendlyErrors = true;
|
||||||
|
|
||||||
|
this.simplepeers = [];
|
||||||
|
this.mystream;
|
||||||
|
this.onStreamCallback;
|
||||||
|
this.onDataCallback;
|
||||||
|
this.onDisconnectCallback;
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
this.socket = io.connect("https://p5livemedia.itp.io/");
|
||||||
|
} else {
|
||||||
|
this.socket = io.connect(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log(elem.elt);
|
||||||
|
|
||||||
|
if (type == "CANVAS") {
|
||||||
|
this.mystream = elem.elt.captureStream(30);
|
||||||
|
} else if (type == "CAPTURE") {
|
||||||
|
this.mystream = elem;
|
||||||
|
} else {
|
||||||
|
// Assume it is just "DATA"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
//console.log("Socket Connected");
|
||||||
|
//console.log("My socket id: ", this.socket.id);
|
||||||
|
|
||||||
|
//console.log("***"+window.location.href);
|
||||||
|
|
||||||
|
// Sends back a list of users in the room
|
||||||
|
if (!room) {
|
||||||
|
this.socket.emit("room_connect", window.location.href);
|
||||||
|
} else {
|
||||||
|
this.socket.emit("room_connect", room);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('disconnect', (data) => {
|
||||||
|
// console.log("Socket disconnected");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('peer_disconnect', (data) => {
|
||||||
|
//console.log("simplepeer has disconnected " + data);
|
||||||
|
for (let i = 0; i < this.simplepeers.length; i++) {
|
||||||
|
if (this.simplepeers[i].socket_id == data) {
|
||||||
|
//console.log("Removed the DOM Element if it exits");
|
||||||
|
this.removeDomElement(this.simplepeers[i]);
|
||||||
|
//console.log("Removing simplepeer: " + i);
|
||||||
|
this.simplepeers.splice(i,1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.callOnDisconnectCallback(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Receive listresults from server
|
||||||
|
this.socket.on('listresults', (data) => {
|
||||||
|
//console.log(data);
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
// Make sure it's not us
|
||||||
|
if (data[i] != this.socket.id) {
|
||||||
|
|
||||||
|
// create a new simplepeer and we'll be the "initiator"
|
||||||
|
let simplepeer = new SimplePeerWrapper(this,
|
||||||
|
true, data[i], this.socket, this.mystream
|
||||||
|
);
|
||||||
|
|
||||||
|
// Push into our array
|
||||||
|
this.simplepeers.push(simplepeer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('signal', (to, from, data) => {
|
||||||
|
|
||||||
|
//console.log("Got a signal from the server: ", to, from, data);
|
||||||
|
|
||||||
|
// // to should be us
|
||||||
|
// if (to != this.socket.id) {
|
||||||
|
// console.log("Socket IDs don't match");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Look for the right simplepeer in our array
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < this.simplepeers.length; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (this.simplepeers[i].socket_id == from) {
|
||||||
|
//console.log("Found right object");
|
||||||
|
// Give that simplepeer the signal
|
||||||
|
this.simplepeers[i].inputsignal(data);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
//console.log("Never found right simplepeer object");
|
||||||
|
// Let's create it then, we won't be the "initiator"
|
||||||
|
let simplepeer = new SimplePeerWrapper(this,
|
||||||
|
false, from, this.socket, this.mystream
|
||||||
|
);
|
||||||
|
|
||||||
|
// Push into our array
|
||||||
|
this.simplepeers.push(simplepeer);
|
||||||
|
|
||||||
|
// Tell the new simplepeer that signal
|
||||||
|
simplepeer.inputsignal(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// // use this to add a track to a stream - assuming this is a stream, it will have to extract the track out
|
||||||
|
// addtrack(stream, type) {
|
||||||
|
// if (type == "CANVAS") {
|
||||||
|
// this.mystream = elem.elt.captureStream(30);
|
||||||
|
// } else if (type == "CAPTURE") {
|
||||||
|
// this.mystream = elem;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
send(data) {
|
||||||
|
for (let i = 0; i < this.simplepeers.length; i++) {
|
||||||
|
if (this.simplepeers[i] != null) {
|
||||||
|
this.simplepeers[i].send(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on(event, callback) {
|
||||||
|
if (event == 'stream') {
|
||||||
|
this.onStream(callback);
|
||||||
|
} else if (event == 'data') {
|
||||||
|
this.onData(callback);
|
||||||
|
} else if (event == "disconnect") {
|
||||||
|
this.onDisconnect(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDisconnect(callback) {
|
||||||
|
this.onDisconnectCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStream(callback) {
|
||||||
|
this.onStreamCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
onData(callback) {
|
||||||
|
this.onDataCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
callOnDisconnectCallback(id) {
|
||||||
|
if (this.onDisconnectCallback) {
|
||||||
|
this.onDisconnectCallback(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callOnDataCallback(data, id) {
|
||||||
|
if (this.onDataCallback) {
|
||||||
|
this.onDataCallback(data, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDomElement(ssp) {
|
||||||
|
if (ssp.domElement) {
|
||||||
|
document.body.removeChild(ssp.domElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callOnStreamCallback(domElement, id) {
|
||||||
|
if (this.onStreamCallback) {
|
||||||
|
|
||||||
|
//////////////////////
|
||||||
|
// Copied from createCapture and addElement in p5.js source 10/12/2020
|
||||||
|
//const videoEl = addElement(domElement, this.sketch, true);
|
||||||
|
document.body.appendChild(domElement);
|
||||||
|
let videoEl = new p5.MediaElement(domElement, this.sketch);
|
||||||
|
this.sketch._elements.push(videoEl);
|
||||||
|
|
||||||
|
videoEl.loadedmetadata = false;
|
||||||
|
// set width and height onload metadata
|
||||||
|
domElement.addEventListener('loadedmetadata', function() {
|
||||||
|
domElement.play();
|
||||||
|
if (domElement.width) {
|
||||||
|
videoEl.width = domElement.width;
|
||||||
|
videoEl.height = domElement.height;
|
||||||
|
} else {
|
||||||
|
videoEl.width = videoEl.elt.width = domElement.videoWidth;
|
||||||
|
videoEl.height = videoEl.elt.height = domElement.videoHeight;
|
||||||
|
}
|
||||||
|
videoEl.loadedmetadata = true;
|
||||||
|
});
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
this.onStreamCallback(videoEl, id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//console.log("no onStreamCallback set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A wrapper for simplepeer as we need a bit more than it provides
|
||||||
|
class SimplePeerWrapper {
|
||||||
|
|
||||||
|
constructor(p5lm, initiator, socket_id, socket, stream) {
|
||||||
|
this.simplepeer = new SimplePeer({
|
||||||
|
initiator: initiator,
|
||||||
|
trickle: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.p5livemedia = p5lm;
|
||||||
|
|
||||||
|
// Their socket id, our unique id for them
|
||||||
|
this.socket_id = socket_id;
|
||||||
|
|
||||||
|
// Socket.io Socket
|
||||||
|
this.socket = socket;
|
||||||
|
|
||||||
|
// Are we connected?
|
||||||
|
this.connected = false;
|
||||||
|
|
||||||
|
// Our video stream
|
||||||
|
this.stream = stream;
|
||||||
|
|
||||||
|
// Dom Element
|
||||||
|
this.domElement = null;
|
||||||
|
|
||||||
|
// simplepeer generates signals which need to be sent across socket
|
||||||
|
this.simplepeer.on('signal', data => {
|
||||||
|
this.socket.emit('signal', this.socket_id, this.socket.id, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// When we have a connection, send our stream
|
||||||
|
this.simplepeer.on('connect', () => {
|
||||||
|
//console.log('simplepeer connection')
|
||||||
|
//console.log(this.simplepeer);
|
||||||
|
//p.send('whatever' + Math.random())
|
||||||
|
|
||||||
|
// We are connected
|
||||||
|
this.connected = true;
|
||||||
|
|
||||||
|
// Let's give them our stream, if we have a stream that is
|
||||||
|
if (stream != null) {
|
||||||
|
this.simplepeer.addStream(stream);
|
||||||
|
//console.log("Send our stream");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stream coming in to us
|
||||||
|
this.simplepeer.on('stream', stream => {
|
||||||
|
//console.log('Incoming Stream');
|
||||||
|
|
||||||
|
// This should really be a callback
|
||||||
|
|
||||||
|
// Create a video object
|
||||||
|
this.domElement = document.createElement("VIDEO");
|
||||||
|
this.domElement.id = this.socket_id;
|
||||||
|
this.domElement.srcObject = stream;
|
||||||
|
this.domElement.muted = false;
|
||||||
|
this.domElement.onloadedmetadata = function(e) {
|
||||||
|
e.target.play();
|
||||||
|
};
|
||||||
|
//document.body.appendChild(ovideo);
|
||||||
|
//console.log(this.domElement);
|
||||||
|
|
||||||
|
this.p5livemedia.callOnStreamCallback(this.domElement, this.socket_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.simplepeer.on('data', data => {
|
||||||
|
let stringData = String(data);
|
||||||
|
|
||||||
|
this.p5livemedia.callOnDataCallback(stringData, this.socket_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.simplepeer.on('error', (err) => {
|
||||||
|
// ERR_WEBRTC_SUPPORT
|
||||||
|
// ERR_CREATE_OFFER
|
||||||
|
// ERR_CREATE_ANSWER
|
||||||
|
// ERR_SET_LOCAL_DESCRIPTION
|
||||||
|
// ERR_SET_REMOTE_DESCRIPTION
|
||||||
|
// ERR_ADD_ICE_CANDIDATE
|
||||||
|
// ERR_ICE_CONNECTION_FAILURE
|
||||||
|
// ERR_SIGNALING
|
||||||
|
// ERR_DATA_CHANNEL
|
||||||
|
// ERR_CONNECTION_FAILURE
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
send(data) {
|
||||||
|
if (this.connected) {
|
||||||
|
this.simplepeer.send(data);
|
||||||
|
} else {
|
||||||
|
//console.log("Can't send, not connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputsignal(sig) {
|
||||||
|
this.simplepeer.signal(sig);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
let myVideo;
|
||||||
|
let otherVideo;
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
createCanvas(windowHeight, windowWidth);
|
||||||
|
|
||||||
|
let constraints = {audio: true, video: true};
|
||||||
|
myVideo = createCapture(constraints,
|
||||||
|
function(stream) {
|
||||||
|
let p5l = new p5LiveMedia(this, "CAPTURE", stream, "NYCAMST")
|
||||||
|
p5l.on('stream', gotStream);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
myVideo.elt.muted = true;
|
||||||
|
myVideo.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
|
||||||
|
background(220);
|
||||||
|
stroke(255);
|
||||||
|
image(myVideo,0,0,width,height);
|
||||||
|
if (otherVideo) {
|
||||||
|
blend(otherVideo, 0, 0, otherVideo.width, otherVideo.height, 0, 0, width, height, MULTIPLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We got a new stream!
|
||||||
|
function gotStream(stream, id) {
|
||||||
|
// This is just like a video/stream from createCapture(VIDEO)
|
||||||
|
otherVideo = stream;
|
||||||
|
//otherVideo.id and id are the same and unique identifiers
|
||||||
|
otherVideo.hide();
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
GESTURE_APPLICATION_ROOT=/breadcube/gesture
|
||||||
|
GESTURE_PORTNUMBER=5002
|
@ -0,0 +1,10 @@
|
|||||||
|
include .env
|
||||||
|
export
|
||||||
|
|
||||||
|
default: local
|
||||||
|
|
||||||
|
local:
|
||||||
|
@flask run --debug
|
||||||
|
|
||||||
|
breadcube:
|
||||||
|
@SCRIPT_NAME=${GESTURE_APPLICATION_ROOT} gunicorn -b localhost:${GESTURE_PORTNUMBER} --reload app:app
|
@ -0,0 +1,10 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import main
|
||||||
|
|
||||||
|
# Load environment variables from the .env file
|
||||||
|
main.load_dotenv()
|
||||||
|
|
||||||
|
# Bind them to Python variables
|
||||||
|
APPLICATION_ROOT = os.environ.get('GESTURE_APPLICATION_ROOT', '/')
|
||||||
|
PORTNUMBER = int(os.environ.get('GESTURE_PORTNUMBER', 5001))
|
||||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.2 MiB |