{"id":341577,"date":"2022-11-23T03:00:13","date_gmt":"2022-11-23T03:00:13","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=341577"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=341577","title":{"rendered":"<span>\u041a\u0430\u043a \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0430-\u043b\u044f Google Meet \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e PeerJS, SocketIO \u0438 NextJS<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u0434\u043e\u0440\u043e\u0433\u0438\u0435 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u0438 \u0425\u0430\u0431\u0440. \u041c\u044b \u0434\u043e\u043b\u0433\u043e \u0434\u0443\u043c\u0430\u043b\u0438, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e \u043e\u0442 \u043d\u0430\u0441 \u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0438 \u043e\u0431\u0443\u0447\u0430\u044e\u0449\u0435\u0435, \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043d\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0430\u043a \u043c\u044b \u043f\u0440\u0438\u0448\u043b\u0438 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043d\u0430\u043c \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0441\u0444\u0435\u0440\u0443 WebRTC \u0438 WebSockets \u0438 \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 Google Meet c \u0435\u00eb \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u0444\u0438\u0447\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0447\u0443\u0442\u044c \u043d\u0438\u0436\u0435. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0441\u0435 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443 \ud83d\ude42 \u041f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u044c\u0442\u0435\u0441\u044c, \u0431\u0443\u0434\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430!<\/p>\n<h3>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/h3>\n<p>\u0414\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 <a href=\"https:\/\/nextjs.org\" rel=\"noopener noreferrer nofollow\"><strong>NextJS<\/strong><\/a>, \u044d\u0442\u043e\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0434\u0430\u0451\u0442 \u043d\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c &#171;endpoint handler&#187; \u0432 \u0434\u0432\u0430 \u0449\u0435\u043b\u0447\u043a\u0430 (\u044d\u0442\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f SocketIO), \u043f\u043b\u044e\u0441 \u0441\u0443\u043f\u0435\u0440 \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043f\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c.<\/p>\n<p>\u0414\u043b\u044f \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u0430\u0448 \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 <a href=\"https:\/\/tailwindcss.com\" rel=\"noopener noreferrer nofollow\"><strong>TailwindCSS<\/strong><\/a>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0441 \u043d\u0438\u043c \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0438 \u043d\u0430\u043c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u041f\u0440\u043e\u0447\u0438\u0442\u0430\u0432 \u043f\u0430\u0440\u0443 \u0442\u0440\u043e\u0439\u043a\u0443 \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0434\u0435\u043e, \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u0447\u0442\u043e <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WebRTC_API\" rel=\"noopener noreferrer nofollow\"><strong>WebRTC<\/strong><\/a> \u0432 \u0447\u0438\u0441\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0433\u0440\u043e\u043c\u043e\u0437\u0434\u043a\u0438\u043c \u0438 \u043d\u0435\u043f\u043e\u0432\u043e\u0440\u043e\u0442\u043b\u0438\u0432\u044b\u043c. \u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u044d\u0442\u0438\u043c \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u043c\u0438 \u0441\u0442\u043e\u044f\u043b \u0432\u044b\u0431\u043e\u0440 \u043c\u0435\u0436\u0434\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 <a href=\"https:\/\/peerjs.com\" rel=\"noopener noreferrer nofollow\"><strong>PeerJS<\/strong><\/a> \u0438 <a href=\"https:\/\/github.com\/feross\/simple-peer\" rel=\"noopener noreferrer nofollow\"><strong>Simple-Peer<\/strong><\/a>. \u041f\u043e\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0432 \u043e\u0431\u0435\u0438\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438, \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 PeerJS. PeerJS \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a STUN server, ICE candidate, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u044c\u0441\u044f \u043d\u0430\u0441\u0447\u00eb\u0442 \u044d\u0442\u043e\u0433\u043e. \u0414\u043b\u044f \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0441\u0443\u043f\u0435\u0440 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0438\u0437-\u0437\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u043a\u0443\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0432 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438.<\/p>\n<p>\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430\u0445 WebRTC \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u0443\u0447\u0438\u0442\u044c <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/API\/WebRTC_API\/Protocols\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0441 WebRTC \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u0443 \u0432\u0430\u0441 \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c <strong>Signaling Server<\/strong> \u0432 \u0440\u043e\u043b\u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430. \u042d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0432\u0430\u043c \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u043c\u0438 \u0438 \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u0435\u0445 \u0438\u043b\u0438 \u0438\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 Signaling Server \u0443 \u043d\u0430\u0441 \u0441\u043b\u0443\u0436\u0438\u0442 <a href=\"https:\/\/socket.io\" rel=\"noopener noreferrer nofollow\"><strong>SocketIO<\/strong><\/a>.<\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0430\u043d\u0442\u0443\u0440\u0430\u0436\u0430, \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u0440\u0443\u0442\u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/github.com\/auth0\/nextjs-auth0\" rel=\"noopener noreferrer nofollow\"><strong>Auth0<\/strong><\/a>.<\/p>\n<h3>\u0424\u0438\u0447\u0438<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a\u0438\u0435 \u0436\u0435 \u0444\u0438\u0447\u0438 \u043d\u0430\u0441 \u0436\u0434\u0443\u0442 \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430-\u043b\u043e\u0431\u0431\u0438 \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0435\u0440\u0435\u0434 \u0432\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u043e\u0441\u043e\u0431\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a\u0430\u043c\u0435\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u043a\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<\/li>\n<li>\n<p>\u0441\u043f\u0438\u0441\u043e\u043a \u0433\u043e\u0441\u0442\u0435\u0439 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441 \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0445 \u0444\u0438\u0447 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u0437\u0430\u0434\u0430\u0447\u0438. \u041d\u0438\u0436\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043d\u0430\u0448\u0438 <strong>Pull Request-\u044b<\/strong> \u0438 <strong>\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Tailwind &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/49\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a>, <a href=\"https:\/\/tailwindcss.com\/docs\/guides\/nextjs\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f Auth0 &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/50\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a>, <a href=\"https:\/\/auth0.com\/docs\/quickstart\/spa\/react#install-the-auth0-react-sdk\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 SocketIO \u0441 NextJS &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/53\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0438\u0447, \u043c\u044b \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u0434-\u0441\u043d\u0438\u043f\u043f\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043e\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438 \u0432\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043b\u0435\u0433\u0447\u0435 \u0441\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0447\u0442\u0435\u043d\u0438\u0439.<\/p>\n<pre><code class=\"markdown\">|- app |----| index.tsx |- components |----| lobby.tsx |----| control-panel.tsx |----| chat.tsx |----| status.tsx |- contexts |----| users-connection.tsx |----| users-settings.tsx |- hooks |----| use-is-audio-active.ts |----| use-media-stream.ts |----| use-peer.ts |----| use-screen.ts |- pages |----| index.tsx |----| room |--------| [roomId].tsx<\/code><\/pre>\n<h3>\u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443-\u043b\u043e\u0431\u0431\u0438<\/h3>\n<p>\u041d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0435\u0441\u0442\u044c \u0434\u0432\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438: \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0438\u043b\u0438 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439. \u041d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443-\u043b\u043e\u0431\u0431\u0438, \u0433\u0434\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b \u0441\u0432\u043e\u00eb \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e \u043f\u0435\u0440\u0435\u0434 \u0432\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u041b\u043e\u0433\u0438\u043a\u0430 &#171;\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b-\u043b\u043e\u0431\u0431\u0438&#187; \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 <strong>useState<\/strong>.<\/p>\n<pre><code class=\"typescript\">\/\/ pages\/[roomId].tsx  export default function Room(): NextPage {   const [isLobby, setIsLobby] = useState(true);   const { stream } = useMediaStream();      return isLobby     ? &lt;Lobby stream={stream} onJoinRoom={() => setIsLobby(false)} \/>     : &lt;Room stream={stream} \/>; }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0432\u0438\u0434\u0435\u043e \u0438 \u0430\u0443\u0434\u0438\u043e, \u0430 \u043e\u043d\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0438 \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u043e\u043d \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c. \u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Media_Capture_and_Streams_API\" rel=\"noopener noreferrer nofollow\"><strong>MediaCapture \u0438 Streams API<\/strong><\/a> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0430\u043c \u043f\u043e\u0442\u043e\u043a (stream) \u043c\u0435\u0434\u0438\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0442\u043e\u043a \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0442\u0440\u0435\u043a\u043e\u0432 (tracks), \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a \u0432\u0438\u0434\u0435\u043e \u0438 \u0430\u0443\u0434\u0438\u043e. \u041d\u0430\u0448 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a <a href=\"https:\/\/www.npmjs.com\/package\/webrtc-webaudio-hooks\" rel=\"noopener noreferrer nofollow\"><strong>useMediaStream<\/strong><\/a> \u0431\u0435\u0440\u00eb\u0442 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u044e \u043d\u0430\u0434 \u043f\u043e\u0442\u043e\u043a\u043e\u043c.<\/p>\n<p>\u041d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435-\u043b\u043e\u0431\u0431\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0434\u0432\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0441\u0442\u0440\u0438\u043c\u0430<\/p>\n<\/li>\n<li>\n<p>\u0432\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">\/\/ components\/lobby.tsx \/\/ pseudocode  const Lobby = ({   stream,   onJoinRoom, }: {   stream: MediaStream;   onJoinRoom: () => void; }) => {   const { toggleAudio, toggleVideo } = useMediaStream(stream);      return (     &lt;>         &lt;video srcObject={stream} \/>         &lt;button onClick={toggleVideo}>Toggle video&lt;\/button>         &lt;button onClick={toggleAudio}>Toggle audio&lt;\/button>         &lt;button onClick={onJoinRoom}>Join&lt;\/button>     &lt;\/>   ); };<\/code><\/pre>\n<blockquote>\n<p><em>\u041f\u0440\u043e\u0441\u0438\u043c \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 track.enabled = true (track  &#8212; audio \/ video), \u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u043a\u0430 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0437 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u043a \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044f\u043c \u0431\u0435\u0437 \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u043e\u0432 (WebSocket, PeerJS), \u0438\u043d\u0430\u0447\u0435, \u0432\u044b\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043f\u0443\u0441\u0442\u044b\u0435 \u043a\u0430\u0434\u0440\u044b. \u0418\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u044d\u0442\u043e\u0433\u043e, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u044d\u0439\u0442 \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e.<\/em><\/p>\n<\/blockquote>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b-\u043b\u043e\u0431\u0431\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/blob\/project-4-video-chat-app\/project-4\/components\/lobby\/index.tsx\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<h3>\u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443<\/h3>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435-\u043b\u043e\u0431\u0431\u0438 \u0432\u044b\u0431\u0440\u0430\u043b \u0430\u0443\u0434\u0438\u043e \u0438 \u0432\u0438\u0434\u0435\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0438 \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u041d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u043c\u0435\u043d\u0442\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432\u0441\u00eb \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 ?\u00a0?\u00a0?. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 <strong>\u0434\u0432\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438<\/strong> \u0434\u043b\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438, \u043a\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u0435: <strong>peer<\/strong> \u0438 <strong>socket<\/strong>. Peer c\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0441\u0442\u0440\u0438\u043c\u0430 \u0434\u0440\u0443\u0433\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u0430 Socket \u0441\u043b\u0443\u0436\u0438\u0442 \u0432 \u0440\u043e\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0447\u0438\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0438\u043c\u0430.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043e <strong>roomId<\/strong> (\u0431\u0435\u0440\u0435\u043c \u0438\u0437 useRouter) \u0438 <strong>user<\/strong> (\u0431\u0435\u0440\u0435\u043c \u0438\u0437 useUser), \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c <strong>peer<\/strong> \u0438 \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c, \u0447\u0442\u043e \u0432\u044b \u0442\u043e\u0436\u0435 \u0432 \u0442\u0443\u0441\u043e\u0432\u043a\u0435.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-peer.ts \/\/ core part of the code  \/\/ open connection peer.on('open', (id: PeerId) => {   \/\/ tell others new user joined the room   socket.emit('room:join', {     roomId, \/\/ which room to connect to     user: { id, name: user.name, muted, visible } \/\/ joining user's data   }); });<\/code><\/pre>\n<p>\u0421\u0430\u043c \u0445\u0443\u043a \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e peer <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/blob\/project-4-video-chat-app\/project-4\/hooks\/use-peer.ts\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430-\u043a\u043e\u043c\u043d\u0430\u0442\u0430 \u043d\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u043a\u0430\u0445 \u0441 \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c:<\/p>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx \/\/ pseudocode  \/\/ stream comes from Lobby page export default App({ stream }: { stream: MediaStream }) => {   const socket = useContext(SocketContext);   const peer = usePeer(stream);      return (     &lt;UsersStateContext.Provider>       &lt;UsersConnectionContext.Provider value={{ peer, stream }}>         &lt;MyStream \/>         &lt;OthersStream \/>         &lt;ControlPanel \/>       &lt;\/UsersConnectionContext.Provider>     &lt;\/UsersStateContext.Provider> );  };<\/code><\/pre>\n<p><strong>UsersStateContext<\/strong> \u0431\u0435\u0440\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c <strong>UsersConnectionContext<\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u044e: \u0432\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443, \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438, \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b \u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430. \u0414\u0430, \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u044d\u043a\u0440\u0430\u043d\u0430 \u043c\u044b \u0432\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0432 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u044e, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u0440\u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u00eb\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u043e\u0431 \u044d\u0442\u043e\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435)<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0437\u0430\u0448\u043b\u0438 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0435\u0441\u044f \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0442\u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u043e\u043c\u0443 \u0433\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435\u043c, \u0434\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0442\u0440\u0438\u043c \u0438 \u0438\u043c\u044f \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ event is listened on users who are already in the room socket.on('user:joined', ({ id, name }: UserConfig) => {   \/\/ call to newly joined user's id with my stream and my name   const call = peer.call(id, stream, {     metadata: {       username: user.name,     },   }); });<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0448 <strong>Signaling Server<\/strong> &#8212; socket \u043a\u0430\u043a \u0431\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0432\u0441\u0435\u043c \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u2018\u0419\u043e\u0443, \u0442\u0443\u0442 \u0443 \u043d\u0430\u0441 \u043d\u043e\u0432\u044b\u0439 \u0433\u043e\u0441\u0442\u044c\u2019 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>user:joined<\/code> , \u0438 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u0442\u043e \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043a \u0433\u043e\u0441\u0442\u044e \u0438 \u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u0441\u044f, \u0434\u0430\u00eb\u0442 \u0435\u043c\u0443 \u0441\u0432\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445 (stream), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0432\u043e\u00eb \u0438\u043c\u044f. \u0410 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0438\u043c\u044f, \u0441\u0442\u0440\u0438\u043c \u0438 \u0430\u0439\u0434\u0438\u0448\u043d\u0438\u043a \u043d\u043e\u0432\u0438\u0447\u043a\u0430:<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ newly joined user's stream, name and id call.on('stream', (stream) => appendVideoStream({ id, name })(stream));<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u0430 \u0441 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438, \u043d\u043e\u0432\u0438\u0447\u043e\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0431\u0435 \u0438\u0445 \u0441\u0442\u0440\u0438\u043c\u044b:<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ action below happens on the newly joined user's device peer.on('call', (call) => {   const { peer, metadata } = call;   const { user } = metadata;      \/\/ answers incoming call with the stream   call.answer(stream);       \/\/ stream, name and id of user who was already in the room   call.on('stream', (stream) => appendVideoStream({ id: peer, name: user.name })(stream)); });<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u043c\u043e\u0433\u0443\u0442 \u0432\u0438\u0434\u0435\u0442\u044c \u0438 \u0441\u043b\u044b\u0448\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0435\u0433\u043e\u0441\u044f \u043d\u043e\u0432\u0438\u0447\u043a\u0430, \u043a\u0430\u043a \u0438 \u043e\u043d \u0438\u0445.<\/p>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f<\/h3>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432 \u0440\u043e\u043b\u044c \u0432\u0441\u0442\u0443\u043f\u0430\u0435\u0442 &#171;\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f&#187; \u043d\u0430\u0434 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0441\u0442\u0440\u0438\u043c\u043e\u043c:<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0430\u0443\u0434\u0438\u043e<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u0438\u0434\u0435\u043e<\/p>\n<\/li>\n<li>\n<p>\u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx  &lt;ControlPanel   visible={visible}   muted={muted}   onLeave={() => router.push('\/')}   onToggle={onToggle} \/><\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 <strong>onLeave<\/strong> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b. \u041c\u0435\u0442\u043e\u0434 <strong>onToggle<\/strong> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0435\u0439 \u044d\u043a\u0440\u0430\u043d\u0430. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx  \/\/ only related part of the code const { toggleAudio, toggleVideo } = useMediaStream(stream); const { myId } = usePeer(stream); const { startShare, stopShare, screenTrack } = useScreen(stream);  async function onToggle(   kind: Kind,   users?: MediaConnection[] ) {   switch (kind) {     case 'audio': {       toggleAudio();       socket.emit('user:toggle-audio', myId);       return;     }     case 'video': {       toggleVideo(         (newVideoTrack: MediaTrack) => {           users.forEach((user) => replaceTrack(user)(newVideoTrack))         }       );       socket.emit('user:toggle-video', myId);       return;     }     case 'screen': {       if (screenTrack) {         stopShare(screenTrack);         socket.emit('user:stop-share-screen');       } else {         await startShare(           () => socket.emit('user:share-screen'),           () => socket.emit('user:stop-share-screen')         );       }       return;     }     default:       break;   } }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 <strong>toggleAudio<\/strong> \u0438 <strong>toggleVideo<\/strong> \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 <em>\u043f\u043e\u0447\u0442\u0438<\/em> \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u043e: \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <strong>muted\/visible<\/strong> \u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <strong>enabled<\/strong> \u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0442\u0440\u0435\u043a\u0430, \u043d\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u043d\u044e\u0430\u043d\u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0445 \u0440\u0430\u0437\u043b\u0438\u0447\u0430\u0435\u0442, \u043e\u0431 \u044d\u0442\u043e\u043c \u0447\u0443\u0442\u044c \u043d\u0438\u0436\u0435.<\/p>\n<h3>\u0414\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430<\/h3>\n<p>\u0414\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0435\u0439 \u044d\u043a\u0440\u0430\u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a <a href=\"https:\/\/www.npmjs.com\/package\/webrtc-webaudio-hooks\" rel=\"noopener noreferrer nofollow\"><strong>useScreen<\/strong><\/a>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u043c\u0435\u0442\u043e\u0434\u044b <strong>startShare<\/strong> \u0438 <strong>stopShare<\/strong>.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-screen.ts  async function startShare(   onstarted: () => void,   onended: () => void ) {   const screenStream = await navigator.mediaDevices.getDisplayMedia({     video: true,     audio: false,   });   const [screenTrack] = screenStream.getTracks();   setScreenTrack(screenTrack);   stream.addTrack(screenTrack);      onstarted();      \/\/ once screen is shared, tiny popup will appear with two buttons - Stop sharing, Hide   \/\/ they are NOT custom, and come as they are   \/\/ so .onended is triggered when user clicks \"Stop sharing\"   screenTrack.onended = () => {     stopShare(screenTrack);     onended();   }; }<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u044d\u043a\u0440\u0430\u043d\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0441\u0442\u0440\u0438\u043c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0443 \u0432 \u0441\u0432\u043e\u0439 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u0442\u0440\u0438\u043c. \u0412 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435, \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u043e\u0442\u043e\u043a\u0435 \u043e\u0434\u043d\u0430 \u0430\u0443\u0434\u0438\u043e- \u0438 \u0434\u0432\u0435 \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0438 (userMediaTrack, displayMediaTrack). \u0414\u0430\u043b\u0435\u0435 \u043c\u044b \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0435\u043c <strong>user:share-screen<\/strong>, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u0438 \u0440\u0435\u0430\u0433\u0438\u0440\u0443\u044e\u0442 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <strong>user:shared-screen<\/strong> \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 peer \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0446\u0435\u043b\u044c\u044e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u00eb\u043d\u043d\u044b\u0439 \u0441\u0442\u0440\u0438\u043c \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u0440\u043e\u0436\u043a\u043e\u0439 \u044d\u043a\u0440\u0430\u043d\u0430.<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  socket.on('user:shared-screen', () => {   \/\/ peer connection reset   peer.disconnect();   peer.reconnect(); });<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u044d\u043a\u0440\u0430\u043d\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0435: \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0443 \u044d\u043a\u0440\u0430\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <strong>stop<\/strong> \u0438 \u0443\u0431\u0438\u0440\u0430\u0435\u043c \u0435\u00eb \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0442\u0440\u0438\u043c\u0430.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-screen.ts  function stopShare(screenTrack: MediaStreamTrack) {   screenTrack.stop();   stream.removeTrack(screenTrack); } <\/code><\/pre>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/h3>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u0441\u043e\u0437\u0434\u0430\u0432\u0448\u0438\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u0443, \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0432\u0443\u043a \u0438 \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0431\u0438\u0440\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b. \u041e\u0442 \u043b\u0438\u0446\u0430 \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044f \u043a\u043e\u043c\u043d\u0430\u0442\u044b \u044d\u0442\u043e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0437\u0430\u043c\u0435\u0442\u0435\u043d \u043f\u0440\u0438 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e-\u0441\u0442\u0440\u0438\u043c \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (<em>on hover<\/em>), \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441 \u0434\u0432\u0443\u043c\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u043c\u0438 \u043f\u043e\u0432\u0435\u0440\u0445 \u0432\u0438\u0434\u0435\u043e \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0437\u0432\u0443\u043a\u0430 \u0438 \u0432\u044b\u0432\u043e\u0434\u0430 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b. \u0412\u043d\u0438\u0437\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u0441\u0430\u043c\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f:<\/p>\n<pre><code class=\"typescript\">\/\/ components\/video-container\/index.tsx \/\/ pseudocode  \/\/ wrapper around the stream takes a responsibility to render \/\/ corresponding component or icon depending on the state of stream function VideoContainer({   children,   id,   onMutePeer,   onRemovePeer }: {   children: React.ReactNode,   id: PeerId,   onMutePeer: (id: PeerId) => void,   onRemovePeer: (id: PeerId) => void }) {   return (     &lt;>       &lt;div>         \/* here goes video stream component *\/         {children}       &lt;\/div>            \/* show host control panel if I created the room *\/       {isHost &amp;&amp; (myId !== id) &amp;&amp; (         &lt;HostControlPanel           onMutePeer={() => onMutePeer &amp;&amp; onMutePeer(id)}           onRemovePeer={() => onRemovePeer &amp;&amp; onRemovePeer(id)}           isMuted={muted}         \/>       )}     &lt;\/>   ) }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0447\u0442\u043e \u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0442\u0438 \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 <strong>onMutePeer<\/strong> \u0438 <strong>onRemovePeer<\/strong>? \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u00eb\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443, \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c <strong>onMutePeer<\/strong>. \u0422\u0430\u043a \u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0440\u0438\u043c\u0430 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0439, \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u0438\u0433\u043d\u0430\u043b \u0441 \u0430\u0439\u0434\u0438\u0448\u043d\u0438\u043a\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u0435\u0431\u044f, \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0438\u043a\u043e\u043d\u043a\u0443 \u043e \u0442\u043e\u043c \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u044e\u0437\u0435\u0440 \u0431\u044b\u043b \u043f\u0440\u0438\u0433\u043b\u0443\u0448\u0435\u043d.<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u043d\u0435 \u0432\u0441\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u0441 \u0441\u043e\u0431\u044b\u0442\u0438\u0435\u043c <strong>onRemovePeer<\/strong>, \u0442\u0443\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0430\u0436 3 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0434\u0430\u0442\u044c \u0441\u0438\u0433\u043d\u0430\u043b \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0441 \u0430\u0439\u0434\u0438\u0448\u043d\u0438\u043a\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043e\u0442\u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e<\/p>\n<\/li>\n<li>\n<p>\u0432 \u0441\u0432\u043e\u0435\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0443\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0435\u0439\u0442 streams<\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u043a\u0440\u044b\u0442\u044c peer \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  function leaveRoom(id: PeerId) {   \/\/ \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u043c \u0432\u0441\u0435\u043c   socket.emit('user:leave', id);      \/\/ \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435   users[id].close();      \/\/ \u0443\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e   setStreams((streams) => {     const copy = {...streams};     delete copy[id];     return copy;   }); }<\/code><\/pre>\n<h3>\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a\u0430\u043c\u0435\u0440\u044b<\/h3>\n<p>\u0412\u043e\u0442 \u043c\u044b \u0438 \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u043d\u044e\u0430\u043d\u0441\u0443 \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u044f \u043c\u0435\u0436\u0434\u0443 <strong>toggleAudio<\/strong> \u0438 <strong>toggleVideo<\/strong>.<\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0432\u0438\u0434\u0435\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <strong>enabled<\/strong>, \u043d\u043e \u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0442\u0435\u043c \u0441\u0430\u043c\u044b\u043c \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u044f, \u0447\u0442\u043e \u043a\u0430\u043c\u0435\u0440\u0430 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0430. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430:<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-media-stream.ts  \/\/ @param onTurnVideoOn - optional callback that takes newly created video track async function toggleVideo(onTurnVideoOn?: (track: MediaTrack) => void) {   const videoTrack = stream.getVideoTracks()[0];      if (videoTrack.readyState === 'live') {     videoTrack.enabled = false;     videoTrack.stop(); \/\/ turns off web cam light indicator   } else {     const newStream = await navigator.mediaDevices.getUserMedia({       video: true,     audio: false,     });     const newVideoTrack = newStream.getVideoTracks()[0];        if (typeof onTurnVideoOn === 'function') onTurnVideoOn(newVideoTrack);        stream.removeTrack(videoTrack);     stream.addTrack(newVideoTrack);      setStream(stream);   } }<\/code><\/pre>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>false<\/strong> \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <strong>enabled<\/strong> \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0438 \u043d\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0441\u0432\u0435\u0442\u043e\u0432\u043e\u0439 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <strong>stop<\/strong> \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <strong>MediaTrack<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u0440\u0435\u043a \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d. \u0421\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>stop()<\/strong>, \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0442\u0440\u0435\u043a\u0430 <strong>readyState<\/strong> \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <strong>ended<\/strong>. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0443 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <strong>MediaTrack<\/strong> \u043d\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u0430 <strong>start<\/strong> \u0438\u043b\u0438 <strong>restart<\/strong>, \u043a\u0430\u043a \u0432\u044b \u0443\u0436\u0435 \u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u0442\u0440\u0438\u043c, \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0438\u0437 \u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0443, \u0430 \u0437\u0430\u0442\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u0443\u044e \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0443 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e.<\/p>\n<p>\u041f\u043e\u0441\u0442\u043e\u0439\u0442\u0435, \u043c\u044b \u0442\u0443\u0442 \u043c\u0435\u043d\u044f\u0435\u043c \u0443 \u0441\u0435\u0431\u044f \u0432\u0438\u0434\u0435\u043e-\u0434\u043e\u0440\u043e\u0436\u043a\u0443 \u043d\u0430\u043f\u0440\u0430\u0432\u043e \u0438 \u043d\u0430\u043b\u0435\u0432\u043e, \u0430 \u043a\u0430\u043a \u043e\u0431 \u044d\u0442\u043e\u043c \u0443\u0437\u043d\u0430\u044e\u0442 \u0434\u0440\u0443\u0433\u0438\u0435 \u043b\u044e\u0434\u0438 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435? \u0421\u043f\u043e\u043a\u043e\u0439\u043d\u043e, \u0444\u0443\u043d\u043a\u0446\u0438\u044f <strong>replaceTrack<\/strong> got your back:<\/p>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx  \/\/ @param track - new track to replace old track function replaceTrack(track: MediaStreamTrack) {   return (peer: MediaConnection) => {     const sender = peer.peerConnection       .getSenders()       .find((s) => s.track.kind === track.kind);          sender?.replaceTrack(track);   } }<\/code><\/pre>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u043c\u043e\u044f \u0432\u0435\u0431-\u043a\u0430\u043c\u0435\u0440\u0430 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u00eb\u043c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u043f\u0440\u0438 \u0435\u00eb \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438. \u041c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u00eb\u043c \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043b\u0431\u044d\u043a \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <strong>toggleVideo<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0432\u0438\u0434\u0435\u043e-\u0442\u0440\u0435\u043a. \u0412 \u043a\u043e\u043b\u0431\u044d\u043a\u0435 \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u0432\u043e\u0439 \u0441\u0442\u0430\u0440\u044b\u0439 \u0432\u0438\u0434\u0435\u043e-\u0442\u0440\u0435\u043a \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/RTCPeerConnection\" rel=\"noopener noreferrer nofollow\"><strong>RTCPeerConnection<\/strong><\/a> \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u043c <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/RTCPeerConnection\/getSenders\" rel=\"noopener noreferrer nofollow\"><strong>getSenders<\/strong><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/RTCRtpSender\" rel=\"noopener noreferrer nofollow\"><strong>RTCRtpSender<\/strong><\/a>. <strong>RTCRtpSender<\/strong> \u2013 \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442, \u0434\u0430\u044e\u0449\u0438\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043b\u044f \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438 \u043d\u0430\u0434 \u043c\u0435\u0434\u0438\u0430-\u0442\u0440\u0435\u043a\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0432\u0441\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c.<\/p>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a\u0438\u0435 \u043f\u0438\u0440\u043e\u0433\u0438, \u0433\u043e\u0441\u043f\u043e\u0434\u0430.<\/p>\n<h3>\u0418\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u043a\u0435\u0440\u0430<\/h3>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u044b \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0438 \u0432 Google Meet \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0439, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0443\u0447\u0430\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u0435, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0430\u044f \u0441\u0438\u043d\u044f\u044f \u0438\u043a\u043e\u043d\u043a\u0430 \u0432 \u0432\u0438\u0434\u0435 \u044d\u043a\u0432\u0430\u043b\u0430\u0439\u0437\u0435\u0440\u0430, \u0442\u0435\u043c \u0441\u0430\u043c\u044b\u043c \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u044f, \u0447\u0442\u043e \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0432 \u0434\u0430\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f. \u0421\u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e. \u0412\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u043d\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0430\u0443\u0434\u0438\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0432 \u043a\u0430\u0441\u0442\u043e\u043c \u0445\u0443\u043a\u0435 <strong>useIsAudioActive<\/strong>.  \u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0432\u043d\u0438\u043a\u0430\u0442\u044c \u0438 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0443\u0436\u0435\u043d \u0442\u0430\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u0432\u043e\u0442 \u0432\u0430\u043c <a href=\"https:\/\/www.npmjs.com\/package\/webrtc-webaudio-hooks#use-is-audio-active\" rel=\"noopener noreferrer nofollow\">npm-\u043f\u0430\u043a\u0435\u0442<\/a>.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0430\u0443\u0434\u0438\u043e \u044d\u0442\u043e \u043f\u043e\u0442\u043e\u043a \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u043f\u0440\u0435\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0447\u0442\u043e-\u0442\u043e \u0446\u0435\u043b\u043e\u0435, WebAPI \u043d\u0430\u043c \u0434\u0430\u00eb\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/AudioContext\" rel=\"noopener noreferrer nofollow\"><strong>AudioContext<\/strong><\/a>. <strong>AudioContext<\/strong> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u044b \u043f\u043e\u0442\u043e\u043a\u0430. \u0418\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043e\u0442\u0440\u0435\u0437\u043a\u0435, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438\u0437 WebAudio API &#8212; <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/AnalyserNode\" rel=\"noopener noreferrer nofollow\"><strong>AnalyserNode<\/strong><\/a>.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-is-audio-active.ts  const audioContext = new AudioContext(); const analyser = new AnalyserNode(audioContext, { fftSize });  \/\/ source is a stream (MediaStream) const audioSource = audioContext.createMediaStreamSource(source);  \/\/ connect your audio source to output (usually laptop's mic), here it is analyser in terms of time domain audioSource.connect(analyser);<\/code><\/pre>\n<p>\u0412 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f FFT (Fast Fourier Transform), \u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <strong>requestAnimationFrame<\/strong> \u043c\u044b \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f \u0431\u0443\u043b\u0435\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443. \u041f\u043e \u0441\u0443\u0442\u0438 FFT \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u043a \u0447\u0430\u0441\u0442\u043e\u0442, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c <strong>analyser<\/strong> \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/AnalyserNode\/fftSize\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a> \u0438 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/AnalyserNode\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-is-audio-active.ts  \/\/ buffer length gives us how many different frequencies we are going to be measuring const bufferLength = analyser.frequencyBinCount;  \/\/ array with 512 length (half of FFT) and filled with 0-s const dataArray = new Uint8Array(bufferLength); update();  function update() {   \/\/ fills up dataArray with ~128 samples for each index   analyser.getByteTimeDomainData(dataArray);      const sum = dataArray.reduce((a, b) => a + b, 0);      if (sum \/ dataArray.length \/ 128.0 >= 1) {     setIsSpeaking(true);     setTimeout(() => setIsSpeaking(false), 1000);   }      requestAnimationFrame(update); }<\/code><\/pre>\n<h3>\u041e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/h3>\n<p>\u041a\u0430\u043a \u0437\u0430\u044f\u0432\u043b\u0435\u043d\u043e \u0432\u044b\u0448\u0435, \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c, \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <strong>chat:post<\/strong> \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c <strong>message<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0432\u0448\u0435\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u0440\u0435\u043c\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <strong>hh:mm<\/strong>. \u0414\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0443 \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u0435\u0439 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <strong>chat:get<\/strong>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0438\u043c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0435\u0439\u0442 <code>messages<\/code>, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0432 \u043d\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c.<\/p>\n<pre><code class=\"typescript\">\/\/ components\/chat\/index.tsx  function Chat() {   const [text, setText] = useState('');   const [messages, setMessages] = useState&lt;UserMessage[]>([]);      useEffect(() => {     socket.on('chat:get', (message: UserMessage) =>       setMessages(append(message))     );   }, []);      return (     &lt;>       &lt;MessagesContainer messages={messages}\/>       &lt;Input         value={text}         onChange={(e) => setText(e.target.value)}         onKeyDown={sendMessage}       \/>     &lt;\/>   ); }<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <strong>sendMessage<\/strong> \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435:<\/p>\n<pre><code class=\"typescript\">\/\/ components\/chat\/index.tsx  function sendMessage(e: React.KeyboardEvent&lt;HTMLInputElement>) {   if (e.key === 'Enter' &amp;&amp; text) {     const message = {       user: username,       text,       time: formatTimeHHMM(Date.now()),     };          socket.emit('chat:post', message);     setMessages(append(message));     setText('');   } }<\/code><\/pre>\n<h3>\u0421\u043f\u0438\u0441\u043e\u043a \u0433\u043e\u0441\u0442\u0435\u0439 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441 \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438<\/h3>\n<p>\u0414\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u0441 \u0438\u0445 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432\u044b\u0448\u0435\u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u044b\u0439 <strong>UsersStateContext<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445: id, \u0430\u0432\u0430\u0442\u0430\u0440, \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0442\u0440\u0438\u043c\u0430.<\/p>\n<pre><code class=\"typescript\">\/\/ components\/status\/index.tsx  const Status = ({ muted, visible }: { muted: boolean; visible: boolean }) => {   const { avatars, muted, visible, names } = useContext(UsersStateContext);   const usersIds = Object.keys(names);      return (     &lt;>       {usersIds.map((id) => (         &lt;div>           &lt;img src={avatars[id]} alt=\"User image\" \/>           &lt;span>{names[id]}&lt;\/span>           &lt;Icon variant={muted[id] ? 'muted' : 'not-muted'} \/>           &lt;Icon variant={visible[id] ? 'visible' : 'not-visible'} \/>         &lt;\/div>       ))}     &lt;\/>   ); };<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u044b \u0432\u0430\u043c \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0438 \u0432\u0441\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u0435\u043b\u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c. \u041d\u0430\u0434\u0435\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u0432\u0430\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c \u0438 \u0432\u044b \u0443\u0437\u043d\u0430\u043b\u0438 \u043a\u043e\u0435-\u0447\u0442\u043e \u043d\u043e\u0432\u043e\u0435.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041e\u043a\u043e\u043d\u0447\u0438\u0432 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442, \u043c\u044b \u043f\u043e\u043d\u044f\u043b\u0438, \u0447\u0442\u043e \u043c\u044b \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u044c WebRTC \u0438 WebSockets. \u041d\u043e \u0432\u0441\u00eb \u0436\u0435 \u043d\u0430\u043c \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0431\u043e\u043b\u0435\u0435-\u043c\u0435\u043d\u0435\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0438\u0447\u0438 \u0432\u0438\u0434\u0435\u043e-\u0447\u0430\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u041f\u043e \u0441\u0443\u0442\u0438 \u0434\u043b\u044f \u043d\u0430\u0441 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u0442\u0430\u043b \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0435\u0439 \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 WebRTC, WebSockets, Tailwind \u0438 Auth0. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/github.com\/evitla\/video-chat\/tree\/video-chat-v2\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>, \u0434\u0435\u043c\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2013 <a href=\"https:\/\/video-chat-123.herokuapp.com\/\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0438\u043c \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<p>P.S. \u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u0430\u0433\u043e\u0432, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u0437\u043d\u0430\u0435\u043c, \u0438, \u043f\u043e \u043c\u0435\u0440\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u0438\u0445 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/701002\/\"> https:\/\/habr.com\/ru\/post\/701002\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u0434\u043e\u0440\u043e\u0433\u0438\u0435 \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u0438 \u0425\u0430\u0431\u0440. \u041c\u044b \u0434\u043e\u043b\u0433\u043e \u0434\u0443\u043c\u0430\u043b\u0438, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e \u043e\u0442 \u043d\u0430\u0441 \u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0438 \u043e\u0431\u0443\u0447\u0430\u044e\u0449\u0435\u0435, \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043d\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0430\u043a \u043c\u044b \u043f\u0440\u0438\u0448\u043b\u0438 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043d\u0430\u043c \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0441\u0444\u0435\u0440\u0443 WebRTC \u0438 WebSockets \u0438 \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 Google Meet c \u0435\u00eb \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u0444\u0438\u0447\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0447\u0443\u0442\u044c \u043d\u0438\u0436\u0435. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0441\u0435 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443 \ud83d\ude42 \u041f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u044c\u0442\u0435\u0441\u044c, \u0431\u0443\u0434\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430!<\/p>\n<h3>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/h3>\n<p>\u0414\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 <a href=\"https:\/\/nextjs.org\" rel=\"noopener noreferrer nofollow\"><strong>NextJS<\/strong><\/a>, \u044d\u0442\u043e\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0434\u0430\u0451\u0442 \u043d\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c &#171;endpoint handler&#187; \u0432 \u0434\u0432\u0430 \u0449\u0435\u043b\u0447\u043a\u0430 (\u044d\u0442\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f SocketIO), \u043f\u043b\u044e\u0441 \u0441\u0443\u043f\u0435\u0440 \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043f\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c.<\/p>\n<p>\u0414\u043b\u044f \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u0430\u0448 \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 <a href=\"https:\/\/tailwindcss.com\" rel=\"noopener noreferrer nofollow\"><strong>TailwindCSS<\/strong><\/a>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0441 \u043d\u0438\u043c \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0438 \u043d\u0430\u043c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u041f\u0440\u043e\u0447\u0438\u0442\u0430\u0432 \u043f\u0430\u0440\u0443 \u0442\u0440\u043e\u0439\u043a\u0443 \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0434\u0435\u043e, \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u0447\u0442\u043e <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WebRTC_API\" rel=\"noopener noreferrer nofollow\"><strong>WebRTC<\/strong><\/a> \u0432 \u0447\u0438\u0441\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0433\u0440\u043e\u043c\u043e\u0437\u0434\u043a\u0438\u043c \u0438 \u043d\u0435\u043f\u043e\u0432\u043e\u0440\u043e\u0442\u043b\u0438\u0432\u044b\u043c. \u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u044d\u0442\u0438\u043c \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u043c\u0438 \u0441\u0442\u043e\u044f\u043b \u0432\u044b\u0431\u043e\u0440 \u043c\u0435\u0436\u0434\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 <a href=\"https:\/\/peerjs.com\" rel=\"noopener noreferrer nofollow\"><strong>PeerJS<\/strong><\/a> \u0438 <a href=\"https:\/\/github.com\/feross\/simple-peer\" rel=\"noopener noreferrer nofollow\"><strong>Simple-Peer<\/strong><\/a>. \u041f\u043e\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0432 \u043e\u0431\u0435\u0438\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438, \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 PeerJS. PeerJS \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a STUN server, ICE candidate, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u044c\u0441\u044f \u043d\u0430\u0441\u0447\u00eb\u0442 \u044d\u0442\u043e\u0433\u043e. \u0414\u043b\u044f \u043d\u0430\u0441 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0441\u0443\u043f\u0435\u0440 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0438\u0437-\u0437\u0430 \u043d\u0430\u0448\u0438\u0445 \u0441\u043a\u0443\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0432 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438.<\/p>\n<p>\u0411\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430\u0445 WebRTC \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u0443\u0447\u0438\u0442\u044c <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/API\/WebRTC_API\/Protocols\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0441 WebRTC \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u0443 \u0432\u0430\u0441 \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c <strong>Signaling Server<\/strong> \u0432 \u0440\u043e\u043b\u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430. \u042d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0432\u0430\u043c \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u043c\u0438 \u0438 \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u0435\u0445 \u0438\u043b\u0438 \u0438\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 Signaling Server \u0443 \u043d\u0430\u0441 \u0441\u043b\u0443\u0436\u0438\u0442 <a href=\"https:\/\/socket.io\" rel=\"noopener noreferrer nofollow\"><strong>SocketIO<\/strong><\/a>.<\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0430\u043d\u0442\u0443\u0440\u0430\u0436\u0430, \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u0440\u0443\u0442\u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/github.com\/auth0\/nextjs-auth0\" rel=\"noopener noreferrer nofollow\"><strong>Auth0<\/strong><\/a>.<\/p>\n<h3>\u0424\u0438\u0447\u0438<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a\u0438\u0435 \u0436\u0435 \u0444\u0438\u0447\u0438 \u043d\u0430\u0441 \u0436\u0434\u0443\u0442 \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430-\u043b\u043e\u0431\u0431\u0438 \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0435\u0440\u0435\u0434 \u0432\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u043e\u0441\u043e\u0431\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043f\u0440\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a\u0430\u043c\u0435\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u043a\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<\/li>\n<li>\n<p>\u0441\u043f\u0438\u0441\u043e\u043a \u0433\u043e\u0441\u0442\u0435\u0439 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441 \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0445 \u0444\u0438\u0447 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u0437\u0430\u0434\u0430\u0447\u0438. \u041d\u0438\u0436\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043d\u0430\u0448\u0438 <strong>Pull Request-\u044b<\/strong> \u0438 <strong>\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Tailwind &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/49\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a>, <a href=\"https:\/\/tailwindcss.com\/docs\/guides\/nextjs\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f Auth0 &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/50\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a>, <a href=\"https:\/\/auth0.com\/docs\/quickstart\/spa\/react#install-the-auth0-react-sdk\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a><\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 SocketIO \u0441 NextJS &#8212; <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/pull\/53\" rel=\"noopener noreferrer nofollow\">Pull Request<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0438\u0447, \u043c\u044b \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u0434-\u0441\u043d\u0438\u043f\u043f\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043e\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438 \u0432\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043b\u0435\u0433\u0447\u0435 \u0441\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0447\u0442\u0435\u043d\u0438\u0439.<\/p>\n<pre><code class=\"markdown\">|- app |----| index.tsx |- components |----| lobby.tsx |----| control-panel.tsx |----| chat.tsx |----| status.tsx |- contexts |----| users-connection.tsx |----| users-settings.tsx |- hooks |----| use-is-audio-active.ts |----| use-media-stream.ts |----| use-peer.ts |----| use-screen.ts |- pages |----| index.tsx |----| room |--------| [roomId].tsx<\/code><\/pre>\n<h3>\u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443-\u043b\u043e\u0431\u0431\u0438<\/h3>\n<p>\u041d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0435\u0441\u0442\u044c \u0434\u0432\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438: \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0438\u043b\u0438 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439. \u041d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443-\u043b\u043e\u0431\u0431\u0438, \u0433\u0434\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b \u0441\u0432\u043e\u00eb \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e \u043f\u0435\u0440\u0435\u0434 \u0432\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u041b\u043e\u0433\u0438\u043a\u0430 &#171;\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b-\u043b\u043e\u0431\u0431\u0438&#187; \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 <strong>useState<\/strong>.<\/p>\n<pre><code class=\"typescript\">\/\/ pages\/[roomId].tsx  export default function Room(): NextPage {   const [isLobby, setIsLobby] = useState(true);   const { stream } = useMediaStream();      return isLobby     ? &lt;Lobby stream={stream} onJoinRoom={() => setIsLobby(false)} \/>     : &lt;Room stream={stream} \/>; }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0432\u0438\u0434\u0435\u043e \u0438 \u0430\u0443\u0434\u0438\u043e, \u0430 \u043e\u043d\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0438 \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u043e\u043d \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c. \u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Media_Capture_and_Streams_API\" rel=\"noopener noreferrer nofollow\"><strong>MediaCapture \u0438 Streams API<\/strong><\/a> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0430\u043c \u043f\u043e\u0442\u043e\u043a (stream) \u043c\u0435\u0434\u0438\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0442\u043e\u043a \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0442\u0440\u0435\u043a\u043e\u0432 (tracks), \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a \u0432\u0438\u0434\u0435\u043e \u0438 \u0430\u0443\u0434\u0438\u043e. \u041d\u0430\u0448 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a <a href=\"https:\/\/www.npmjs.com\/package\/webrtc-webaudio-hooks\" rel=\"noopener noreferrer nofollow\"><strong>useMediaStream<\/strong><\/a> \u0431\u0435\u0440\u00eb\u0442 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u044e \u043d\u0430\u0434 \u043f\u043e\u0442\u043e\u043a\u043e\u043c.<\/p>\n<p>\u041d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435-\u043b\u043e\u0431\u0431\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0434\u0432\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0441\u0442\u0440\u0438\u043c\u0430<\/p>\n<\/li>\n<li>\n<p>\u0432\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"typescript\">\/\/ components\/lobby.tsx \/\/ pseudocode  const Lobby = ({   stream,   onJoinRoom, }: {   stream: MediaStream;   onJoinRoom: () => void; }) => {   const { toggleAudio, toggleVideo } = useMediaStream(stream);      return (     &lt;>         &lt;video srcObject={stream} \/>         &lt;button onClick={toggleVideo}>Toggle video&lt;\/button>         &lt;button onClick={toggleAudio}>Toggle audio&lt;\/button>         &lt;button onClick={onJoinRoom}>Join&lt;\/button>     &lt;\/>   ); };<\/code><\/pre>\n<blockquote>\n<p><em>\u041f\u0440\u043e\u0441\u0438\u043c \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 track.enabled = true (track  &#8212; audio \/ video), \u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0442\u0440\u0435\u043a\u0430 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0437 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u043a \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044f\u043c \u0431\u0435\u0437 \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u043e\u0432 (WebSocket, PeerJS), \u0438\u043d\u0430\u0447\u0435, \u0432\u044b\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043f\u0443\u0441\u0442\u044b\u0435 \u043a\u0430\u0434\u0440\u044b. \u0418\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u044d\u0442\u043e\u0433\u043e, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u044d\u0439\u0442 \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e.<\/em><\/p>\n<\/blockquote>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b-\u043b\u043e\u0431\u0431\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/blob\/project-4-video-chat-app\/project-4\/components\/lobby\/index.tsx\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<h3>\u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443<\/h3>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0447\u0435\u043b\u043e\u0432\u0435\u043a \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435-\u043b\u043e\u0431\u0431\u0438 \u0432\u044b\u0431\u0440\u0430\u043b \u0430\u0443\u0434\u0438\u043e \u0438 \u0432\u0438\u0434\u0435\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0438 \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u041d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u043c\u0435\u043d\u0442\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432\u0441\u00eb \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 ?\u00a0?\u00a0?. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 <strong>\u0434\u0432\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438<\/strong> \u0434\u043b\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438, \u043a\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u0435: <strong>peer<\/strong> \u0438 <strong>socket<\/strong>. Peer c\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0441\u0442\u0440\u0438\u043c\u0430 \u0434\u0440\u0443\u0433\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u0430 Socket \u0441\u043b\u0443\u0436\u0438\u0442 \u0432 \u0440\u043e\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0447\u0438\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0438\u043c\u0430.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043e <strong>roomId<\/strong> (\u0431\u0435\u0440\u0435\u043c \u0438\u0437 useRouter) \u0438 <strong>user<\/strong> (\u0431\u0435\u0440\u0435\u043c \u0438\u0437 useUser), \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c <strong>peer<\/strong> \u0438 \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c, \u0447\u0442\u043e \u0432\u044b \u0442\u043e\u0436\u0435 \u0432 \u0442\u0443\u0441\u043e\u0432\u043a\u0435.<\/p>\n<pre><code class=\"typescript\">\/\/ hooks\/use-peer.ts \/\/ core part of the code  \/\/ open connection peer.on('open', (id: PeerId) => {   \/\/ tell others new user joined the room   socket.emit('room:join', {     roomId, \/\/ which room to connect to     user: { id, name: user.name, muted, visible } \/\/ joining user's data   }); });<\/code><\/pre>\n<p>\u0421\u0430\u043c \u0445\u0443\u043a \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e peer <a href=\"https:\/\/github.com\/olzh2102\/dribbble-components\/blob\/project-4-video-chat-app\/project-4\/hooks\/use-peer.ts\" rel=\"noopener noreferrer nofollow\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430-\u043a\u043e\u043c\u043d\u0430\u0442\u0430 \u043d\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u043a\u0430\u0445 \u0441 \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c:<\/p>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx \/\/ pseudocode  \/\/ stream comes from Lobby page export default App({ stream }: { stream: MediaStream }) => {   const socket = useContext(SocketContext);   const peer = usePeer(stream);      return (     &lt;UsersStateContext.Provider>       &lt;UsersConnectionContext.Provider value={{ peer, stream }}>         &lt;MyStream \/>         &lt;OthersStream \/>         &lt;ControlPanel \/>       &lt;\/UsersConnectionContext.Provider>     &lt;\/UsersStateContext.Provider> );  };<\/code><\/pre>\n<p><strong>UsersStateContext<\/strong> \u0431\u0435\u0440\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0437\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c <strong>UsersConnectionContext<\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u044e: \u0432\u0445\u043e\u0434 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443, \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438, \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b \u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430. \u0414\u0430, \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u044d\u043a\u0440\u0430\u043d\u0430 \u043c\u044b \u0432\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0432 \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u044e, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u0440\u0438 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u00eb\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u043e\u0431 \u044d\u0442\u043e\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435)<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0437\u0430\u0448\u043b\u0438 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u0435\u0441\u044f \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0442\u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u043e\u043c\u0443 \u0433\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435\u043c, \u0434\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0442\u0440\u0438\u043c \u0438 \u0438\u043c\u044f \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ event is listened on users who are already in the room socket.on('user:joined', ({ id, name }: UserConfig) => {   \/\/ call to newly joined user's id with my stream and my name   const call = peer.call(id, stream, {     metadata: {       username: user.name,     },   }); });<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0448 <strong>Signaling Server<\/strong> &#8212; socket \u043a\u0430\u043a \u0431\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0432\u0441\u0435\u043c \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u2018\u0419\u043e\u0443, \u0442\u0443\u0442 \u0443 \u043d\u0430\u0441 \u043d\u043e\u0432\u044b\u0439 \u0433\u043e\u0441\u0442\u044c\u2019 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f <code>user:joined<\/code> , \u0438 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u0442\u043e \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u043a \u0433\u043e\u0441\u0442\u044e \u0438 \u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u0441\u044f, \u0434\u0430\u00eb\u0442 \u0435\u043c\u0443 \u0441\u0432\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445 (stream), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0432\u043e\u00eb \u0438\u043c\u044f. \u0410 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0438\u043c\u044f, \u0441\u0442\u0440\u0438\u043c \u0438 \u0430\u0439\u0434\u0438\u0448\u043d\u0438\u043a \u043d\u043e\u0432\u0438\u0447\u043a\u0430:<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ newly joined user's stream, name and id call.on('stream', (stream) => appendVideoStream({ id, name })(stream));<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u0430 \u0441 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438, \u043d\u043e\u0432\u0438\u0447\u043e\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0431\u0435 \u0438\u0445 \u0441\u0442\u0440\u0438\u043c\u044b:<\/p>\n<pre><code class=\"typescript\">\/\/ contexts\/users-connection.tsx  \/\/ action below happens on the newly joined user's device peer.on('call', (call) => {   const { peer, metadata } = call;   const { user } = metadata;      \/\/ answers incoming call with the stream   call.answer(stream);       \/\/ stream, name and id of user who was already in the room   call.on('stream', (stream) => appendVideoStream({ id: peer, name: user.name })(stream)); });<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u043c\u043e\u0433\u0443\u0442 \u0432\u0438\u0434\u0435\u0442\u044c \u0438 \u0441\u043b\u044b\u0448\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0435\u0433\u043e\u0441\u044f \u043d\u043e\u0432\u0438\u0447\u043a\u0430, \u043a\u0430\u043a \u0438 \u043e\u043d \u0438\u0445.<\/p>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f<\/h3>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0432 \u0440\u043e\u043b\u044c \u0432\u0441\u0442\u0443\u043f\u0430\u0435\u0442 &#171;\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f&#187; \u043d\u0430\u0434 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0441\u0442\u0440\u0438\u043c\u043e\u043c:<\/p>\n<ul>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0430\u0443\u0434\u0438\u043e<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u0438\u0434\u0435\u043e<\/p>\n<\/li>\n<li>\n<p>\u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<\/li>\n<li>\n<p>\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx  &lt;ControlPanel   visible={visible}   muted={muted}   onLeave={() => router.push('\/')}   onToggle={onToggle} \/><\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 <strong>onLeave<\/strong> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u043a\u043e\u043c\u043d\u0430\u0442\u044b. \u041c\u0435\u0442\u043e\u0434 <strong>onToggle<\/strong> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0430\u0443\u0434\u0438\u043e\/\u0432\u0438\u0434\u0435\u043e \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0435\u0439 \u044d\u043a\u0440\u0430\u043d\u0430. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"typescript\">\/\/ app\/index.tsx  \/\/ only related part of the code const { toggleAudio, toggleVideo } = useMediaStream(stream); const { myId } = usePeer(stream); const { startShare, stopShare, screenTrack } = useScreen(stream);  async function onToggle(   kind: Kind,   users?: MediaConnection[] ) {   switch (kind) {     case 'audio': {       toggleAudio();       socket.emit('user:toggle-audio', myId);       return;     }     case 'video': {       toggleVideo(         (newVideoTrack: MediaTrack) => {           users.forEach((user) => replaceTrack(user)(newVideoTrack))         }       );       socket.emit('user:toggle-video', myId);       return;     }     case 'screen': {       if (screenTrack) {         stopShare(screenTrack);         socket.emit('user:stop-share-screen');       } else {         await startShare(           () => socket.emit('user:share-screen'),           () => socket.emit('user:stop-share-screen')         );       }       return;     }     default:       break;   } }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 <strong>toggleAudio<\/strong> \u0438 <strong>toggleVideo<\/strong> \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 <em>\u043f\u043e\u0447\u0442\u0438<\/em> \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u043e: \u043c\u0435\u043d\u044f\u044e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <strong>muted\/visible<\/strong> \u0438<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-341577","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341577","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=341577"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341577\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=341577"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=341577"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=341577"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}