- commit
- 04da858fe8564c85333bc59443e79e4370b28940
- parent
- 460199e9da003a14c6515c6eaa015f75ed926abb
- Author
- Tobias Bengfort <bengfort@mpib-berlin.mpg.de>
- Date
- 2020-03-21 16:16
rtc
Diffstat
| M | common.css | 20 | ++++++++++++++++---- |
| A | rtc/index.html | 34 | ++++++++++++++++++++++++++++++++++ |
| A | rtc/rtc.css | 23 | +++++++++++++++++++++++ |
| A | rtc/rtc.js | 168 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 241 insertions, 4 deletions
diff --git a/common.css b/common.css
@@ -8,7 +8,8 @@ body {
8 8 }
9 9
10 10 input,
11 -1 button {
-1 11 button,
-1 12 .btn {
12 13 border: 1px solid #aaa;
13 14 padding: 0.3em 0.75em;
14 15 font-family: inherit;
@@ -18,19 +19,30 @@ button {
18 19 min-width: 0;
19 20 }
20 21
21 -1 button {
-1 22 button,
-1 23 .btn {
22 24 display: inline-block;
23 25 text-align: center;
24 26 border-color: #26c;
25 27 background: #26c;
26 28 color: #fff;
-1 29 cursor: pointer;
27 30 }
28 31 button:hover,
29 -1 button:focus {
-1 32 button:focus,
-1 33 .btn:hover,
-1 34 .btn:focus {
30 35 border-color: #25a;
31 36 background: #25a;
32 37 }
33 -1 button:active {
-1 38 button:active,
-1 39 .btn:active,
-1 40 .toggle :checked + .btn {
34 41 border-color: blue;
35 42 background: blue;
36 43 }
-1 44
-1 45 .toggle input {
-1 46 /* FIXME: hide only visually */
-1 47 display: none;
-1 48 }
diff --git a/rtc/index.html b/rtc/index.html
@@ -0,0 +1,34 @@ -1 1 <!DOCTYPE html> -1 2 <html> -1 3 <head> -1 4 <meta charset="utf-8" /> -1 5 <title>duct rtc</title> -1 6 <meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src https://patchbay.pub"> -1 7 <link rel="stylesheet" href="../common.css"> -1 8 <link rel="stylesheet" href="rtc.css"> -1 9 </head> -1 10 <body> -1 11 <div class="rtc"> -1 12 <div class="videos"> -1 13 <video class="local" autoplay muted></video> -1 14 </div> -1 15 <div class="controls"> -1 16 <label class="toggle"> -1 17 <input type="checkbox" name="audio" autocomplete="off"> -1 18 <span class="btn">Audio</span> -1 19 </label> -1 20 <label class="toggle"> -1 21 <input type="checkbox" name="video" autocomplete="off"> -1 22 <span class="btn">Video</span> -1 23 </label> -1 24 <label class="toggle"> -1 25 <input type="checkbox" name="screen" autocomplete="off"> -1 26 <span class="btn">Screen</span> -1 27 </label> -1 28 </div> -1 29 </div> -1 30 <script src="../patch.js"></script> -1 31 <script src="rtc.js"></script> -1 32 </body> -1 33 </html> -1 34
diff --git a/rtc/rtc.css b/rtc/rtc.css
@@ -0,0 +1,23 @@
-1 1 body {
-1 2 text-align: center;
-1 3 }
-1 4
-1 5 .rtc {
-1 6 display: grid;
-1 7 grid-template-rows: 1fr min-content;
-1 8 grid-gap: 1em;
-1 9 min-height: calc(100vh - 2em);
-1 10 }
-1 11
-1 12 .videos {
-1 13 display: grid;
-1 14 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-1 15 align-content: center;
-1 16 grid-gap: 1em;
-1 17 }
-1 18
-1 19 video {
-1 20 background-color: black;
-1 21 width: 100%;
-1 22 max-height: 80vh;
-1 23 }
diff --git a/rtc/rtc.js b/rtc/rtc.js
@@ -0,0 +1,168 @@
-1 1 (function() {
-1 2 // https://webrtc.github.io/samples/
-1 3 // https://www.html5rocks.com/en/tutorials/webrtc/basics/
-1 4
-1 5 // ICE -- networking information
-1 6 // offer/answer -- media stream information
-1 7
-1 8 var roomUrl = 'https://patchbay.pub/pubsub/' + location.hash.substr(1);
-1 9 var queueUrl = 'https://patchbay.pub/queue/' + patch.randomString(10);
-1 10 console.log('own queue', queueUrl);
-1 11
-1 12 var container = document.querySelector('.videos');
-1 13 var cons = {};
-1 14
-1 15 var localVideo = document.querySelector('video.local');
-1 16 localVideo.srcObject = new MediaStream();
-1 17
-1 18 var getConnection = function(sender) {
-1 19 if (sender in cons) {
-1 20 return cons[sender].con;
-1 21 }
-1 22
-1 23 var video = document.createElement('video');
-1 24 video.autoplay = true;
-1 25 container.append(video);
-1 26
-1 27 var con = new RTCPeerConnection({
-1 28 iceServers: [{urls: 'stun:stun.l.google.com:19302'}]
-1 29 });
-1 30 con.addEventListener('icecandidate', function(event) {
-1 31 patch.post(sender, {'sender': queueUrl, 'data': event.candidate});
-1 32 });
-1 33 con.addEventListener('track', function(event) {
-1 34 // TODO: maybe check if already equal?
-1 35 // TODO: rm image if no video stream
-1 36 video.srcObject = event.streams[0];
-1 37 });
-1 38
-1 39 var tracks = [];
-1 40 localVideo.srcObject.getTracks().forEach(track => {
-1 41 var s = con.addTrack(track, localVideo.srcObject);
-1 42 tracks.push(s);
-1 43 });
-1 44
-1 45 cons[sender] = {
-1 46 'con': con,
-1 47 'video': video,
-1 48 'tracks': tracks,
-1 49 };
-1 50
-1 51 return con;
-1 52 };
-1 53
-1 54 var makeOffer = function(sender) {
-1 55 if (sender !== queueUrl) {
-1 56 var con = getConnection(sender);
-1 57 con.createOffer().then(offer => {
-1 58 con.setLocalDescription(offer).then(_ => {
-1 59 patch.post(sender, {'sender': queueUrl, 'data': offer});
-1 60 });
-1 61 });
-1 62 }
-1 63 };
-1 64
-1 65 var handleOffer = function(sender, offer) {
-1 66 var con = getConnection(sender);
-1 67 con.setRemoteDescription(offer).then(_ => {
-1 68 con.createAnswer().then(answer => {
-1 69 con.setLocalDescription(answer).then(_ => {
-1 70 patch.post(sender, {'sender': queueUrl, 'data': answer});
-1 71 });
-1 72 });
-1 73 });
-1 74 };
-1 75
-1 76 var handleAnswer = function(sender, answer) {
-1 77 var con = cons[sender].con;
-1 78 con.setRemoteDescription(answer);
-1 79 };
-1 80
-1 81 var handleCandidate = function(sender, candidate) {
-1 82 var con = cons[sender].con;
-1 83 con.addIceCandidate(candidate);
-1 84 };
-1 85
-1 86 patch.listen(roomUrl, function(data) {
-1 87 makeOffer(data);
-1 88 });
-1 89
-1 90 patch.listen(queueUrl, function(data) {
-1 91 var sender = data.sender;
-1 92 var data = data.data;
-1 93 if (sender && data) {
-1 94 if (data.type === 'offer') {
-1 95 return handleOffer(sender, data);
-1 96 } else if (data.type === 'answer') {
-1 97 return handleAnswer(sender, data);
-1 98 } else if (data.candidate) {
-1 99 return handleCandidate(sender, data);
-1 100 }
-1 101 }
-1 102 console.log('unknown message', data);
-1 103 });
-1 104
-1 105 patch.post(roomUrl, queueUrl);
-1 106
-1 107 var updateConnections = function() {
-1 108 var sender, c;
-1 109 var tracks = localVideo.srcObject.getTracks();
-1 110
-1 111 for (sender in cons) {
-1 112 var c = cons[sender];
-1 113
-1 114 while (c.tracks.length) {
-1 115 s = c.tracks.pop();
-1 116 c.con.removeTrack(s);
-1 117 }
-1 118
-1 119 tracks.forEach(track => {
-1 120 s = c.con.addTrack(track, localVideo.srcObject);
-1 121 c.tracks.push(s);
-1 122 });
-1 123
-1 124 makeOffer(sender);
-1 125 }
-1 126 }
-1 127
-1 128 var updateStreams = async function(event) {
-1 129 var oldTracks, getStream, other;
-1 130
-1 131 if (event.target.name === 'audio') {
-1 132 oldTracks = localVideo.srcObject.getAudioTracks();
-1 133 getStream = () => navigator.mediaDevices.getUserMedia({audio: true});
-1 134 } else if (event.target.name === 'video') {
-1 135 oldTracks = localVideo.srcObject.getVideoTracks();
-1 136 getStream = () => navigator.mediaDevices.getUserMedia({video: true});
-1 137 other = document.querySelector('[name="screen"]');
-1 138 } else {
-1 139 oldTracks = localVideo.srcObject.getVideoTracks();
-1 140 getStream = () => navigator.mediaDevices.getDisplayMedia();
-1 141 other = document.querySelector('[name="video"]');
-1 142 }
-1 143
-1 144 oldTracks.forEach(track => {
-1 145 track.stop()
-1 146 localVideo.srcObject.removeTrack(track);
-1 147 });
-1 148 if (event.target.checked) {
-1 149 try {
-1 150 var stream = await getStream();
-1 151 stream.getTracks().forEach(track => {
-1 152 localVideo.srcObject.addTrack(track);
-1 153 });
-1 154 } catch {
-1 155 event.target.checked = false;
-1 156 return;
-1 157 }
-1 158 }
-1 159
-1 160 if (other) {
-1 161 other.checked = false;
-1 162 }
-1 163
-1 164 updateConnections();
-1 165 };
-1 166
-1 167 document.addEventListener('change', updateStreams);
-1 168 })();