├── LICENSE
├── README.md
├── client
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.jsx
│ ├── components
│ │ ├── CreateRoom.jsx
│ │ └── Room.jsx
│ └── main.jsx
└── vite.config.js
├── go.mod
├── go.sum
├── main.go
├── server
├── rooms.go
└── signalling.go
└── video-chat-app
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Junaid H. Rahim
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building Video Chat Apps using WebRTC and Golang
2 |
3 | Code for the DSC KIIT Workshop "WebRTC + Golang" conducted on 23rd Feb 2021
4 |
5 | PPT available [here](https://app.pitch.com/app/presentation/6371a8aa-a4ec-44ea-a9cc-432a66726150/2ad0e236-a776-4b2f-9d24-abc0245819cb)
6 |
7 | ### Frontend
8 |
9 | The `client` is written in React and uses [Vite](https://vitejs.dev/) for the dev server. Run the following commands in the `client` directory
10 |
11 | * `npm i` to install all the dependencies
12 | * `npm run dev` to start the local dev server
13 |
14 | ### Backend
15 |
16 | Written in Go. A simple WebSocket server for signalling implemented using
17 | [gorilla/websocket](https://github.com/gorilla/websocket)
18 |
19 | * `go build` to compile and build the binary
20 | * `./video-chat-app` to run the backend server on `:8000`
21 |
22 |
23 |
24 | [](https://forthebadge.com)
25 | [](https://forthebadge.com)
26 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DSC Video Chat App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/client/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/code-frame": {
8 | "version": "7.12.13",
9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
10 | "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
11 | "dev": true,
12 | "requires": {
13 | "@babel/highlight": "^7.12.13"
14 | }
15 | },
16 | "@babel/core": {
17 | "version": "7.12.17",
18 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.17.tgz",
19 | "integrity": "sha512-V3CuX1aBywbJvV2yzJScRxeiiw0v2KZZYYE3giywxzFJL13RiyPjaaDwhDnxmgFTTS7FgvM2ijr4QmKNIu0AtQ==",
20 | "dev": true,
21 | "requires": {
22 | "@babel/code-frame": "^7.12.13",
23 | "@babel/generator": "^7.12.17",
24 | "@babel/helper-module-transforms": "^7.12.17",
25 | "@babel/helpers": "^7.12.17",
26 | "@babel/parser": "^7.12.17",
27 | "@babel/template": "^7.12.13",
28 | "@babel/traverse": "^7.12.17",
29 | "@babel/types": "^7.12.17",
30 | "convert-source-map": "^1.7.0",
31 | "debug": "^4.1.0",
32 | "gensync": "^1.0.0-beta.1",
33 | "json5": "^2.1.2",
34 | "lodash": "^4.17.19",
35 | "semver": "^5.4.1",
36 | "source-map": "^0.5.0"
37 | }
38 | },
39 | "@babel/generator": {
40 | "version": "7.12.17",
41 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.17.tgz",
42 | "integrity": "sha512-DSA7ruZrY4WI8VxuS1jWSRezFnghEoYEFrZcw9BizQRmOZiUsiHl59+qEARGPqPikwA/GPTyRCi7isuCK/oyqg==",
43 | "dev": true,
44 | "requires": {
45 | "@babel/types": "^7.12.17",
46 | "jsesc": "^2.5.1",
47 | "source-map": "^0.5.0"
48 | }
49 | },
50 | "@babel/helper-function-name": {
51 | "version": "7.12.13",
52 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz",
53 | "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==",
54 | "dev": true,
55 | "requires": {
56 | "@babel/helper-get-function-arity": "^7.12.13",
57 | "@babel/template": "^7.12.13",
58 | "@babel/types": "^7.12.13"
59 | }
60 | },
61 | "@babel/helper-get-function-arity": {
62 | "version": "7.12.13",
63 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz",
64 | "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==",
65 | "dev": true,
66 | "requires": {
67 | "@babel/types": "^7.12.13"
68 | }
69 | },
70 | "@babel/helper-member-expression-to-functions": {
71 | "version": "7.12.17",
72 | "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.17.tgz",
73 | "integrity": "sha512-Bzv4p3ODgS/qpBE0DiJ9qf5WxSmrQ8gVTe8ClMfwwsY2x/rhykxxy3bXzG7AGTnPB2ij37zGJ/Q/6FruxHxsxg==",
74 | "dev": true,
75 | "requires": {
76 | "@babel/types": "^7.12.17"
77 | }
78 | },
79 | "@babel/helper-module-imports": {
80 | "version": "7.12.13",
81 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz",
82 | "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==",
83 | "dev": true,
84 | "requires": {
85 | "@babel/types": "^7.12.13"
86 | }
87 | },
88 | "@babel/helper-module-transforms": {
89 | "version": "7.12.17",
90 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.17.tgz",
91 | "integrity": "sha512-sFL+p6zOCQMm9vilo06M4VHuTxUAwa6IxgL56Tq1DVtA0ziAGTH1ThmJq7xwPqdQlgAbKX3fb0oZNbtRIyA5KQ==",
92 | "dev": true,
93 | "requires": {
94 | "@babel/helper-module-imports": "^7.12.13",
95 | "@babel/helper-replace-supers": "^7.12.13",
96 | "@babel/helper-simple-access": "^7.12.13",
97 | "@babel/helper-split-export-declaration": "^7.12.13",
98 | "@babel/helper-validator-identifier": "^7.12.11",
99 | "@babel/template": "^7.12.13",
100 | "@babel/traverse": "^7.12.17",
101 | "@babel/types": "^7.12.17",
102 | "lodash": "^4.17.19"
103 | }
104 | },
105 | "@babel/helper-optimise-call-expression": {
106 | "version": "7.12.13",
107 | "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz",
108 | "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==",
109 | "dev": true,
110 | "requires": {
111 | "@babel/types": "^7.12.13"
112 | }
113 | },
114 | "@babel/helper-plugin-utils": {
115 | "version": "7.12.13",
116 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz",
117 | "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==",
118 | "dev": true
119 | },
120 | "@babel/helper-replace-supers": {
121 | "version": "7.12.13",
122 | "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz",
123 | "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==",
124 | "dev": true,
125 | "requires": {
126 | "@babel/helper-member-expression-to-functions": "^7.12.13",
127 | "@babel/helper-optimise-call-expression": "^7.12.13",
128 | "@babel/traverse": "^7.12.13",
129 | "@babel/types": "^7.12.13"
130 | }
131 | },
132 | "@babel/helper-simple-access": {
133 | "version": "7.12.13",
134 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz",
135 | "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==",
136 | "dev": true,
137 | "requires": {
138 | "@babel/types": "^7.12.13"
139 | }
140 | },
141 | "@babel/helper-split-export-declaration": {
142 | "version": "7.12.13",
143 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz",
144 | "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==",
145 | "dev": true,
146 | "requires": {
147 | "@babel/types": "^7.12.13"
148 | }
149 | },
150 | "@babel/helper-validator-identifier": {
151 | "version": "7.12.11",
152 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
153 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
154 | "dev": true
155 | },
156 | "@babel/helpers": {
157 | "version": "7.12.17",
158 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.17.tgz",
159 | "integrity": "sha512-tEpjqSBGt/SFEsFikKds1sLNChKKGGR17flIgQKXH4fG6m9gTgl3gnOC1giHNyaBCSKuTfxaSzHi7UnvqiVKxg==",
160 | "dev": true,
161 | "requires": {
162 | "@babel/template": "^7.12.13",
163 | "@babel/traverse": "^7.12.17",
164 | "@babel/types": "^7.12.17"
165 | }
166 | },
167 | "@babel/highlight": {
168 | "version": "7.12.13",
169 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz",
170 | "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==",
171 | "dev": true,
172 | "requires": {
173 | "@babel/helper-validator-identifier": "^7.12.11",
174 | "chalk": "^2.0.0",
175 | "js-tokens": "^4.0.0"
176 | }
177 | },
178 | "@babel/parser": {
179 | "version": "7.12.17",
180 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.17.tgz",
181 | "integrity": "sha512-r1yKkiUTYMQ8LiEI0UcQx5ETw5dpTLn9wijn9hk6KkTtOK95FndDN10M+8/s6k/Ymlbivw0Av9q4SlgF80PtHg==",
182 | "dev": true
183 | },
184 | "@babel/plugin-transform-react-jsx-self": {
185 | "version": "7.12.13",
186 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.13.tgz",
187 | "integrity": "sha512-FXYw98TTJ125GVCCkFLZXlZ1qGcsYqNQhVBQcZjyrwf8FEUtVfKIoidnO8S0q+KBQpDYNTmiGo1gn67Vti04lQ==",
188 | "dev": true,
189 | "requires": {
190 | "@babel/helper-plugin-utils": "^7.12.13"
191 | }
192 | },
193 | "@babel/plugin-transform-react-jsx-source": {
194 | "version": "7.12.13",
195 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.13.tgz",
196 | "integrity": "sha512-O5JJi6fyfih0WfDgIJXksSPhGP/G0fQpfxYy87sDc+1sFmsCS6wr3aAn+whbzkhbjtq4VMqLRaSzR6IsshIC0Q==",
197 | "dev": true,
198 | "requires": {
199 | "@babel/helper-plugin-utils": "^7.12.13"
200 | }
201 | },
202 | "@babel/runtime": {
203 | "version": "7.12.18",
204 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz",
205 | "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==",
206 | "requires": {
207 | "regenerator-runtime": "^0.13.4"
208 | }
209 | },
210 | "@babel/template": {
211 | "version": "7.12.13",
212 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz",
213 | "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==",
214 | "dev": true,
215 | "requires": {
216 | "@babel/code-frame": "^7.12.13",
217 | "@babel/parser": "^7.12.13",
218 | "@babel/types": "^7.12.13"
219 | }
220 | },
221 | "@babel/traverse": {
222 | "version": "7.12.17",
223 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.17.tgz",
224 | "integrity": "sha512-LGkTqDqdiwC6Q7fWSwQoas/oyiEYw6Hqjve5KOSykXkmFJFqzvGMb9niaUEag3Rlve492Mkye3gLw9FTv94fdQ==",
225 | "dev": true,
226 | "requires": {
227 | "@babel/code-frame": "^7.12.13",
228 | "@babel/generator": "^7.12.17",
229 | "@babel/helper-function-name": "^7.12.13",
230 | "@babel/helper-split-export-declaration": "^7.12.13",
231 | "@babel/parser": "^7.12.17",
232 | "@babel/types": "^7.12.17",
233 | "debug": "^4.1.0",
234 | "globals": "^11.1.0",
235 | "lodash": "^4.17.19"
236 | }
237 | },
238 | "@babel/types": {
239 | "version": "7.12.17",
240 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.17.tgz",
241 | "integrity": "sha512-tNMDjcv/4DIcHxErTgwB9q2ZcYyN0sUfgGKUK/mm1FJK7Wz+KstoEekxrl/tBiNDgLK1HGi+sppj1An/1DR4fQ==",
242 | "dev": true,
243 | "requires": {
244 | "@babel/helper-validator-identifier": "^7.12.11",
245 | "lodash": "^4.17.19",
246 | "to-fast-properties": "^2.0.0"
247 | }
248 | },
249 | "@vitejs/plugin-react-refresh": {
250 | "version": "1.3.1",
251 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-refresh/-/plugin-react-refresh-1.3.1.tgz",
252 | "integrity": "sha512-VxHkrvOOTnMGWq9BveEN5ufmfDfaGxfawCymlKdF+X0RApCr0jQFzOyewhmSSCgGHjqnpRj+7TTDebjBkB3qhg==",
253 | "dev": true,
254 | "requires": {
255 | "@babel/core": "^7.12.13",
256 | "@babel/plugin-transform-react-jsx-self": "^7.12.13",
257 | "@babel/plugin-transform-react-jsx-source": "^7.12.13",
258 | "react-refresh": "^0.9.0"
259 | }
260 | },
261 | "ansi-styles": {
262 | "version": "3.2.1",
263 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
264 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
265 | "dev": true,
266 | "requires": {
267 | "color-convert": "^1.9.0"
268 | }
269 | },
270 | "chalk": {
271 | "version": "2.4.2",
272 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
273 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
274 | "dev": true,
275 | "requires": {
276 | "ansi-styles": "^3.2.1",
277 | "escape-string-regexp": "^1.0.5",
278 | "supports-color": "^5.3.0"
279 | }
280 | },
281 | "color-convert": {
282 | "version": "1.9.3",
283 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
284 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
285 | "dev": true,
286 | "requires": {
287 | "color-name": "1.1.3"
288 | }
289 | },
290 | "color-name": {
291 | "version": "1.1.3",
292 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
293 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
294 | "dev": true
295 | },
296 | "colorette": {
297 | "version": "1.2.1",
298 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
299 | "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
300 | "dev": true
301 | },
302 | "convert-source-map": {
303 | "version": "1.7.0",
304 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
305 | "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
306 | "dev": true,
307 | "requires": {
308 | "safe-buffer": "~5.1.1"
309 | }
310 | },
311 | "debug": {
312 | "version": "4.3.1",
313 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
314 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
315 | "dev": true,
316 | "requires": {
317 | "ms": "2.1.2"
318 | }
319 | },
320 | "esbuild": {
321 | "version": "0.8.50",
322 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.50.tgz",
323 | "integrity": "sha512-oidFLXssA7IccYzkqLVZSqNJDwDq8Mh/vqvrW+3fPWM7iUiC5O2bCllhnO8+K9LlyL/2Z6n+WwRJAz9fqSIVRg==",
324 | "dev": true
325 | },
326 | "escape-string-regexp": {
327 | "version": "1.0.5",
328 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
329 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
330 | "dev": true
331 | },
332 | "fsevents": {
333 | "version": "2.3.2",
334 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
335 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
336 | "dev": true,
337 | "optional": true
338 | },
339 | "function-bind": {
340 | "version": "1.1.1",
341 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
342 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
343 | "dev": true
344 | },
345 | "gensync": {
346 | "version": "1.0.0-beta.2",
347 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
348 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
349 | "dev": true
350 | },
351 | "globals": {
352 | "version": "11.12.0",
353 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
354 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
355 | "dev": true
356 | },
357 | "has": {
358 | "version": "1.0.3",
359 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
360 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
361 | "dev": true,
362 | "requires": {
363 | "function-bind": "^1.1.1"
364 | }
365 | },
366 | "has-flag": {
367 | "version": "3.0.0",
368 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
369 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
370 | "dev": true
371 | },
372 | "history": {
373 | "version": "4.10.1",
374 | "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
375 | "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
376 | "requires": {
377 | "@babel/runtime": "^7.1.2",
378 | "loose-envify": "^1.2.0",
379 | "resolve-pathname": "^3.0.0",
380 | "tiny-invariant": "^1.0.2",
381 | "tiny-warning": "^1.0.0",
382 | "value-equal": "^1.0.1"
383 | }
384 | },
385 | "hoist-non-react-statics": {
386 | "version": "3.3.2",
387 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
388 | "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
389 | "requires": {
390 | "react-is": "^16.7.0"
391 | }
392 | },
393 | "is-core-module": {
394 | "version": "2.2.0",
395 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
396 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
397 | "dev": true,
398 | "requires": {
399 | "has": "^1.0.3"
400 | }
401 | },
402 | "isarray": {
403 | "version": "0.0.1",
404 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
405 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
406 | },
407 | "js-tokens": {
408 | "version": "4.0.0",
409 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
410 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
411 | },
412 | "jsesc": {
413 | "version": "2.5.2",
414 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
415 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
416 | "dev": true
417 | },
418 | "json5": {
419 | "version": "2.2.0",
420 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
421 | "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
422 | "dev": true,
423 | "requires": {
424 | "minimist": "^1.2.5"
425 | }
426 | },
427 | "lodash": {
428 | "version": "4.17.21",
429 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
430 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
431 | "dev": true
432 | },
433 | "loose-envify": {
434 | "version": "1.4.0",
435 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
436 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
437 | "requires": {
438 | "js-tokens": "^3.0.0 || ^4.0.0"
439 | }
440 | },
441 | "mini-create-react-context": {
442 | "version": "0.4.1",
443 | "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
444 | "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
445 | "requires": {
446 | "@babel/runtime": "^7.12.1",
447 | "tiny-warning": "^1.0.3"
448 | }
449 | },
450 | "minimist": {
451 | "version": "1.2.5",
452 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
453 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
454 | "dev": true
455 | },
456 | "ms": {
457 | "version": "2.1.2",
458 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
459 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
460 | "dev": true
461 | },
462 | "nanoid": {
463 | "version": "3.1.20",
464 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
465 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
466 | "dev": true
467 | },
468 | "object-assign": {
469 | "version": "4.1.1",
470 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
471 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
472 | },
473 | "path-parse": {
474 | "version": "1.0.6",
475 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
476 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
477 | "dev": true
478 | },
479 | "path-to-regexp": {
480 | "version": "1.8.0",
481 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
482 | "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
483 | "requires": {
484 | "isarray": "0.0.1"
485 | }
486 | },
487 | "postcss": {
488 | "version": "8.2.6",
489 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz",
490 | "integrity": "sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==",
491 | "dev": true,
492 | "requires": {
493 | "colorette": "^1.2.1",
494 | "nanoid": "^3.1.20",
495 | "source-map": "^0.6.1"
496 | },
497 | "dependencies": {
498 | "source-map": {
499 | "version": "0.6.1",
500 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
501 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
502 | "dev": true
503 | }
504 | }
505 | },
506 | "prop-types": {
507 | "version": "15.7.2",
508 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
509 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
510 | "requires": {
511 | "loose-envify": "^1.4.0",
512 | "object-assign": "^4.1.1",
513 | "react-is": "^16.8.1"
514 | }
515 | },
516 | "react": {
517 | "version": "17.0.1",
518 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
519 | "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==",
520 | "requires": {
521 | "loose-envify": "^1.1.0",
522 | "object-assign": "^4.1.1"
523 | }
524 | },
525 | "react-dom": {
526 | "version": "17.0.1",
527 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz",
528 | "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==",
529 | "requires": {
530 | "loose-envify": "^1.1.0",
531 | "object-assign": "^4.1.1",
532 | "scheduler": "^0.20.1"
533 | }
534 | },
535 | "react-is": {
536 | "version": "16.13.1",
537 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
538 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
539 | },
540 | "react-refresh": {
541 | "version": "0.9.0",
542 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
543 | "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
544 | "dev": true
545 | },
546 | "react-router": {
547 | "version": "5.2.0",
548 | "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
549 | "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
550 | "requires": {
551 | "@babel/runtime": "^7.1.2",
552 | "history": "^4.9.0",
553 | "hoist-non-react-statics": "^3.1.0",
554 | "loose-envify": "^1.3.1",
555 | "mini-create-react-context": "^0.4.0",
556 | "path-to-regexp": "^1.7.0",
557 | "prop-types": "^15.6.2",
558 | "react-is": "^16.6.0",
559 | "tiny-invariant": "^1.0.2",
560 | "tiny-warning": "^1.0.0"
561 | }
562 | },
563 | "react-router-dom": {
564 | "version": "5.2.0",
565 | "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
566 | "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
567 | "requires": {
568 | "@babel/runtime": "^7.1.2",
569 | "history": "^4.9.0",
570 | "loose-envify": "^1.3.1",
571 | "prop-types": "^15.6.2",
572 | "react-router": "5.2.0",
573 | "tiny-invariant": "^1.0.2",
574 | "tiny-warning": "^1.0.0"
575 | }
576 | },
577 | "regenerator-runtime": {
578 | "version": "0.13.7",
579 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
580 | "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
581 | },
582 | "resolve": {
583 | "version": "1.20.0",
584 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
585 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
586 | "dev": true,
587 | "requires": {
588 | "is-core-module": "^2.2.0",
589 | "path-parse": "^1.0.6"
590 | }
591 | },
592 | "resolve-pathname": {
593 | "version": "3.0.0",
594 | "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
595 | "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
596 | },
597 | "rollup": {
598 | "version": "2.39.0",
599 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.0.tgz",
600 | "integrity": "sha512-+WR3bttcq7zE+BntH09UxaW3bQo3vItuYeLsyk4dL2tuwbeSKJuvwiawyhEnvRdRgrII0Uzk00FpctHO/zB1kw==",
601 | "dev": true,
602 | "requires": {
603 | "fsevents": "~2.3.1"
604 | }
605 | },
606 | "safe-buffer": {
607 | "version": "5.1.2",
608 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
609 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
610 | "dev": true
611 | },
612 | "scheduler": {
613 | "version": "0.20.1",
614 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz",
615 | "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==",
616 | "requires": {
617 | "loose-envify": "^1.1.0",
618 | "object-assign": "^4.1.1"
619 | }
620 | },
621 | "semver": {
622 | "version": "5.7.1",
623 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
624 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
625 | "dev": true
626 | },
627 | "source-map": {
628 | "version": "0.5.7",
629 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
630 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
631 | "dev": true
632 | },
633 | "supports-color": {
634 | "version": "5.5.0",
635 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
636 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
637 | "dev": true,
638 | "requires": {
639 | "has-flag": "^3.0.0"
640 | }
641 | },
642 | "tiny-invariant": {
643 | "version": "1.1.0",
644 | "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
645 | "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
646 | },
647 | "tiny-warning": {
648 | "version": "1.0.3",
649 | "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
650 | "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
651 | },
652 | "to-fast-properties": {
653 | "version": "2.0.0",
654 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
655 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
656 | "dev": true
657 | },
658 | "value-equal": {
659 | "version": "1.0.1",
660 | "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
661 | "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
662 | },
663 | "vite": {
664 | "version": "2.0.2",
665 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.0.2.tgz",
666 | "integrity": "sha512-X+PTIPRt6/5Odf/h0kBkwkck+YC0I6oKH5+ttA9ytoLyC9yeksktVq1KNzImqB+/1CNBiBE2vr7orcgSxAi67w==",
667 | "dev": true,
668 | "requires": {
669 | "esbuild": "^0.8.47",
670 | "fsevents": "~2.3.1",
671 | "postcss": "^8.2.1",
672 | "resolve": "^1.19.0",
673 | "rollup": "^2.38.5"
674 | }
675 | }
676 | }
677 | }
678 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build",
7 | "serve": "vite preview"
8 | },
9 | "dependencies": {
10 | "react": "^17.0.0",
11 | "react-dom": "^17.0.0",
12 | "react-router-dom": "^5.2.0"
13 | },
14 | "devDependencies": {
15 | "@vitejs/plugin-react-refresh": "^1.1.0",
16 | "vite": "^2.0.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/client/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { BrowserRouter, Route, Switch } from "react-router-dom";
3 |
4 | import CreateRoom from "./components/CreateRoom"
5 | import Room from "./components/Room"
6 |
7 | function App() {
8 | return
9 |
10 |
11 |
12 |
13 |
14 |
15 |
;
16 | }
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/client/src/components/CreateRoom.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const CreateRoom = (props) => {
4 | const create = async (e) => {
5 | e.preventDefault();
6 |
7 | const resp = await fetch("http://localhost:8000/create");
8 | const { room_id } = await resp.json();
9 |
10 | props.history.push(`/room/${room_id}`)
11 | };
12 |
13 | return (
14 |
15 | Create Room
16 |
17 | );
18 | };
19 |
20 | export default CreateRoom;
21 |
--------------------------------------------------------------------------------
/client/src/components/Room.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 |
3 | const Room = (props) => {
4 | const userVideo = useRef();
5 | const userStream = useRef();
6 | const partnerVideo = useRef();
7 | const peerRef = useRef();
8 | const webSocketRef = useRef();
9 |
10 | const openCamera = async () => {
11 | const allDevices = await navigator.mediaDevices.enumerateDevices();
12 | const cameras = allDevices.filter(
13 | (device) => device.kind == "videoinput"
14 | );
15 | console.log(cameras);
16 |
17 | const constraints = {
18 | audio: true,
19 | video: {
20 | deviceId: cameras[1].deviceId,
21 | },
22 | };
23 |
24 | try {
25 | return await navigator.mediaDevices.getUserMedia(constraints);
26 | } catch (err) {
27 | console.log(err);
28 | }
29 | };
30 |
31 | useEffect(() => {
32 | openCamera().then((stream) => {
33 | userVideo.current.srcObject = stream;
34 | userStream.current = stream;
35 |
36 | webSocketRef.current = new WebSocket(
37 | `ws://localhost:8000/join?roomID=${props.match.params.roomID}`
38 | );
39 |
40 | webSocketRef.current.addEventListener("open", () => {
41 | webSocketRef.current.send(JSON.stringify({ join: true }));
42 | });
43 |
44 | webSocketRef.current.addEventListener("message", async (e) => {
45 | const message = JSON.parse(e.data);
46 |
47 | if (message.join) {
48 | callUser();
49 | }
50 |
51 | if (message.offer) {
52 | handleOffer(message.offer);
53 | }
54 |
55 | if (message.answer) {
56 | console.log("Receiving Answer");
57 | peerRef.current.setRemoteDescription(
58 | new RTCSessionDescription(message.answer)
59 | );
60 | }
61 |
62 | if (message.iceCandidate) {
63 | console.log("Receiving and Adding ICE Candidate");
64 | try {
65 | await peerRef.current.addIceCandidate(
66 | message.iceCandidate
67 | );
68 | } catch (err) {
69 | console.log("Error Receiving ICE Candidate", err);
70 | }
71 | }
72 | });
73 | });
74 | });
75 |
76 | const handleOffer = async (offer) => {
77 | console.log("Received Offer, Creating Answer");
78 | peerRef.current = createPeer();
79 |
80 | await peerRef.current.setRemoteDescription(
81 | new RTCSessionDescription(offer)
82 | );
83 |
84 | userStream.current.getTracks().forEach((track) => {
85 | peerRef.current.addTrack(track, userStream.current);
86 | });
87 |
88 | const answer = await peerRef.current.createAnswer();
89 | await peerRef.current.setLocalDescription(answer);
90 |
91 | webSocketRef.current.send(
92 | JSON.stringify({ answer: peerRef.current.localDescription })
93 | );
94 | };
95 |
96 | const callUser = () => {
97 | console.log("Calling Other User");
98 | peerRef.current = createPeer();
99 |
100 | userStream.current.getTracks().forEach((track) => {
101 | peerRef.current.addTrack(track, userStream.current);
102 | });
103 | };
104 |
105 | const createPeer = () => {
106 | console.log("Creating Peer Connection");
107 | const peer = new RTCPeerConnection({
108 | iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
109 | });
110 |
111 | peer.onnegotiationneeded = handleNegotiationNeeded;
112 | peer.onicecandidate = handleIceCandidateEvent;
113 | peer.ontrack = handleTrackEvent;
114 |
115 | return peer;
116 | };
117 |
118 | const handleNegotiationNeeded = async () => {
119 | console.log("Creating Offer");
120 |
121 | try {
122 | const myOffer = await peerRef.current.createOffer();
123 | await peerRef.current.setLocalDescription(myOffer);
124 |
125 | webSocketRef.current.send(
126 | JSON.stringify({ offer: peerRef.current.localDescription })
127 | );
128 | } catch (err) {}
129 | };
130 |
131 | const handleIceCandidateEvent = (e) => {
132 | console.log("Found Ice Candidate");
133 | if (e.candidate) {
134 | console.log(e.candidate);
135 | webSocketRef.current.send(
136 | JSON.stringify({ iceCandidate: e.candidate })
137 | );
138 | }
139 | };
140 |
141 | const handleTrackEvent = (e) => {
142 | console.log("Received Tracks");
143 | partnerVideo.current.srcObject = e.streams[0];
144 | };
145 |
146 | return (
147 |
148 |
149 |
150 |
151 | );
152 | };
153 |
154 | export default Room;
155 |
--------------------------------------------------------------------------------
/client/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import App from "./App"
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById("root")
11 | );
12 |
--------------------------------------------------------------------------------
/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import reactRefresh from '@vitejs/plugin-react-refresh'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [reactRefresh()]
7 | })
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module video-chat-app
2 |
3 | go 1.15
4 |
5 | require github.com/gorilla/websocket v1.4.2
6 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
2 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
3 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "video-chat-app/server"
5 | "log"
6 | "net/http"
7 | )
8 |
9 | func main() {
10 | server.AllRooms.Init()
11 |
12 | http.HandleFunc("/create", server.CreateRoomRequestHandler)
13 | http.HandleFunc("/join", server.JoinRoomRequestHandler)
14 |
15 | log.Println("Starting Server on Port 8000")
16 | err := http.ListenAndServe(":8000", nil)
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/rooms.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/gorilla/websocket"
5 | "log"
6 | "sync"
7 | "math/rand"
8 | "time"
9 | )
10 |
11 | // Participant describes a single entity in the hashmap
12 | type Participant struct {
13 | Host bool
14 | Conn *websocket.Conn
15 | }
16 |
17 | // RoomMap is the main hashmap [roomID string] -> [[]Participant]
18 | type RoomMap struct {
19 | Mutex sync.RWMutex
20 | Map map[string][]Participant
21 | }
22 |
23 | // Init initialises the RoomMap struct
24 | func (r *RoomMap) Init() {
25 | r.Map = make(map[string][]Participant)
26 | }
27 |
28 | // Get will return the array of participants in the room
29 | func (r *RoomMap) Get(roomID string) []Participant {
30 | r.Mutex.RLock()
31 | defer r.Mutex.RUnlock()
32 |
33 | return r.Map[roomID]
34 | }
35 |
36 | // CreateRoom generate a unique room ID and return it -> insert it in the hashmap
37 | func (r *RoomMap) CreateRoom() string {
38 | r.Mutex.Lock()
39 | defer r.Mutex.Unlock()
40 |
41 | rand.Seed(time.Now().UnixNano())
42 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
43 | b := make([]rune, 8)
44 |
45 | for i := range b {
46 | b[i] = letters[rand.Intn(len(letters))]
47 | }
48 |
49 | roomID := string(b)
50 | r.Map[roomID] = []Participant{}
51 |
52 | return roomID
53 | }
54 |
55 | // InsertIntoRoom will create a participant and add it in the hashmap
56 | func (r *RoomMap) InsertIntoRoom(roomID string, host bool, conn *websocket.Conn) {
57 | r.Mutex.Lock()
58 | defer r.Mutex.Unlock()
59 |
60 | p := Participant{host, conn}
61 |
62 | log.Println("Inserting into Room with RoomID: ", roomID)
63 | r.Map[roomID] = append(r.Map[roomID], p);
64 | }
65 |
66 | // DeleteRoom deletes the room with the roomID
67 | func (r *RoomMap) DeleteRoom(roomID string) {
68 | r.Mutex.Lock()
69 | defer r.Mutex.Unlock()
70 |
71 | delete(r.Map, roomID)
72 | }
--------------------------------------------------------------------------------
/server/signalling.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gorilla/websocket"
6 | "log"
7 | "net/http"
8 | )
9 |
10 | // AllRooms is the global hashmap for the server
11 | var AllRooms RoomMap
12 |
13 | // CreateRoomRequestHandler Create a Room and return roomID
14 | func CreateRoomRequestHandler(w http.ResponseWriter, r *http.Request) {
15 | w.Header().Set("Access-Control-Allow-Origin", "*")
16 | roomID := AllRooms.CreateRoom()
17 |
18 | type resp struct {
19 | RoomID string `json:"room_id"`
20 | }
21 |
22 | log.Println(AllRooms.Map)
23 | json.NewEncoder(w).Encode(resp{RoomID: roomID})
24 | }
25 |
26 | var upgrader = websocket.Upgrader{
27 | CheckOrigin: func(r *http.Request) bool {
28 | return true
29 | },
30 | }
31 |
32 | type broadcastMsg struct {
33 | Message map[string]interface{}
34 | RoomID string
35 | Client *websocket.Conn
36 | }
37 |
38 | var broadcast = make(chan broadcastMsg)
39 |
40 | func broadcaster() {
41 | for {
42 | msg := <- broadcast
43 |
44 | for _, client := range AllRooms.Map[msg.RoomID] {
45 | if(client.Conn != msg.Client) {
46 | err := client.Conn.WriteJSON(msg.Message)
47 |
48 | if err != nil {
49 | log.Fatal(err)
50 | client.Conn.Close()
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
57 |
58 | // JoinRoomRequestHandler will join the client in a particular room
59 | func JoinRoomRequestHandler(w http.ResponseWriter, r *http.Request) {
60 | roomID, ok := r.URL.Query()["roomID"]
61 |
62 | if !ok {
63 | log.Println("roomID missing in URL Parameters")
64 | return
65 | }
66 |
67 | ws, err := upgrader.Upgrade(w, r, nil)
68 | if err != nil {
69 | log.Fatal("Web Socket Upgrade Error", err)
70 | }
71 |
72 | AllRooms.InsertIntoRoom(roomID[0], false, ws)
73 |
74 | go broadcaster()
75 |
76 | for {
77 | var msg broadcastMsg
78 |
79 | err := ws.ReadJSON(&msg.Message)
80 | if err != nil {
81 | log.Fatal("Read Error: ", err)
82 | }
83 |
84 | msg.Client = ws
85 | msg.RoomID = roomID[0]
86 |
87 | log.Println(msg.Message)
88 |
89 | broadcast <- msg
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/video-chat-app:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/junaidrahim/webrtc-session/55abe4911904d378f9cc5fe42a9c10995f02154f/video-chat-app
--------------------------------------------------------------------------------