Browse Source

non-working webrtc test

split-pipe
Hendrik Langer 4 years ago
parent
commit
fd1eb1ef29
  1. 2
      README.md
  2. 19
      raspberry/cert.pem
  3. 28
      raspberry/key.pem
  4. 1
      raspberry/requirements.py
  5. 11
      raspberry/roberto.py
  6. 1
      raspberry/roberto/static/js/socket.io.js.map
  7. 68
      raspberry/roberto/views/websocket/routes.py
  8. 401
      raspberry/roberto/views/websocket/templates/camera.html

2
README.md

@ -21,7 +21,9 @@ WantedBy=sys-subsystem-net-devices-wlan0.device
rpi ~$ sudo systemctl disable wifi_powersave@on.service
rpi ~$ sudo systemctl enable wifi_powersave@off.service
## Test certificate
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
## Camera test

19
raspberry/cert.pem

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUaSR0XsMtkf98JniZIgZCiG5cEfUwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDcyNTEwMjc0NFoXDTIxMDcy
NTEwMjc0NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAw/b9EORjM+5hXkjjg6Fnrzr+QT3m1H/w7W4J08rXsU9Z
UV3JBgTCNqVWOF+Fv4RRB/kymm/keJ25bmfgrG8MwvF9IIOu2vbppLz2yX0Qsx5F
5ZEQ+7Mqm7YxCrQPYwk8hftLLBB9rOaxVpIzOlLXv88NnlRQ1yfxu+QYckgiwzrj
SmKjf7QdksYj8sg3w9zFRuuky/04hWfg1LUn7Mb9r491AUuaHTLqQFlZCgvyvjVJ
sOJafhOWsLFUVZfQeTp3Fc0w0BPPosFdbKNzHP2Top4b3tdSrdzLxHa1zIpWbys8
KGQSW6eL5mwp0RnZsVpaotNqKPtgIYVLIE6xiINeEwIDAQABo1MwUTAdBgNVHQ4E
FgQU/1bdqVZYHiZIwZooDpVBrB9DE0wwHwYDVR0jBBgwFoAU/1bdqVZYHiZIwZoo
DpVBrB9DE0wwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAdfm7
MxhQUn47NfZYfb5XrwJAoHkek3WX9zNiAwmqI1FzpiFGKt2Jr9VESKZc5aqpRcps
rVtfe33g544hYakpFh2jsX2nzFGVz61DnhKot61AYOVRWSdgej94+ik5h9n1fLsH
+aEQnXZ+UvHU9sX8ya1RHZwAbMKYkRZ9NHH4IlVTUdvT3o/uPCG9eF0ayFRk9bsG
sUlBZ1C6MaDwehUz/3gXtToM3Z1PLllHipyKnRUdhWgh/YZp6MfY8PhWB9BGa1d1
TKSaJEyMi0IdvB7e8/dmssGK10TBGNoFCXlzXkt6745fnw+T1f+ZSEzSfM41P/jm
YYeFaQybnPDAx5rD0Q==
-----END CERTIFICATE-----

28
raspberry/key.pem

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDD9v0Q5GMz7mFe
SOODoWevOv5BPebUf/DtbgnTytexT1lRXckGBMI2pVY4X4W/hFEH+TKab+R4nblu
Z+CsbwzC8X0gg67a9umkvPbJfRCzHkXlkRD7syqbtjEKtA9jCTyF+0ssEH2s5rFW
kjM6Ute/zw2eVFDXJ/G75BhySCLDOuNKYqN/tB2SxiPyyDfD3MVG66TL/TiFZ+DU
tSfsxv2vj3UBS5odMupAWVkKC/K+NUmw4lp+E5awsVRVl9B5OncVzTDQE8+iwV1s
o3Mc/ZOinhve11Kt3MvEdrXMilZvKzwoZBJbp4vmbCnRGdmxWlqi02oo+2AhhUsg
TrGIg14TAgMBAAECggEAJ1LFv4EFAyO/uCrKNNzLsJcW9EKWsmemEg71u+FFXRg+
JRy7vRBxZIPTBGbusNq9Y+s2o1T2tNl5n1UK+a8jEW9iG4cxLFFF2z/sCcyl8DuP
7RwqG0f9sddiifJN8CKnWX9uuz3n6i00wtl9nCOJlbzXz8C9pB8o2/pOiYg+KYyq
mAg+iuZGx8HIqR0TOV3TRk5kKvl4OZ9ah51bpm6/X+Rv8DCXsZ1ADZB5kRJA5lC7
lZkCleoan/SVpXccd7ExFF7mByx/6DmfAoWjuoGyPL2fmPmMALYCgc6ZLQ0x53CZ
lwHgvek4Hc0HIAYnoronldLI5urX3N3hKfGBvdUm8QKBgQDquml/0dfTNS8lBvmi
7YPCfqpiuK8mbTNiTO3xaXimqom6/riyF3nrWK2Fdg3LuFCxkWHXOR0kWNBkUEzv
Jb3pCa+B14TqTPCpsXoXerngmxuWnT0BgFGg6HPvvPkYWbT6gpgGOdLv8fY6vrFl
0zq12ipqLrvdcPhvksnfXp5yqQKBgQDVuUib0lz34vsQOoOSUUuWD5rkBu534atB
LGzsbbrPMQUHdDAPKjQRSOAIQzEshVhmjkDL3D2VykIK8QNwxsCm7wOwNyG6Uf1V
YCn03nIXn0Vj3C5bpUyoPqnaNxOWzGU+DdW1H1Hzl2PU7YOdVT3KbpA5i32IutVZ
YsIMXGk8WwKBgCtPEcAfu66glX5DdzP0lub/7/gfE1IHu/9bKlvslfJKbPcvoGxb
oIcn6XxCd/EqpNjedir7wsC+ElUv68IEOLISs2tFlKSWZaEpudkzL7Cdbc2wXV01
i9ogiaOmPl+bzaSbC+m6KY1UG5ZjMOAUxTRgeIr27HrDKVOMxeqMzrdhAoGAK1FB
cSui4i8kGbbyYd6ORlzlVOA+xxd7IVuCvCzFVyclUMxwzTINmY8+sQ4FUnO4Zhjg
8zCxXsG+vv74ZowyEeORyM5zzJK/mOVDu7i2QSlr5ACFeNe5AMSqomrVUpQc1QXy
0aIGdyuw9UAqk+HqAzSDkNY/3E2Z3mMQ13aHcc8CgYACSCQO7TiOLWZicjcJxbCC
DYJuDFIvX7omGYxlXXOrp7QHmv2SiBUdZDZiyBGEQPJQv8Md6eS+Rza6h28FA3kA
Mnewl6gGWX6YerNTZvKtahDqii8ZWFbyys//CYBMlIIohdEYSGaDJG11ysoM5J/E
OsBiiHTujUhuiOWa3JjT1w==
-----END PRIVATE KEY-----

1
raspberry/requirements.py

@ -7,3 +7,4 @@ python3-opencv
python3-picamera
libjs-jquery
libjs-bootstrap
python3-eventlet

11
raspberry/roberto.py

@ -7,4 +7,13 @@ from roberto import create_app
app = create_app('config.cfg')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, threaded=True)
#app.run(host='0.0.0.0', debug=False, threaded=True)
#app.run(host='0.0.0.0', debug=False, threaded=True, ssl_context='adhoc')
from roberto import socketio
socketio.run(
app,
host='0.0.0.0',
port=5000,
certfile='cert.pem',
keyfile='key.pem'
)

1
raspberry/roberto/static/js/socket.io.js.map

File diff suppressed because one or more lines are too long

68
raspberry/roberto/views/websocket/routes.py

@ -51,3 +51,71 @@ def applyDeadZone(value, threshold):
if value < 0:
new_value = new_value * -1
return new_value
connected_particpants = {}
@websocket_blueprint.route('/camera')
def camera():
return render_template('camera.html', room='default')
@socketio.on('message', namespace='/webrtc')
def webrtc_message(sid, data):
sio.emit('message', data=data)
@socketio.on('disconnect', namespace='/webrtc')
def disconnect(sid):
print("Received Disconnect message from %s" % sid)
for room, clients in connected_particpants.iteritems():
try:
clients.remove(sid)
print("Removed %s from %s \n list of left participants is %s" %(sid, room, clients))
except ValueError:
print("Remove %s from %s \n list of left participants is %s has failed" %(sid, room, clients))
@socketio.on('create or join', namespace='/webrtc')
def create_or_join(sid, data):
sio.enter_room(sid, data)
try:
connected_particpants[data].append(sid)
except KeyError:
connected_particpants[data] = [sid]
numClients = len(connected_particpants[data])
if numClients == 1:
sio.emit('created', data)
elif numClients > 2:
sio.emit('full')
elif numClients == 2:
sio.emit('joined')
sio.emit('join')
print (sid, data, len(connected_particpants[data]))
@websocket_blueprint.route('/<room>')
def room(room):
return render_template('camera.html', room=room)

401
raspberry/roberto/views/websocket/templates/camera.html

@ -0,0 +1,401 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Video Streaming Demonstration</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<!-- jQuery and Bootstrap -->
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
</head>
<body>
<div id="videos">
<video id="stream" autoplay playsinline>Your browser does not support video</video>
</div>
<div id="chatlog"></div>
<textarea id="text" name="text"></textarea>
<button id="sendText" onclick="sendData()">Send</button>
<script type="text/javascript">
'use strict';
var room = '{{room}}';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var receiveChannel;
var sendChannel;
var sendText = document.querySelector("#text");
var chatlog = document.querySelector('#chatlog');
var pcConfig = {
'iceServers': [{
'urls': 'stun:stun.l.google.com:19302'
}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
//room = 'foo';
// Could prompt for room name:
// var room = prompt('Enter room name:');
var socket = io.connect('wss://' + document.domain + ':' + location.port + '/test');
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a message
socket.on('message', function(message) {
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
video: true
};
console.log('Getting user media with constraints', constraints);
if (false) {
requestTurn(
'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
);
}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
socket.emit('disconnect', room)
console.log("sending disconnect")
};
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(pcConfig);
sendChannel = pc.createDataChannel('chat', null);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
pc.ondatachannel = handleChannelCallback;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleChannelCallback(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessageCallback;
}
function onReceiveMessageCallback(event) {
var text = document.createElement("P");
text.appendChild(document.createTextNode(event.data))
chatlog.appendChild(text);
}
function handleIceCandidate(event) {
console.log('icecandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
}
function sendData() {
var text = document.createElement("P");
text.appendChild(document.createTextNode(sendText.value));
chatlog.appendChild(text);
sendChannel.send(sendText.value);
sendText.value = '';
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event) {
console.log('createOffer() error: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
// sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message', sessionDescription);
sendMessage(sessionDescription);
}
function onCreateSessionDescriptionError(error) {
//trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
var turnExists = false;
for (var i in pcConfig.iceServers) {
if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
if (!turnExists) {
console.log('Getting TURN server from ', turnURL);
// No TURN server. Get one from computeengineondemand.appspot.com:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
pcConfig.iceServers.push({
'url': 'turn:' + turnServer.username + '@' + turnServer.turn,
'credential': turnServer.password
});
turnReady = true;
}
};
xhr.open('GET', turnURL, true);
xhr.send();
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length - 1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}
</script>
</body>
</html>
Loading…
Cancel
Save