Compare commits
2 Commits
3fedec0d28
...
6fc19f6c5f
Author | SHA1 | Date |
---|---|---|
BodgeMaster | 6fc19f6c5f | |
BodgeMaster | 9380771938 |
31
README.md
31
README.md
|
@ -1,32 +1 @@
|
||||||
# WebRTC tests...
|
# WebRTC tests...
|
||||||
|
|
||||||
original readme:
|
|
||||||
|
|
||||||
## webrtc-php
|
|
||||||
|
|
||||||
WebRTC is nice, but impossible to test out if you just have a normal cheap shared-hosting PHP server (which means no websockets), and no time/money/energy to hire a webserver with commandline access to use node.js or Java or a PHP websocket framework. So, here is an example of webRtc with just using plain old cheaply-available PHP.
|
|
||||||
|
|
||||||
**You do need a https server with SSL, otherwise it will not work. It has to be on a real working certificate on a real server. Please please look carefully to all the errors and warnings in your Javascript console - do not ignore any of them.**
|
|
||||||
|
|
||||||
My intention is to keep it as simple as possible, so that you can use it as a startpoint for your own application or just to try out some webRtc stuff.
|
|
||||||
|
|
||||||
It works great, however the WebRTC specs change a bit over time, and not all browsers keep up. Please do not ask me any webRTC specific questions - not because I do not want to help you, but simply because I do not have much specific webRTC knowledge. Please test it first with two Chrome browsers, that should work fine, and then start with testing other browsers. I tested it with some Firefox browsers and some mobile phones too, it worked every time, although the handshakes were extremely difficult to follow.
|
|
||||||
**So again: please do not ask me any webRTC questions, I simply do not have the answers...**
|
|
||||||
|
|
||||||
Currently this works when two users load the same URL in their browser. I use this to communicate with my girlfriend, we both know an unique URL. As far as I know there is no restriction that this can work for more users too, although you have to figure that out yourself.
|
|
||||||
|
|
||||||
I intentionally left out any "room" functionality (meaning: pairs of people can connect in seperate rooms), but if you search for 'room' you can see that on the serverside it already has some hints. You still have to work out the details though, only enabling the room variable will not be enough. And it will still work for just 2 people, not more.
|
|
||||||
|
|
||||||
### No Websocket?
|
|
||||||
|
|
||||||
The "websocket" usually seen in webRTC is only for handshaking, but webRTC does not need a websocket at all. You can also do a handshake by e-mail if you want. Or by using a dove.
|
|
||||||
|
|
||||||
I am using EventSource (or SSE, Server Side Events) instead of a websocket, which actually works in all browsers including mobile browsers (not in old browsers like IE; you could use a polyfill for that, but I removed the polyfill because it is now legally accepted to ignore IE). With EventSource, the browser automaticly request the server for new data with a normal HTTP request (or a HTTP2 socket) every few seconds.
|
|
||||||
|
|
||||||
Because a "few seconds" might be up to 30 seconds, I shortened it to 1 second (that is the "retry: ..." line in the code), and I wrote the eventsource.onmessage in a way that it can receive multiple messages in one call. It works perfectly, but due to the "few seconds" the handshake takes a about two seconds instead of "as soon as possible" - you can fill in a lower number at the retry, but it will upscale the load for your server (unless you stop the eventsource when you're done handshaking). It is fine as it is.
|
|
||||||
|
|
||||||
EventSource is syntacticly equal to WebSocket (so it is easy to move to websockets later!), except that there is no send() method to send data back to the server - for that I extend the eventsource with a send() method with a plain AJAX xmlHttpRequest. On the serverside, plain files are used for storage.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
To install (on a https server!), just place the files in a folder, and you need to "chmod" the directory to writable (because two files will be created) too. Be sure to hit F12 to look at the javascript console and see what is going on.
|
|
||||||
|
|
186
src/index.html
186
src/index.html
|
@ -1,186 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<video id="localVideo" autoplay="true" muted="muted"></video>
|
|
||||||
<video id="remoteVideo" autoplay="true" style="display:none"></video>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
var answer = 0;
|
|
||||||
var pc=null
|
|
||||||
var localStream=null;
|
|
||||||
var ws=null;
|
|
||||||
|
|
||||||
// Not necessary with websockets, but here I need it to distinguish calls
|
|
||||||
var unique = Math.floor(100000 + Math.random() * 900000);
|
|
||||||
|
|
||||||
var localVideo = document.getElementById('localVideo');
|
|
||||||
var remoteVideo = document.getElementById('remoteVideo');
|
|
||||||
var configuration = {
|
|
||||||
'iceServers': [
|
|
||||||
{ 'urls': 'stun:stun.stunprotocol.org:3478' },
|
|
||||||
//{'urls': 'stun:stun.l.google.com:19302' },
|
|
||||||
//{'urls': 'stun:stun1.l.google.com:19302' },
|
|
||||||
//{'urls': 'stun:stun2.l.google.com:19302' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start
|
|
||||||
navigator.mediaDevices.getUserMedia({
|
|
||||||
// audio: true, // audio is off here, enable this line to get audio too
|
|
||||||
video: true
|
|
||||||
}).then(function (stream) {
|
|
||||||
localVideo.srcObject = stream;
|
|
||||||
localStream = stream;
|
|
||||||
|
|
||||||
try {
|
|
||||||
ws = new EventSource('serverGet.php?unique='+unique);
|
|
||||||
} catch(e) {
|
|
||||||
console.error("Could not create eventSource ",e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Websocket-hack: EventSource does not have a 'send()'
|
|
||||||
// so I use an ajax-xmlHttpRequest for posting data.
|
|
||||||
// Now the eventsource-functions are equal to websocket.
|
|
||||||
ws.send = function send(message) {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState!=4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.status != 200) {
|
|
||||||
console.log("Error sending to server with message: " +message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open('POST', 'serverPost.php?unique='+unique, true);
|
|
||||||
xhttp.setRequestHeader("Content-Type","Application/X-Www-Form-Urlencoded");
|
|
||||||
xhttp.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Websocket-hack: onmessage is extended for receiving
|
|
||||||
// multiple events at once for speed, because the polling
|
|
||||||
// frequency of EventSource is low.
|
|
||||||
ws.onmessage = function(e) {
|
|
||||||
if (e.data.includes("_MULTIPLEVENTS_")) {
|
|
||||||
multiple = e.data.split("_MULTIPLEVENTS_");
|
|
||||||
for (x=0; x<multiple.length; x++) {
|
|
||||||
onsinglemessage(multiple[x]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onsinglemessage(e.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go show myself
|
|
||||||
localVideo.addEventListener('loadedmetadata',
|
|
||||||
function () {
|
|
||||||
publish('client-call', null)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}).catch(function (e) {
|
|
||||||
console.log("Problem while getting audio/video stuff ",e);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function onsinglemessage(data) {
|
|
||||||
var package = JSON.parse(data);
|
|
||||||
var data = package.data;
|
|
||||||
|
|
||||||
console.log("received single message: " + package.event);
|
|
||||||
switch (package.event) {
|
|
||||||
case 'client-call':
|
|
||||||
icecandidate(localStream);
|
|
||||||
pc.createOffer({
|
|
||||||
offerToReceiveAudio: 1,
|
|
||||||
offerToReceiveVideo: 1
|
|
||||||
}).then(function (desc) {
|
|
||||||
pc.setLocalDescription(desc).then(
|
|
||||||
function () {
|
|
||||||
publish('client-offer', pc.localDescription);
|
|
||||||
}
|
|
||||||
).catch(function (e) {
|
|
||||||
console.log("Problem with publishing client offer"+e);
|
|
||||||
});
|
|
||||||
}).catch(function (e) {
|
|
||||||
console.log("Problem while doing client-call: "+e);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'client-answer':
|
|
||||||
if (pc==null) {
|
|
||||||
console.error('Before processing the client-answer, I need a client-offer');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pc.setRemoteDescription(new RTCSessionDescription(data),function(){},
|
|
||||||
function(e) { console.log("Problem while doing client-answer: ",e);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'client-offer':
|
|
||||||
icecandidate(localStream);
|
|
||||||
pc.setRemoteDescription(new RTCSessionDescription(data), function(){
|
|
||||||
if (!answer) {
|
|
||||||
pc.createAnswer(function (desc) {
|
|
||||||
pc.setLocalDescription(desc, function () {
|
|
||||||
publish('client-answer', pc.localDescription);
|
|
||||||
}, function(e){
|
|
||||||
console.log("Problem getting client answer: ",e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
,function(e){
|
|
||||||
console.log("Problem while doing client-offer: ",e);
|
|
||||||
});
|
|
||||||
answer = 1;
|
|
||||||
}
|
|
||||||
}, function(e){
|
|
||||||
console.log("Problem while doing client-offer2: ",e);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'client-candidate':
|
|
||||||
if (pc==null) {
|
|
||||||
console.error('Before processing the client-answer, I need a client-offer');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pc.addIceCandidate(new RTCIceCandidate(data), function(){},
|
|
||||||
function(e) { console.log("Problem adding ice candidate: "+e);});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function icecandidate(localStream) {
|
|
||||||
pc = new RTCPeerConnection(configuration);
|
|
||||||
pc.onicecandidate = function (event) {
|
|
||||||
if (event.candidate) {
|
|
||||||
publish('client-candidate', event.candidate);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
pc.addStream(localStream);
|
|
||||||
}catch(e){
|
|
||||||
var tracks = localStream.getTracks();
|
|
||||||
for(var i=0;i<tracks.length;i++){
|
|
||||||
pc.addTrack(tracks[i], localStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pc.ontrack = function (e) {
|
|
||||||
document.getElementById('remoteVideo').style.display="block";
|
|
||||||
document.getElementById('localVideo').style.display="none";
|
|
||||||
remoteVideo.srcObject = e.streams[0];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function publish(event, data) {
|
|
||||||
console.log("sending ws.send: " + event);
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
event:event,
|
|
||||||
data:data
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,72 +0,0 @@
|
||||||
<?
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - read all files from the folder, not just the first one
|
|
||||||
// - look at this one, might demonstrate slightly better: https://shanetully.com/2014/09/a-dead-simple-webrtc-example/
|
|
||||||
|
|
||||||
|
|
||||||
// A unique identifier (not necessary when working with websockets)
|
|
||||||
if (!isset($_GET['unique'])) {
|
|
||||||
die('no identifier');
|
|
||||||
}
|
|
||||||
$unique=$_GET['unique'];
|
|
||||||
if (strlen($unique)==0 || ctype_digit($unique)===false) {
|
|
||||||
die('not a correct identifier');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
header('Content-Type: text/event-stream');
|
|
||||||
header('Cache-Control: no-cache'); // recommended
|
|
||||||
|
|
||||||
function startsWith($haystack, $needle) {
|
|
||||||
return (substr($haystack, 0, strlen($needle) ) === $needle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of all files that start with '_file_' except the file containing
|
|
||||||
// messages that this client has sended itsself ('_file_'.$unique).
|
|
||||||
$all = array ();
|
|
||||||
$handle = opendir ( '../'.basename ( dirname ( __FILE__ ) ) );
|
|
||||||
if ($handle !== false) {
|
|
||||||
while ( false !== ($filename = readdir ( $handle )) ) {
|
|
||||||
if (startsWith($filename,'_file_' /* .$room */) && !(startsWith($filename,'_file_' /*.$room*/ .$unique) )) {
|
|
||||||
$all [] .= $filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir( $handle );
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($all)!=0) {
|
|
||||||
|
|
||||||
// A main lock to ensure save safe writing/reading
|
|
||||||
$mainlock = fopen('serverGet.php','r');
|
|
||||||
if ($mainlock===false) {
|
|
||||||
die('could not create main lock');
|
|
||||||
}
|
|
||||||
flock($mainlock, LOCK_EX);
|
|
||||||
|
|
||||||
// show and empty the first file that is not empty
|
|
||||||
for ($x=0; $x<count($all); $x++) {
|
|
||||||
$filename=$all[$x];
|
|
||||||
|
|
||||||
// prevent sending empty files
|
|
||||||
if (filesize($filename)==0) {
|
|
||||||
unlink($filename);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = fopen($filename, 'c+b');
|
|
||||||
flock($file, LOCK_SH);
|
|
||||||
echo 'data: ', fread($file, filesize($filename)), PHP_EOL;
|
|
||||||
fclose($file);
|
|
||||||
unlink($filename);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock main lock
|
|
||||||
flock($mainlock, LOCK_UN);
|
|
||||||
fclose($mainlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo 'retry: 1000', PHP_EOL, PHP_EOL; // shorten the 3 seconds to 1 sec
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?
|
|
||||||
|
|
||||||
// A unique identifier (not necessary when working with websockets)
|
|
||||||
if (!isset($_GET['unique'])) {
|
|
||||||
die('no identifier');
|
|
||||||
}
|
|
||||||
$unique=$_GET['unique'];
|
|
||||||
if (strlen($unique)==0 || ctype_digit($unique)===false) {
|
|
||||||
die('not a correct identifier');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// A main lock to ensure save safe writing/reading
|
|
||||||
$mainlock = fopen('serverGet.php','r');
|
|
||||||
if ($mainlock===false) {
|
|
||||||
die('could not create main lock');
|
|
||||||
}
|
|
||||||
flock($mainlock, LOCK_EX);
|
|
||||||
|
|
||||||
// Add the new message to file
|
|
||||||
$filename = '_file_' /*.$room*/ . $unique;
|
|
||||||
$file = fopen($filename,'ab');
|
|
||||||
if (filesize($filename)!=0) {
|
|
||||||
fwrite($file,'_MULTIPLEVENTS_');
|
|
||||||
}
|
|
||||||
$posted = file_get_contents('php://input');
|
|
||||||
fwrite($file,$posted);
|
|
||||||
fclose($file);
|
|
||||||
|
|
||||||
// Unlock main lock
|
|
||||||
flock($mainlock,LOCK_UN);
|
|
||||||
fclose($mainlock);
|
|
||||||
|
|
||||||
?>
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<titile> User Agent String Checker </title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo $_SERVER['HTTP_USER_AGENT'];
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue