{"id":486228,"date":"2026-07-05T11:26:36","date_gmt":"2026-07-05T11:26:36","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=486228"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=486228","title":{"rendered":"\u0417\u0430\u043c\u0435\u0442\u043a\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u044f \u043f\u0438\u0441\u0430\u043b SFU \u043d\u0430 Rust (1 \u0447\u0430\u0441\u0442\u044c)"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0441 Rust \u0443\u0436\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u0435\u0442. \u0412 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c &#8212; \u0431\u044d\u043a\u0435\u043d\u0434, \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u043c\u043d\u043e\u0433\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0442\u043e\u0447\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u0410 \u0435\u0449\u0451 \u044f \u0434\u0430\u0432\u043d\u043e \u0445\u043e\u0442\u0435\u043b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 WebRTC. \u041d\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 &#171;\u0432\u043e\u0442 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b, \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438&#187;, \u0430 \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441 \u043f\u0430\u043a\u0435\u0442\u0430\u043c\u0438, \u043a\u0430\u043a \u043e\u043d\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f, \u043a\u0430\u043a \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0438\u0440\u0443\u044e\u0442\u0441\u044f.<\/p>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 SFU.<\/p>\n<p>\u041d\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430. \u041d\u0435 \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u043a\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 LiveKit \u0438\u043b\u0438 Jitsi. \u041f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u043e\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u0437\u043d\u0443\u0442\u0440\u0438. \u0418 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 Rust &#8212; \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u043c\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0438 \u043c\u043d\u0435 \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u043e\u0434\u0430 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f &#8212; \u043d\u0435 \u0433\u0430\u0439\u0434 &#171;\u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c SFU \u0437\u0430 5 \u043c\u0438\u043d\u0443\u0442&#187;. \u042d\u0442\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0434\u043d\u0435\u0432\u043d\u0438\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. <\/p>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u044f \u0431\u0443\u0434\u0443 \u0440\u0430\u0434, \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0443\u043a\u0430\u0436\u0435\u0442 \u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438\u043b\u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u043b\u0443\u0447\u0448\u0438\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u0446\u0435\u043b\u044c &#8212; \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0433\u043b\u0443\u0431\u0436\u0435.<\/p>\n<h3>\u0421 \u0447\u0435\u0433\u043e \u0432\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c<\/h3>\n<p>\u041f\u0430\u0440\u0443 \u043c\u0435\u0441\u044f\u0446\u0435\u0432 \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0441\u0442\u0430\u0442\u044c\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d WebRTC \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0412 \u043d\u0435\u0439 \u0431\u044b\u043b\u043e \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e SDP, ICE \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u044b, DTLS \u0440\u0443\u043a\u043e\u043f\u043e\u0436\u0430\u0442\u0438\u0435. \u042f \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b \u0438 \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432 \u043d\u0435 \u0437\u043d\u0430\u044e.<\/p>\n<p>\u0422\u043e\u0433\u0434\u0430 \u044f \u0440\u0435\u0448\u0438\u043b: \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u043d\u044f\u0442\u044c &#8212; \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u0430\u043c\u043e\u043c\u0443.<\/p>\n<p>\u042f \u043d\u0430\u0447\u0430\u043b \u0441 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e: \u0432\u0437\u044f\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0<a href=\"https:\/\/github.com\/webrtc-rs\/webrtc\" rel=\"noopener noreferrer nofollow\"><code>webrtc-rs<\/code><\/a>, \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u044d\u0445\u043e-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u043b \u0432\u0438\u0434\u0435\u043e \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u0435\u0433\u043e \u043e\u0431\u0440\u0430\u0442\u043d\u043e. \u0417\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e. \u041f\u043e\u0442\u043e\u043c \u044f \u0437\u0430\u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c \u0447\u0442\u043e \u043c\u043d\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0442\u0435\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u043d\u0435 \u043f\u043e\u043d\u044f\u043b \u0438\u0437 \u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0432\u0441\u0435 \u0442\u0430\u043a\u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u044b, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0435\u043e\u0440\u0438\u0438.<\/p>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 SFU \u0438 \u0437\u0430\u0447\u0435\u043c \u043e\u043d \u043d\u0443\u0436\u0435\u043d<\/h3>\n<p>WebRTC \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u043b\u0441\u044f \u043a\u0430\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c\u0438. Peer-to-peer. \u041e\u0434\u0438\u043d \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0442\u043e\u043a \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0434\u0440\u0443\u0433\u043e\u043c\u0443. \u0412\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e, \u043f\u043e\u043a\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u0432\u043e\u0435.<\/p>\n<p>\u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0445 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u0440\u043e\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443. \u042d\u0442\u043e \u0443\u0436\u0435 \u0442\u0440\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430. \u0414\u043b\u044f \u0447\u0435\u0442\u044b\u0440\u0451\u0445 &#8212; \u0448\u0435\u0441\u0442\u044c. \u0414\u043b\u044f \u0434\u0435\u0441\u044f\u0442\u0438 &#8212; \u0441\u043e\u0440\u043e\u043a \u043f\u044f\u0442\u044c. \u042d\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f mesh-\u0442\u043e\u043f\u043e\u043b\u043e\u0433\u0438\u044f, \u0438 \u043e\u043d\u0430 \u043d\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442\u0441\u044f &#8212; \u0440\u0430\u0441\u0442\u0451\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0438 \u0440\u0430\u0441\u0445\u043e\u0434 \u0442\u0440\u0430\u0444\u0438\u043a\u0430.<\/p>\n<p>SFU (Selective Forwarding Unit) \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443. \u042d\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0438\u0434\u0438\u0442 \u043c\u0435\u0436\u0434\u0443 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c\u0438. \u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 &#8212; \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043a \u0438 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e36\/2d5\/4ab\/e362d54abc418a64ffa5f8e93e925dd5.png\" width=\"1042\" height=\"650\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e36\/2d5\/4ab\/e362d54abc418a64ffa5f8e93e925dd5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e36\/2d5\/4ab\/e362d54abc418a64ffa5f8e93e925dd5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u041a\u0430\u043a\u0438\u0435 \u0444\u0438\u0447\u0438 \u044f \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u0441\u0432\u043e\u0439 SFU, \u044f \u0441\u0440\u0430\u0437\u0443 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b, \u0447\u0442\u043e \u0445\u043e\u0447\u0443:<\/p>\n<p><strong>1. \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043d\u0430 \u043b\u0435\u0442\u0443<\/strong><\/p>\n<p>\u041d\u0435 \u0443 \u0432\u0441\u0435\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u0430\u043d\u0430\u043b. \u041a\u0442\u043e-\u0442\u043e \u0441\u0438\u0434\u0438\u0442 \u0441 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430, \u043a\u0442\u043e-\u0442\u043e &#8212; \u0441 \u043e\u043f\u0442\u043e\u0432\u043e\u043b\u043e\u043a\u043d\u0430. SFU \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c 1080p \u0442\u0435\u043c, \u043a\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0435\u0433\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u044c, \u0438 360p &#8212; \u0442\u0435\u043c, \u0443 \u043a\u043e\u0433\u043e \u043f\u043b\u043e\u0445\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u0418 \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p><strong>2. \u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043a\u0430\u043d\u0430\u043b\u0430<\/strong><\/p>\n<p>\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442, \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0443\u0445\u0443\u0434\u0448\u0438\u0442\u044c\u0441\u044f. SFU \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u044c \u044d\u0442\u043e (\u043f\u043e\u0442\u0435\u0440\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438) \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u043d\u0438\u0436\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0432\u043e\u043d\u043e\u043a \u043d\u0435 \u043f\u0440\u0435\u0440\u0432\u0430\u043b\u0441\u044f.<\/p>\n<p><strong>3. \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430<\/strong><\/p>\n<p>SFU \u043d\u0435 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u0442 \u0432\u0438\u0434\u0435\u043e (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 MCU &#8212; Multipoint Control Unit). \u041e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442 \u043f\u0430\u043a\u0435\u0442\u044b. \u042d\u0442\u043e \u0434\u0430\u0451\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u0438 \u043d\u0438\u0437\u043a\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<h3>\u041a\u0430\u043a \u044d\u0442\u0438 \u0444\u0438\u0447\u0438 \u043f\u043e\u0432\u043b\u0438\u044f\u043b\u0438 \u043d\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443<\/h3>\n<p>\u042d\u0442\u0438 \u0442\u0440\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0432\u0441\u0451, \u0447\u0442\u043e \u044f \u0434\u0435\u043b\u0430\u043b \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p><strong>\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430<\/strong>\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e, \u0447\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e \u0441 \u0440\u0430\u0437\u043d\u044b\u043c \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u0438\u043d \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0438\u0434\u0435\u043e \u0432 1080p, 720p \u0438 360p \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. SFU \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u043e\u0442\u0434\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0443.<\/p>\n<p><strong>\u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c<\/strong>\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u0430, \u0447\u0442\u043e \u044f \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0430\u043d\u0430\u043b\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430. \u0415\u0441\u043b\u0438 \u0443\u0445\u0443\u0434\u0448\u0430\u0435\u0442\u0441\u044f &#8212; \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u043d\u0438\u0437\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e. \u0415\u0441\u043b\u0438 \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u0442\u0441\u044f &#8212; \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p><strong>\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430<\/strong>\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u0430, \u0447\u0442\u043e \u044f \u043d\u0435 \u043c\u043e\u0433\u0443 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0422\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0442\u044c RTP-\u043f\u0430\u043a\u0435\u0442\u044b. \u042d\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u043b\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443, \u043d\u043e \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u043b\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f: \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430, \u0430 \u043d\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u042d\u0442\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043c\u0435\u043d\u044f \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435:<\/p>\n<p><strong>1. \u041a\u043e\u043c\u043d\u0430\u0442\u0430 \u043a\u0430\u043a \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440<\/strong><\/p>\n<p>\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 RTP-\u043f\u0430\u043a\u0435\u0442\u044b \u0438 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 \u0438\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c \u0431\u0435\u0437 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a. \u041a\u043e\u043c\u043d\u0430\u0442\u0430 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0432 \u0440\u043e\u043b\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430: \u043e\u043d\u0430 \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u0443\u0435\u0442 \u043c\u0435\u0434\u0438\u0430 \u043e\u0442 Publisher \u0432\u043e \u0432\u0441\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p><strong>2. User (\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c)<\/strong><\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c User, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0440\u043e\u043b\u044f\u0445 &#8212; \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044f (Publisher) \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 (Subscriber). \u0420\u043e\u043b\u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043b\u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0438 \u0442\u043e, \u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. User \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u044d\u0442\u0438 \u0440\u043e\u043b\u0438 \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u0445 \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u043e\u0439.<\/p>\n<p><strong>3. Publisher (\u0418\u0437\u0434\u0430\u0442\u0435\u043b\u044c)<\/strong><\/p>\n<p>\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044f. Publisher \u0445\u0440\u0430\u043d\u0438\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0434\u0435\u043e\u0442\u0440\u0435\u043a\u043e\u0432 \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0438 \u0430\u0443\u0434\u0438\u043e\u0442\u0440\u0435\u043a, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0438 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0441\u043b\u043e\u0439 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430.<\/p>\n<p><strong>4. QualityMonitor (\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430)<\/strong><\/p>\n<p>\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0430\u043d\u0430\u043b\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e \u0441\u043c\u0435\u043d\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430. QualityMonitor \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c (\u043f\u043e\u0442\u0435\u0440\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438) \u0438 \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 Publisher, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.<\/p>\n<p><strong>5. \u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 (VideoSubscription \/ AudioSubscription)<\/strong><\/p>\n<p>\u041d\u0443\u0436\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043a\u0430\u043d\u0430\u043b\u044b \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 &#8212; \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a \u043d\u0435 \u0437\u0430\u043c\u0435\u0442\u0438\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430. VideoSubscription \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0441\u043b\u043e\u0438 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0440\u0435\u043a. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 Publisher \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0441\u043b\u043e\u0439, \u0430 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 &#8212; \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u044f \u043f\u0440\u043e\u0439\u0434\u0443\u0441\u044c \u043b\u0438\u0448\u044c \u043f\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432, \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u0441\u043c\u0435\u043d\u0443 \u0441\u043b\u043e\u0435\u0432 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ac5\/146\/9bd\/ac51469bd3adebdd5be6b3dc7b079fa7.png\" alt=\"\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\" title=\"\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\" width=\"1039\" height=\"725\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/ac5\/146\/9bd\/ac51469bd3adebdd5be6b3dc7b079fa7.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ac5\/146\/9bd\/ac51469bd3adebdd5be6b3dc7b079fa7.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/figcaption><\/div>\n<\/figure>\n<h3>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442<\/h3>\n<p>\u041c\u043d\u0435 \u043d\u0443\u0436\u0435\u043d \u0431\u044b\u043b \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434. \u041f\u0440\u044f\u043c\u044b\u0435 tokio-\u0442\u0430\u0441\u043a\u0438, Arc&lt;Mutex&gt;  \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u0433\u043e\u043d\u043a\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0445: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u0438\u043d \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0447\u0438\u0442\u0430\u043b \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432, \u043f\u043e\u043a\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0435\u0433\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043b. \u041d\u0443\u0436\u043d\u0430 \u0431\u044b\u043b\u0430 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e.<\/p>\n<p>\u0411\u0440\u0430\u0442\u044c Actix \u0440\u0430\u0434\u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0431\u044b\u043b\u043e \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u043d\u0430\u0434\u00a0tokio::spawn\u00a0+\u00a0mpsc::channel\u00a0\u0432 \u0434\u0443\u0445\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 Active Object:<\/p>\n<ul>\n<li>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0436\u0438\u0432\u0451\u0442 \u0432 \u0441\u0432\u043e\u0435\u0439 \u0442\u0430\u0441\u043a\u0435<\/p>\n<\/li>\n<li>\n<p>\u0412\u0441\u0451 \u043e\u0431\u0449\u0435\u043d\u0438\u0435 &#8212; \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u043d\u0430\u043b<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e &#8212; \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043d\u0443\u0442\u0440\u0438 \u0442\u0430\u0441\u043a\u0438, \u0431\u0435\u0437 \u043c\u044c\u044e\u0442\u0435\u043a\u0441\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0438\u043a\u0430\u043a\u043e\u0439 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u0441\u0443\u043f\u0435\u0440\u0432\u0438\u0437\u043e\u0440\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432 \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u0442 &#8212; \u0434\u043b\u044f \u043c\u043e\u0435\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u044f \u044d\u0442\u043e \u043d\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c. \u041f\u0430\u0434\u0435\u043d\u0438\u0435 \u0442\u0430\u0441\u043a\u0438 WebRTC-\u043f\u0438\u0440\u0430 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043b\u0441\u044f, \u0438 \u044d\u0442\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b.<\/p>\n<pre><code class=\"rust\">pub trait Actor: Sized + Send + 'static {    type Message: Sized + Send + 'static;    fn handle(&amp;mut self, ctx: &amp;mut Ctx&lt;'_, Self&gt;, m: Self::Message) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn starting(&amp;mut self, ctx: &amp;Ctx&lt;'_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn stopping(self, ctx: &amp;Ctx&lt;'_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn stop(&amp;mut self, ctx: &amp;mut Ctx&lt;'_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send {        async move {            ctx.should_stop = true;        }    }    fn start(self) -&gt; Addr&lt;Self&gt; {        Addr::spawn(self, 32)    }    fn start_with_capacity(self, capacity: usize) -&gt; Addr&lt;Self&gt; {        Addr::spawn(self, capacity)    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0430\u043a\u0442\u043e\u0440 \u0436\u0438\u0432\u0435\u0442 \u0432 \u0441\u0432\u043e\u0439 tokio \u0442\u0430\u0441\u043a\u0435, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e mpsc \u043a\u0430\u043d\u0430\u043b\u0443. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u043c\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u043c\u0435\u0442\u044c \u0435\u0433\u043e \u0430\u0434\u0440\u0435\u0441.<\/p>\n<pre><code class=\"rust\">pub struct Addr&lt;A: Actor&gt; {    id: Uuid,    requests: tokio::sync::mpsc::Sender&lt;&lt;A as Actor&gt;::Message&gt;,    terminate_call: Arc&lt;Mutex&lt;Option&lt;tokio::sync::oneshot::Sender&lt;()&gt;&gt;&gt;&gt;}impl&lt;A: Actor&gt; Addr&lt;A&gt; {    fn spawn(mut actor: A, capacity: usize) -&gt; Self {        let (requests, messages) = tokio::sync::mpsc::channel(capacity);        let (terminate_call, terminate) = tokio::sync::oneshot::channel();        let addr = Addr { id: Uuid::new_v4(), requests, terminate_call: Arc::new(Mutex::new(Some(terminate_call))) };        tokio::spawn({            let ctx_addr = addr.clone();            async move {                let mut ctx = Ctx {addr: &amp;ctx_addr, should_stop: false};                actor.starting(&amp;ctx).await;                tokio::select! {                    _ = handle_messages(&amp;mut actor, &amp;mut ctx, messages) =&gt; {},                    _ = terminate =&gt; {}                }                actor.stopping(&amp;ctx).await;            }        });        addr    }    pub async fn send(&amp;self, m: &lt;A as Actor&gt;::Message) -&gt; Result&lt;(), SendError&lt;&lt;A as Actor&gt;::Message&gt;&gt; {        self.requests.send(m).await?;        Ok(())    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u043a\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d (\u0438 \u0434\u0440\u043e\u043f\u043d\u0443\u0442) \u0432 \u0434\u0432\u0443\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445: \u043d\u0435 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 \u0443 \u0430\u0434\u0440\u0435\u0441\u0430 \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u043c\u0435\u0442\u043e\u0434 terminate. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u044f \u043b\u044e\u0431\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 send \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0431\u044b\u043b \u0434\u0440\u043e\u043f\u043d\u0443\u0442.<\/p>\n<h4>\u0421\u0435\u0440\u0432\u0435\u0440-\u043a\u043b\u0438\u0435\u043d\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435<\/h4>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 sfu \u0432\u043a\u0440\u0430\u0442\u0446\u0435 \u043e\u043f\u0438\u0448\u0443 \u0442\u043e \u043a\u0430\u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. <\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f16\/ebb\/0b5\/f16ebb0b5651e017b275b41f6205e09f.png\" alt=\"\u041a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\" title=\"\u041a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\" width=\"669\" height=\"862\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/f16\/ebb\/0b5\/f16ebb0b5651e017b275b41f6205e09f.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f16\/ebb\/0b5\/f16ebb0b5651e017b275b41f6205e09f.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435<\/figcaption><\/div>\n<\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043d\u0430\u0442. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u0430\u043a \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0438 \u0437\u0430\u0439\u0442\u0438 \u0432 \u043d\u0443\u0436\u043d\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0442\u0430\u043a \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u044e POST \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c. \u0426\u0435\u043b\u044c\u044e \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0441 WEBRTC \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u043d\u0430\u0442 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u043d\u0435 \u0431\u044b\u043b\u043e, \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2a6\/459\/530\/2a64595309f97b2327cc558fd85b39fc.png\" alt=\"\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\" title=\"\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\" width=\"823\" height=\"872\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/2a6\/459\/530\/2a64595309f97b2327cc558fd85b39fc.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2a6\/459\/530\/2a64595309f97b2327cc558fd85b39fc.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d30\/229\/40d\/d3022940dc4259f32e1bf4a0c34101b9.png\" alt=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\" title=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\" width=\"802\" height=\"535\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/d30\/229\/40d\/d3022940dc4259f32e1bf4a0c34101b9.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d30\/229\/40d\/d3022940dc4259f32e1bf4a0c34101b9.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0432 OBS \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443 \u0441 \u0433\u0438\u0444\u043a\u043e\u0439 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c\u0441\u044f c \u0434\u0440\u0443\u0433\u0438\u0445 \u0432\u043a\u043b\u0430\u0434\u043e\u043a.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/800\/238\/bd0\/800238bd0dcbcb0f17cc4c81ce141549.gif\" width=\"1920\" height=\"1080\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/800\/238\/bd0\/800238bd0dcbcb0f17cc4c81ce141549.gif 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/800\/238\/bd0\/800238bd0dcbcb0f17cc4c81ce141549.gif 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h2>\u041f\u043e\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u0441\u044f \u0432 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u0440 WEBRTC<\/h2>\n<p>\u0418\u0442\u0430\u043a \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441 \u0434\u0432\u0443\u0445 \u0432\u043a\u043b\u0430\u0434\u043e\u043a \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430 \u043e\u0431\u043e\u0438\u0445 \u0438\u0437 \u043d\u0438\u0445 \u043d\u0430\u0448\u0430 \u0432\u0438\u0440\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u0430. \u0414\u0443\u043c\u0430\u044e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>\u041f\u043e \u0441\u0443\u0442\u0438 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u043e\u0439 rtp \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u043e\u0442 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u043b \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430 \u0442\u043e\u0442 \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0443\u0436\u0435 \u0441\u0432\u044f\u0437\u0430\u043b \u0435\u0433\u043e \u0441 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0438 \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442.<\/p>\n<h4>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443<\/h4>\n<p>\u041f\u043e\u043f\u0430\u0432 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u043c\u044b c\u043e\u0437\u0434\u0430\u0435\u043c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0430\u0439\u0434\u0438 \u043a\u043e\u043c\u043d\u0430\u0442\u044b<\/p>\n<pre><code class=\"javascript\">function createWebSocket (room_id) {    const hostname = window.location.hostname;    if (hostname === 'localhost') {        return new WebSocket(`ws:\/\/${hostname}:8080\/api\/room\/${room_id}`);    } else {        return new WebSocket(`wss:\/\/${hostname}\/api\/room\/${room_id}`);    }};<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0430\u0439\u0434\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0430\u043a\u0442\u043e\u0440 User \u0432 \u043d\u0435\u0435.<\/p>\n<h3>Actor User<\/h3>\n<p>\u0412 \u043c\u043e\u0435\u0439 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u044e\u0437\u0435\u0440 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u043b \u0438\u0437 \u0441\u0435\u0431\u044f God Object, \u043e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u043b \u0438 \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u043c \u0438 \u0437\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0442\u0440\u0435\u043a\u043e\u0432 \u0438 \u0437\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u043d\u0430 \u0442\u0440\u0435\u043a\u0438. \u041f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c, \u044f \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438  \u043b\u043e\u0432\u0438\u043b \u0431\u0430\u0433\u0438 \u0441 \u0441\u0430\u043c\u0438\u043c webrtc. \u041f\u043e \u044d\u0442\u043e\u043c\u0443 \u044f \u043f\u0440\u0438\u043d\u044f\u043b \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u043f\u0438\u043b\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u044e\u0437\u0435\u0440\u0430 \u043d\u0430 User, Publisher, Subscriber.<\/p>\n<pre><code class=\"rust\">pub struct Publisher&lt;S: SyncChannel&gt; {    pub peer_id: Uuid,    pub pc: Arc&lt;RTCPeerConnection&gt;,    pub video_tracks: HashMap&lt;StreamQuality, Track&lt;VideoPacketForwarder&gt;&gt;,    pub audio_track: Option&lt;Track&lt;AudioPacketForwarder&gt;&gt;,    pub user: Addr&lt;User&lt;S&gt;&gt;,    pub room: Addr&lt;Room&lt;S&gt;&gt;,        pub qualify_monitor: WeakAddr&lt;QualityMonitor&lt;S&gt;&gt;,}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">pub struct Subscriber&lt;S: SyncChannel&gt; {    pub user: Addr&lt;User&lt;S&gt;&gt;,    pub pc: Arc&lt;RTCPeerConnection&gt;,    pub audio_subscriptions: HashMap&lt;Uuid, Addr&lt;AudioSubscription&lt;S&gt;&gt;&gt;,    pub video_subscriptions: HashMap&lt;Uuid, Addr&lt;VideoSubscription&gt;&gt;,}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0442\u0435\u043f\u0435\u0440\u044c \u044e\u0437\u0435\u0440 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043a\u043e\u043c\u043d\u0430\u0442\u043e\u0439 \u0438 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u043c, \u043e\u043d \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043f\u0440\u043e <strong>RTCPeerConnection<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0434\u0432\u0430.<br \/><strong>RTCPeerConnection<\/strong> \u043f\u0430\u0431\u043b\u0438\u0448\u0435\u0440\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u0438\u0435\u043c \u0432\u0438\u0434\u0435\u043e \u0438 \u0430\u0443\u0434\u0438\u043e \u0442\u0440\u0435\u043a\u043e\u0432 \u0441\u0430\u043c\u043e\u0433\u043e \u044e\u0437\u0435\u0440\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430 \u0435\u0433\u043e \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u0443 \u0438 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d. <br \/><strong>RTCPeerConnection<\/strong> \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0443\u0436\u0438\u0445 \u0442\u0440\u0435\u043a\u043e\u0432.<\/p>\n<h3>\u041e\u0431\u043c\u0435\u043d \u0441\u0435\u0442\u0435\u0432\u044b\u043c\u0438 \u0430\u0434\u0440\u0435\u0441\u0430\u043c\u0438<\/h3>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u0441\u0435\u0442\u0435\u0432\u044b\u043c\u0438 \u0430\u0434\u0440\u0435\u0441\u0430\u043c\u0438. <br \/><strong>ICE-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442<\/strong>  \u044d\u0442\u043e \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0430\u0434\u0440\u0435\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u044b &#8212; \u044d\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438 SFU-\u0441\u0435\u0440\u0432\u0435\u0440. ICE \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043d\u0430\u0439\u0442\u0438 \u0440\u0430\u0431\u043e\u0447\u0443\u044e \u043f\u0430\u0440\u0443 \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u0432 \u043e\u0431\u0445\u043e\u0434 NAT-\u043e\u0432 \u0438 \u0444\u0430\u0439\u0440\u0432\u043e\u043b\u043e\u0432.<br \/>\u041d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0444\u0440\u043e\u043d\u0442\u0435 \u043c\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c \u0435\u0432\u0435\u043d\u0442\u0430 icecandidate, \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u0435\u0433\u043e \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0443. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0431\u043e\u0438\u0445 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 (\u043f\u0430\u0431\u043b\u0438\u0448\u0435\u0440\u0430 \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 &#8212; \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c).<\/p>\n<p>\u0416\u0434\u0435\u043c \u0435\u0432\u0435\u043d\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435:<\/p>\n<pre><code class=\"rust\">self.pc.on_ice_candidate(Box::new(move |candidate| {    let addr = addr.clone();    Box::pin(async move {        if let Some(cand) = candidate {            if let Ok(candidate_json) = cand.to_json() {                let RTCIceCandidateInit {                       candidate, s                      dp_mid,                       sdp_mline_index, .. } = candidate_json;                addr.send(PublisherMessage::IceCandidate {                   candidate: IceCandidate {                         candidate,                         sdp_mid,                         sdp_mline_index                       }                   }).await;            }        }    })}));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044e \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435.<\/p>\n<pre><code class=\"javascript\">async add_ice_candidate(message) {    const iceCandidate = new RTCIceCandidate({        candidate: message.candidate,        sdpMid: message.sdp_mid,        sdpMLineIndex: message.sdp_mline_index    });    await this.publisher_pc.addIceCandidate(iceCandidate);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0416\u0434\u0435\u043c \u0435\u0432\u0435\u043d\u0442 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435:<\/p>\n<pre><code class=\"javascript\">publisher_pc.onicecandidate = (event) =&gt; {    if (event.candidate &amp;&amp; event.candidate.candidate) {        this.ws.send(JSON.stringify({            kind: \"rtc\",            target: \"publisher\",            type: \"candidate\",            candidate: event.candidate.candidate,        }));    }};<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<pre><code class=\"rust\">MessageType::Candidate { candidate } =&gt; {    let IceCandidate { candidate, sdp_mid, sdp_mline_index } = candidate;    let candidate_init = RTCIceCandidateInit {        candidate,        sdp_mid,        sdp_mline_index,        ..Default::default()    };    self.pc.add_ice_candidate(candidate_init).await?;},<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/17d\/009\/1ba\/17d0091ba3209c0f1bf99eeb50b1e5e0.png\" width=\"951\" height=\"765\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/17d\/009\/1ba\/17d0091ba3209c0f1bf99eeb50b1e5e0.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/17d\/009\/1ba\/17d0091ba3209c0f1bf99eeb50b1e5e0.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c<\/h3>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0441\u044c \u043f\u043e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0443 \u0442\u0435\u043f\u0435\u0440\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u043e\u0442 \u043d\u0430\u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f welcome \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c \u0435\u0433\u043e \u0430\u0439\u0434\u0438. <\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4f8\/dcf\/51c\/4f8dcf51cec09672582a261b51cd8b21.png\" width=\"963\" height=\"415\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/4f8\/dcf\/51c\/4f8dcf51cec09672582a261b51cd8b21.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4f8\/dcf\/51c\/4f8dcf51cec09672582a261b51cd8b21.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<pre><code class=\"javascript\">case 'welcome': {    this.peer_id = message.peer_id;    this.room_status.value = \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043c\u0435\u0434\u0438\u0430\u043f\u043e\u0442\u043e\u043a\u0430...\";    const video_stream = await this.getMedia({         video: {            width: { ideal: 1280 },            height: { ideal: 720 },            frameRate: { ideal: 30 }        }    });    const audio_stream = await this.getMedia({        audio: {            echoCancellation: true,                    noiseSuppression: true,                    autoGainControl: true,                     sampleRate: { ideal: 48000 },              sampleSize: { ideal: 16 },                 channelCount: { ideal: 1 }             }    });    await this.peer_connection.add_tracks(video_stream, audio_stream);    this.users.value.set(this.peer_id, {        stream: video_stream,        isLocal: true    });    this.room_status.value = \"\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e\";    break;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/338\/281\/f64\/338281f64411060acb91728bd564c5cb.png\" width=\"969\" height=\"425\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/338\/281\/f64\/338281f64411060acb91728bd564c5cb.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/338\/281\/f64\/338281f64411060acb91728bd564c5cb.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0435\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d \u0438 \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u0443 (\u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u044f \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u043a\u043e\u0434 \u0431\u0435\u0437 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0442\u043e\u0433\u043e \u0438 \u0434\u0440\u0443\u0433\u043e\u0433\u043e). \u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u043c\u0435\u0434\u0438\u0430 \u0441\u0442\u0438\u043c\u044b \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445 \u0432 \u043d\u0430\u0448\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0430\u0431\u043b\u0438\u0448\u0435\u0440\u0430.<\/p>\n<pre><code class=\"javascript\">async add_tracks(video_stream, audio_stream) {    const video_track = video_stream.getVideoTracks()[0];    this.publisher_pc.addTransceiver(video_track, {        direction: 'sendonly',    });    const audio_track = audio_stream.getAudioTracks()[0];    this.publisher_pc.addTransceiver(audio_track, { direction: 'sendonly' });    let offer = await this.publisher_pc.createOffer();    await this.publisher_pc.setLocalDescription(offer);    this.ws.send(JSON.stringify({        kind: \"rtc\",        target: \"publisher\",        type: \"offer\",        sdp: offer.sdp    }));}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u043a\u0430 \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u0431\u044b\u043b\u043e \u0441\u0430\u043c\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0432 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0438 \u0447\u0430\u0441\u0442\u044c\u044e \u043f\u043e\u043a\u0430 \u044f \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043b \u0447\u0442\u043e \u0437\u0430 \u043e\u0444\u0444\u0435\u0440 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c.<br \/>\u0427\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0441 \u0447\u0435\u043c \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0434\u0435\u043b\u043e, \u044f \u0441\u043e\u0431\u0440\u0430\u043b \u043d\u0443\u0436\u043d\u0443\u044e \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 JSON.<\/p>\n<pre><code class=\"json\">{  \"session_type\": \"offer\",  \"direction\": \"sendonly\",  \"security\": {    \"fingerprint_algorithm\": \"sha-256\",    \"fingerprint\": \"C6:EB:6A:00:AF:81:07:85:D1:E1:9A:C7:E3:C9:77:BE:A6:72:E2:46:BC:47:86:0F:51:03:48:0E:01:E8:97:5C\"  },  \"connectivity\": {    \"ice_ufrag\": \"d4ew\",    \"ice_pwd\": \"0\/FjYTns3K\/Ib8xQ8JvFnaPN\",    \"ice_options\": \"trickle\"  },  \"media_streams\": {    \"video\": {      \"supported_codecs\": [        \"VP8\",        \"H264\",        \"AV1\",        \"VP9\"      ],      \"features\": {        \"simulcast\": \"enabled\",        \"simulcast_layers\": [\"low\", \"mid\", \"high\"]      }    },    \"audio\": {      \"primary_codec\": \"opus\",      \"sample_rate_hz\": 48000,      \"channels\": 2,      \"fallback_codecs\": [        \"G722\",        \"PCMU\",        \"PCMA\"      ]    }  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 Offer \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, SFU \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u00a0<strong>set_remote_description(offer).<\/strong> \u0418\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0442 \u0432\u044b\u0437\u043e\u0432 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430:<\/p>\n<ul>\n<li>\n<p><strong>\u041f\u0440\u0438\u0451\u043c \u043c\u0435\u0434\u0438\u0430:<\/strong>\u00a0\u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f DTLS-\u0440\u0443\u043a\u043e\u043f\u043e\u0436\u0430\u0442\u0438\u0435, \u0438 \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f, \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043b\u0431\u044d\u043a\u00a0<strong>on_track<\/strong>\u00a0&#8212; \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u043c RTP-\u043f\u0430\u043a\u0435\u0442\u0430\u043c, \u043d\u0435 \u0434\u043e\u0436\u0438\u0434\u0430\u044f\u0441\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 Answer<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f:<\/strong>\u00a0SFU \u0441\u043e\u0437\u0434\u0430\u0451\u0442 Answer \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0443, \u0447\u0442\u043e\u0431\u044b \u0442\u043e\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b \u0440\u0443\u043a\u043e\u043f\u043e\u0436\u0430\u0442\u0438\u0435 \u0441\u043e \u0441\u0432\u043e\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u0438 \u0434\u0432\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0438\u0434\u0443\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. SFU \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u0447\u0430\u0442\u044c \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b \u0434\u0440\u0443\u0433\u0438\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0435\u0449\u0451 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043a\u043b\u0438\u0435\u043d\u0442-\u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442 Answer, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e UDP-\u043f\u043e\u0442\u043e\u043a \u0438\u0434\u0451\u0442 \u0432 \u043e\u0434\u043d\u0443 \u0441\u0442\u043e\u0440\u043e\u043d\u0443, \u0430 \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f &#8212; \u043f\u043e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0443.<\/p>\n<pre><code class=\"rust\">MessageType::Offer {sdp} =&gt; {    let offer_desc = RTCSessionDescription::offer(sdp)?;    self.pc.set_remote_description(offer_desc).await?;    let answer = self.pc.create_answer(None).await?;    self.pc.set_local_description(answer.clone()).await?;    let message = SignalMessage::Rtc {        target: Target::Publisher,        message_type: MessageType::Answer {sdp: answer.sdp }    };    self.user.send(UserMessage::SignalMessage(message)).await;    self.room.send(RoomMessage::SubscribeToPeers {       peer_id: self.peer_id     }).await;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8f8\/3a8\/8b7\/8f83a88b735b8858af41a04804e2c7dc.png\" width=\"996\" height=\"487\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8f8\/3a8\/8b7\/8f83a88b735b8858af41a04804e2c7dc.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8f8\/3a8\/8b7\/8f83a88b735b8858af41a04804e2c7dc.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u0438\u0441\u044b\u043b\u0430\u0435\u0442 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442 answer (\u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432\u0432\u0438\u0434\u0435 json):<\/p>\n<pre><code class=\"json\">{  \"role\": \"answer\",  \"direction\": \"recvonly\",  \"connection\": {    \"bundle\": true,    \"dtls_setup\": \"active\",    \"fingerprint_hash\": \"sha-256 F1:5C:67:08:21:4B:C5:33:CD:AB:B4:66:EC:54:8D:70:5D:F6:41:CF:21:A4:CD:42:0F:E9:67:2D:D8:86:50:17\",    \"ice_trickle_enabled\": true,    \"ice_credentials\": {      \"ufrag\": \"IOOqnyJnAPErLLLV\",      \"pwd\": \"PIsWYhOYTLAoOIDfaEZtugybeZKLmwQi\"    }  },  \"media_streams\": [    {      \"type\": \"video\",      \"mid\": 0,      \"port\": 9,      \"protocol\": \"UDP\/TLS\/RTP\/SAVPF\",      \"codecs\": [        { \"payload_type\": 96, \"name\": \"VP8\", \"clock_rate\": 90000 },        { \"payload_type\": 103, \"name\": \"H264\", \"clock_rate\": 90000, \"profile\": \"42001f\", \"packetization_mode\": 1 },        { \"payload_type\": 107, \"name\": \"H264\", \"clock_rate\": 90000, \"profile\": \"42001f\", \"packetization_mode\": 0 },        { \"payload_type\": 109, \"name\": \"H264\", \"clock_rate\": 90000, \"profile\": \"42e01f\", \"packetization_mode\": 1 },        { \"payload_type\": 115, \"name\": \"H264\", \"clock_rate\": 90000, \"profile\": \"42e01f\", \"packetization_mode\": 0 },        { \"payload_type\": 45, \"name\": \"AV1\", \"clock_rate\": 90000, \"profile\": 0 },        { \"payload_type\": 98, \"name\": \"VP9\", \"clock_rate\": 90000, \"profile_id\": 0 },        { \"payload_type\": 121, \"name\": \"ulpfec\", \"clock_rate\": 90000 }      ],      \"features\": {        \"rtcp_mux\": true,        \"rtcp_rsize\": true,        \"feedback_mechanisms\": [\"goog-remb\", \"transport-cc\", \"ccm fir\", \"nack\", \"nack pli\"],        \"simulcast\": false      }    },    {      \"type\": \"audio\",      \"mid\": 1,      \"port\": 9,      \"protocol\": \"UDP\/TLS\/RTP\/SAVPF\",      \"codecs\": [        { \"payload_type\": 111, \"name\": \"opus\", \"clock_rate\": 48000, \"channels\": 2, \"fec\": true },        { \"payload_type\": 9, \"name\": \"G722\", \"clock_rate\": 8000 },        { \"payload_type\": 0, \"name\": \"PCMU\", \"clock_rate\": 8000 },        { \"payload_type\": 8, \"name\": \"PCMA\", \"clock_rate\": 8000 }      ],      \"features\": {        \"rtcp_mux\": true,        \"rtcp_rsize\": true,        \"feedback_mechanisms\": [\"transport-cc\"]      }    }  ]}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u043e\u043b\u043b\u0431\u0435\u043a\u0430 on_track \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0437\u043e\u0441\u043b\u0430\u0442\u044c \u0442\u0440\u0435\u043a \u0434\u0440\u0443\u0433\u0438\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=\"rust\">self.pc.on_track(Box::new(move |track, _, _| {    let pc = pc.clone();    let addr = addr.clone();    let ssrc = track.ssrc();    let mime_type = track.codec().capability.mime_type;    let mime_type = MimeType::from_str(&amp;mime_type).unwrap_or_default();    let kind = track.kind();    let rid = StreamQuality::from_str(track.rid());    Box::pin(async move {        match kind {            RTPCodecType::Audio | RTPCodecType::Unspecified =&gt; {                 let rtp_packet_forwarder =                     RtpPacketGatewayRouter::&lt;AudioPacketForwarder&gt;::spawn(                      track, StreamQuality::Audio, None                    );                addr.send(                    PublisherMessage::NewAudioTrack(Track {                       mime_type, addr: rtp_packet_forwarder                     })                ).await;            },            RTPCodecType::Video =&gt; {                let pli_sender = PliSender::new(pc.clone(), ssrc).start();                let quality = rid.unwrap_or(StreamQuality::High);                let rtp_packet_forwarder=                     RtpPacketGatewayRouter::&lt;VideoPacketForwarder&gt;::spawn(                      track, quality, Some(pli_sender)                    );                addr.send(                    PublisherMessage::NewVideoTrack {                        track: Track { mime_type, addr: rtp_packet_forwarder } ,                         quality                     }                ).await;            }           };    })}));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u043a\u0442\u043e\u0440 \u043a\u043e\u043c\u043d\u0430\u0442\u0430 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442 \u0442\u0440\u0435\u043a \u0438 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442 \u0435\u0433\u043e, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043b\u0430\u0442\u044c  \u0435\u0433\u043e \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"rust\">  RoomMessage::AddAudioTrack { peer_id, track: mut stream } =&gt; {      self.connect_new_audio_stream(peer_id, &amp;mut stream).await;      let Some(peer) = self.peers.get_mut(&amp;peer_id) else { return; };      peer.add_audio_track(stream);  },  RoomMessage::AddVideoTrack { peer_id, track: mut stream, quality } =&gt; {      self.connect_new_video_stream(peer_id, quality, &amp;mut stream).await;      let Some(peer) = self.peers.get_mut(&amp;peer_id) else { return; };      peer.add_stream_track(quality, stream);  }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">async fn connect_new_audio_stream(&amp;mut self,   peer_id: Uuid,   stream: &amp;mut PeerTrack&lt;AudioPacketForwarder&gt;) {      let peers = self.peers.iter()          .filter(|(id, _)| **id != peer_id);      for (_, peer) in peers {          let Peer { user, .. } = peer;          user.send(UserMessage::ConnectAudio(ConnectionRequest {               peer_id,               gateway_router: stream.gateway_router.clone(),               codec_mime_type: stream.mime_type.clone()          })).await;      }}async fn connect_new_video_stream(&amp;mut self,   peer_id: Uuid,   quality: StreamQuality,   stream: &amp;mut PeerTrack&lt;VideoPacketForwarder&gt;) {    let peers = self.peers.iter()        .filter(|(id, _)| **id != peer_id);    for (_, peer) in peers {        let Peer { user, .. } = peer;        user.send(UserMessage::ConnectVideo{             quality,             request: ConnectionRequest {                 peer_id,                 gateway_router: stream.gateway_router.clone(),                 codec_mime_type: stream.mime_type.clone()            }        }).await;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u043d\u0430 \u0447\u0443\u0436\u0438\u0435 \u0442\u0440\u0435\u043a\u0438<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0435 \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0448\u0435\u0434\u0448\u0438\u0439 \u043f\u0440\u0438 \u0437\u0430\u0445\u043e\u0434\u0435 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445.<\/p>\n<pre><code class=\"rust\">RoomMessage::SubscribeToPeers { peer_id } =&gt; {    self.connect_old_streams(peer_id).await;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">async fn connect_old_streams(&amp;mut self, peer_id: Uuid) {    let Some(Peer { user, .. }) = self.peers.get(&amp;peer_id) else { return ; };    let stream = self.peers.iter()        .filter(|(id, _)| **id != peer_id);    for (existed_peer_id, Peer { audio_track: audio_stream, video_tracks: video_streams, .. }) in stream {        if let Some(audio_stream) = audio_stream {            user.send(UserMessage::ConnectAudio(ConnectionRequest {                codec_mime_type: audio_stream.mime_type.clone(),                peer_id: *existed_peer_id,                gateway_router: audio_stream.gateway_router.clone()            })).await;        }        for (quailty, video_stream) in video_streams {            user.send(UserMessage::ConnectVideo {                quality,                request: ConnectionRequest {                    codec_mime_type: video_stream.mime_type.clone(),                    peer_id: *existed_peer_id,                    gateway_router: video_stream.gateway_router.clone()                }            }).await;        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0447\u0443\u0436\u043e\u0439 \u0442\u0440\u0435\u043a \u043a RTCPeerConnection \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430.<\/p>\n<pre><code class=\"rust\">let track: Arc&lt;TrackLocalStaticRTP&gt; = Arc::new(TrackLocalStaticRTP::new(    RTCRtpCodecCapability {        mime_type: mime_type.to_string(),        ..Default::default()    },    format!(\"video_{peer_id}\"),    format!(\"video_{peer_id}\")));let active_track = pc.add_transceiver_from_track(        track.clone() as Arc&lt;_&gt;,        Some(RTCRtpTransceiverInit {           direction: RTCRtpTransceiverDirection::Sendonly,           send_encodings: vec![]       }))    .await?    .sender()    .await;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a34\/cde\/65d\/a34cde65df549fda5097823439867bea.png\" width=\"954\" height=\"381\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a34\/cde\/65d\/a34cde65df549fda5097823439867bea.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a34\/cde\/65d\/a34cde65df549fda5097823439867bea.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0441\u044f \u0435\u0432\u0435\u043d\u0442 negotiation_needed, \u043c\u044b \u043e\u0431\u044f\u0437\u0430\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0444\u0444\u0435\u0440 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043f\u0435\u0440\u0435\u0441\u043b\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0444\u0440\u043e\u043d\u0442 \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c.<\/p>\n<pre><code class=\"rust\">self.pc.on_negotiation_needed(Box::new(move || {    let pc = Arc::clone(&amp;pc);    let addr = addr.clone();    Box::pin(async move {        if pc.signaling_state() != RTCSignalingState::Stable {            return;        }        match pc.create_offer(None).await {            Ok(offer) =&gt; {                if let Err(e) = pc.set_local_description(offer.clone()).await {                    tracing::error!(\"[User] set_local_description: {:?}\", e);                    return;                }                addr.send(SubscriberMessage::Signal {                    offer: offer.sdp                }).await;            }            Err(e) =&gt; tracing::error!(\"[User] create_offer: {:?}\", e),        }    })}));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10a\/7ba\/a60\/10a7baa60957ae75b11f15e50e7ae840.png\" width=\"1035\" height=\"466\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/10a\/7ba\/a60\/10a7baa60957ae75b11f15e50e7ae840.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10a\/7ba\/a60\/10a7baa60957ae75b11f15e50e7ae840.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0412 \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0444\u0444\u0435\u0440\u0430 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435 \u043d\u0430\u043c \u0432\u0430\u0436\u043d\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f Promise Queue.<\/p>\n<pre><code class=\"javascript\">async create_answer(sdp) {    this.signaling_queue = this.signaling_queue.then(      () =&gt; this.create_answer_task(sdp)    );    return this.signaling_queue;}async create_answer_task(sdp) {    try {        await this.subscriber_pc.setRemoteDescription(new RTCSessionDescription({            type: 'offer',            sdp: sdp        }));        const answer = await this.subscriber_pc.createAnswer();        await this.subscriber_pc.setLocalDescription(answer);        this.ws.send(JSON.stringify({            kind: \"rtc\",            target: 'subscriber',            type: 'answer',            sdp: answer.sdp        }));    } catch (error) {        console.error(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 SFU Offer:\", error);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430\u043c \u0432\u0430\u0436\u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443, \u0435\u0441\u043b\u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u0432\u0430 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0448\u0430\u0433\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 <strong>Offer<\/strong> \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430), \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <strong>signalingState<\/strong> \u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0432\u044f\u0437\u0438 \u0443\u0439\u0434\u0435\u0442 \u0432 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f \u043d\u0430 <strong>have-remote-offer <\/strong>\u043f\u043e\u0441\u0440\u0435\u0434\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430), \u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u0442 \u043d\u0435\u0443\u0441\u0442\u0440\u0430\u043d\u0438\u043c\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443. \u041e\u0447\u0435\u0440\u0435\u0434\u044c \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442, \u0447\u0442\u043e \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u0432\u044f\u0437\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044f \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043d\u0430\u0447\u043d\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a44\/7ef\/1e6\/a447ef1e6d7049c58eeed6afecba5e92.png\" width=\"803\" height=\"781\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a44\/7ef\/1e6\/a447ef1e6d7049c58eeed6afecba5e92.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a44\/7ef\/1e6\/a447ef1e6d7049c58eeed6afecba5e92.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0430\u043d\u043d\u044b\u0439 answer \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b  \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0432\u044b\u0437\u0432\u0430\u0432 set_remote_description \u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f &#171;\u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430&#187;.<\/p>\n<pre><code class=\"rust\">MessageType::Answer { sdp } =&gt; {    let answer_desc = RTCSessionDescription::answer(sdp)?;    self.pc.set_remote_description(answer_desc).await?;},<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 Offer \u043e\u0442 SFU \u0447\u0435\u0440\u0435\u0437\u00a0setRemoteDescription, \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u0432 SDP \u043d\u043e\u0432\u044b\u0435 \u043c\u0435\u0434\u0438\u0430-\u0442\u0440\u0435\u043a\u0438 \u0438 \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u00a0ontrack\u00a0&#8212; \u0435\u0449\u0451 \u0434\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 Answer.\u00a0\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0430\u0443\u0434\u0438\u043e \u0438 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e \u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0440\u0435\u043a\u0430 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043d\u0435 \u0431\u044b\u043b \u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0441\u0442\u0440\u0438\u043c \u0434\u043e \u044d\u0442\u043e\u0433\u043e, \u0438 \u0435\u0441\u043b\u0438 \u0431\u044b\u043b, \u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0440\u0435\u043a \u043a \u043d\u0435\u043c\u0443.<\/p>\n<pre><code class=\"javascript\">subscriber_pc.ontrack = ({ streams, track }) =&gt; {  const remote_stream = streams[0];  const user_id = remoteStream.id.split(\"_\")[1];  let user = users.value.get(user_id);  if (!!user) {      user.stream.addTrack(track);      return;  }  users.value.set(user_id, {      stream: remoteStream,      isLocal: false  });};<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9ad\/07e\/d1c\/9ad07ed1c10814768f5cad86a880b0ec.png\" width=\"939\" height=\"746\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/9ad\/07e\/d1c\/9ad07ed1c10814768f5cad86a880b0ec.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9ad\/07e\/d1c\/9ad07ed1c10814768f5cad86a880b0ec.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e, \u0434\u0430, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0442\u0440\u0435\u043a, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u043e\u043a\u0430 \u043c\u044b \u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0443 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u043e\u0442 \u043f\u0430\u0431\u043b\u0438\u0448\u0435\u0440\u0430 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044e\u0437\u0435\u0440\u0430 \u043a \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430\u043c \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442.<\/p>\n<h4>\u0410\u043a\u0442\u043e\u0440\u044b RtpPacketGatewayRouter \u0438 PacketForwarder<\/h4>\n<p>\u0414\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u0432\u043f\u0440\u043e\u0447\u0435\u043c \u0435\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0443 \u043d\u0430\u0441 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0438 \u0432\u0438\u0434\u0435\u043e, \u0438 \u0430\u0443\u0434\u0438\u043e.  <\/p>\n<pre><code class=\"rust\">impl&lt;A: Actor&gt; RtpPacketGatewayRouter&lt;A&gt;  {    pub fn spawn(track: Arc&lt;TrackRemote&gt;) -&gt; Addr&lt;Self&gt; {        let this: Addr&lt;RtpPacketGatewayRouter&lt;A&gt;&gt; = Self {          subscriptions: HashSet::new()        }          .start_with_capacity(2048);        let receiver = this.clone();        tokio::spawn(async move {            loop {                let Ok((packet, _)) = track.read_rtp().await                    .map_err(|e| tracing::error!(\"[RtpPacketGatewayRouter] read_rtp {e}\")) else {                         receiver.terminate().await;                        break;                    };                let Ok(_) = receiver.do_send(RtpPacketGatewayRouterMessage::RtpPacket(packet))                    .map_err(|_| tracing::error!(\"[RtpPacketGatewayRouter] send RtpPacketForwarderMessage::RtpPacket(packet)\"))                 else { break; };            }        });        this    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">async fn handle(&amp;mut self, _ctx: &amp;mut crate::actor::Ctx&lt;'_, Self&gt;, msg: Self::Message) {  match msg {      RtpPacketGatewayRouterMessage::Subscribe(sub) =&gt; {          self.subscriptions.insert(sub);          self.pc.write_rtcp(&amp;[Box::new(FullIntraRequest {              media_ssrc: self.ssrc,              sender_ssrc: 0,              fir: [                  FirEntry { ssrc: self.ssrc, sequence_number: self.fir_sequence }              ].to_vec()            })]).await?;            self.fir_sequence = self.fir_sequence.wrapping_add(1);      },      RtpPacketGatewayRouterMessage::RtpPacket(packet) =&gt; {          self.subscriptions.iter().for_each(|sub| {             sub.do_send(packet.clone());           });      },      RtpPacketGatewayRouterMessage::Unsubscribe(sub) =&gt; {          self.subscriptions.remove(&amp;sub);      }  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u043a\u0442\u043e\u0440 \u043f\u043e \u0441\u0443\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0440\u043e\u0443\u0442\u0435\u0440\u0430, \u043e\u043d \u0443\u043c\u0435\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432, \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0438\u0445, \u0438 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0442\u0440\u0435\u043a\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c. \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0438 \u043d\u0430\u043c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b, \u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0430\u043c \u043f\u0430\u043a\u0435\u0442, \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0445\u0435\u0434\u0435\u0440 + Bytes (\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043f\u0430\u043c\u044f\u0442\u044c + \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 \u0441\u0447\u0435\u0442\u0447\u0438\u043a).<\/p>\n<p>\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c FullIntraRequest. <\/p>\n<p><strong>Full Intra Request (FIR)<\/strong> \u2014 \u044d\u0442\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 \u0432 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430\u0445 WebRTC \u0438 RTP\/RTCP, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u043f\u0440\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u0432\u0438\u0434\u0435\u043e. \u041a\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u043e\u0432\u0440\u0435\u0436\u0434\u0435\u043d\u043d\u044b\u0439 \u0438\u043b\u0438 \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u043a\u0430\u0434\u0440, \u043e\u043d\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044e \u043f\u0430\u043a\u0435\u0442 FIR. \u042d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439, \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0439 \u043e\u043f\u043e\u0440\u043d\u044b\u0439 \u043a\u0430\u0434\u0440 (Intra-\u043a\u0430\u0434\u0440, I-frame \u0438\u043b\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043a\u0430\u0434\u0440).<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u0435\u043d \u0434\u043b\u044f \u043d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u043e\u0441\u0442\u0438 \u0432\u0438\u0434\u0435\u043e \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 \u043a \u043e\u0448\u0438\u0431\u043a\u0430\u043c, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432 WebRTC. \u0414\u0435\u043a\u043e\u0434\u0435\u0440 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0435 \u043a\u0430\u0434\u0440\u044b (P-frames \u0438\u043b\u0438 B-frames) \u0431\u0435\u0437 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0433\u043e \u043a\u0430\u0434\u0440\u0430. \u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f Intra-\u043a\u0430\u0434\u0440\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0442\u043e\u043a\u0430 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0442\u0435\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0435\u0432 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435.<\/p>\n<p>\u0410\u043a\u0442\u043e\u0440 PacketForwarder \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0435\u0449\u0435 \u043f\u0440\u043e\u0449\u0435, \u043f\u043e \u0441\u0443\u0442\u0438 \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u0435\u0433\u043e \u0430\u0434\u0440\u0435\u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u044f\u0442 \u0440\u043e\u0443\u0442\u0435\u0440\u0430 \u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043b\u0438\u0448\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u0438\u0445 \u0432 \u0442\u0440\u0435\u043a \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430.<\/p>\n<pre><code class=\"rust\">let forwarder = PacketForwarder { track: output_track.clone() }      .start_with_capacity(256);gateway_router    .send(RtpPacketGatewayRouterMessage::Subscribe(forwarder.clone()))    .await?;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">pub struct PacketForwarder {    pub track: Arc&lt;TrackLocalStaticRTP&gt;,}impl PacketForwarder {    async fn forward(&amp;self, r: Packet) -&gt; Result&lt;(), Error&gt; {        self.track.write_rtp(&amp;r).await?;        Ok(())    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">impl Actor for PacketForwarder {    type Message = AudioPacketForwarderMessage;    async fn handle(&amp;mut self, ctx: &amp;mut crate::actor::Ctx&lt;'_, Self&gt;, packet: Self::Message) {        if let Err(e) = self.forward(packet.packet).await {            self.stop(ctx).await;        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fd2\/b26\/9e8\/fd2b269e8a62fc80171c67db2f735a1e.png\" width=\"718\" height=\"320\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/fd2\/b26\/9e8\/fd2b269e8a62fc80171c67db2f735a1e.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fd2\/b26\/9e8\/fd2b269e8a62fc80171c67db2f735a1e.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3eb\/79e\/9eb\/3eb79e9eb8d52a89a53690228b0344f1.png\" width=\"983\" height=\"423\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3eb\/79e\/9eb\/3eb79e9eb8d52a89a53690228b0344f1.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3eb\/79e\/9eb\/3eb79e9eb8d52a89a53690228b0344f1.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h2>\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/h2>\n<p>\u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0430\u043a\u0442\u043e\u0440 \u044e\u0437\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d, \u0434\u0440\u043e\u043f\u043d\u0435\u0442 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u0438 \u043f\u0430\u0431\u043b\u0438\u0448\u0435\u0440\u0430, \u0438 \u043f\u0435\u0440\u0435\u0448\u043b\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u044e\u0437\u0435\u0440\u0430 \u0438 \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442\u044c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c59\/484\/811\/c594848115ca3a7a419b9e2af63255d8.png\" width=\"819\" height=\"543\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/c59\/484\/811\/c594848115ca3a7a419b9e2af63255d8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c59\/484\/811\/c594848115ca3a7a419b9e2af63255d8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<pre><code class=\"rust\">async fn stopping(self, _: &amp;Ctx&lt;'_, Self&gt;) {    self.subscriber.try_terminate().await;    self.publisher.try_terminate().await;    let _ = self.room.send(RoomMessage::Leave {         peer_id: self.peer_id     }).await;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"rust\">RoomMessage::Leave { peer_id } =&gt; {    self.peers.remove(&amp;peer_id);    for (_, Peer { user, .. }) in self.peers.iter() {        user.send(UserMessage::Unsubscribe { user_id: peer_id }).await;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6f9\/6bb\/9f4\/6f96bb9f41499439a91a698cfb1f42b5.png\" width=\"955\" height=\"543\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/6f9\/6bb\/9f4\/6f96bb9f41499439a91a698cfb1f42b5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6f9\/6bb\/9f4\/6f96bb9f41499439a91a698cfb1f42b5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0442 \u0430\u043a\u0442\u043e\u0440\u043e\u0432 \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a \u043d\u0430 \u0432\u044b\u0448\u0435\u0434\u0448\u0435\u0433\u043e \u044e\u0437\u0435\u0440\u0430.<\/p>\n<pre><code class=\"rust\">async fn disconnect_from_user(&amp;mut self, speaker_id: Uuid) -&gt; Result&lt;(), Error&gt; {    let audio_subscription = self.audio_subscriptions        .remove(&amp;speaker_id)        .ok_or(Error::SystemError { message: \"subscription not found\".into() })?;    audio_subscription.terminate().await?;    let video_subscription = self.video_subscriptions        .remove(&amp;speaker_id)        .ok_or(Error::SystemError { message: \"subscription not found\".into() })?;    video_subscription.terminate().await?;    Ok(())}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0430\u043a\u0442\u043e\u0440\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u043f\u043e\u0448\u043b\u044e\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0442\u043f\u0438\u0441\u043a\u0435 RtpPacketGatewayRouter \u0438 \u0443\u0434\u0430\u043b\u044f\u0442 \u0442\u0440\u0435\u043a \u0438\u0437 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5f3\/2e4\/294\/5f32e4294aeed6b89ec229802fa4c6bb.png\" width=\"1178\" height=\"466\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/5f3\/2e4\/294\/5f32e4294aeed6b89ec229802fa4c6bb.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5f3\/2e4\/294\/5f32e4294aeed6b89ec229802fa4c6bb.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h2>\u0412\u044b\u0445\u043e\u0434 \u0432 \u0441\u0432\u0435\u0442<\/h2>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438, \u043d\u043e \u0438 \u0432 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0435 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 \u0432\u043f\u0441\u043a\u0435, \u0445\u043e\u0442\u044c \u043d\u0430\u043c \u0438 \u043d\u0443\u0436\u043d\u044b \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b SSL\/TLS, \u0438\u0437 \u0437\u0430 \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b (Chrome, Safari, Firefox) \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043c\u0435\u0434\u0438\u0430\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c \u043d\u0430 \u0441\u0430\u0439\u0442\u0430\u0445, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0445 \u043f\u043e \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 HTTP. \u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043b\u0438\u0448\u044c <strong>localhost<\/strong> \u0434\u043b\u044f \u0446\u0435\u043b\u0435\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f tuna, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u043e\u0433 Ngrok. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u0442\u0443\u043d\u043d\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0430\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u043a \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e (<a href=\"http:\/\/localhost\" rel=\"noopener noreferrer nofollow\">localhost<\/a>) \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0440\u043e\u0443\u0442\u0435\u0440 \u0438 \u0438\u043c\u0435\u0442\u044c \u00ab\u0431\u0435\u043b\u044b\u0439\u00bb IP-\u0430\u0434\u0440\u0435\u0441. <\/p>\n<pre><code class=\"powershell\">tuna http 8080INFO[16:31:04] Web Interface: http:\/\/127.0.0.1:4040         INFO[16:31:04] Forwarding https:\/\/ua3buj-134-17-184-155.ru.tuna.am -&gt; 127.0.0.1:8080<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/016\/feb\/6cb\/016feb6cb1fe84576c0ed054aaac201c.gif\" alt=\"\u0411\u0440\u0430\u0442\u043a\u0438 \u043d\u0430 \u0441\u043e\u0437\u0432\u043e\u043d\u0435\" title=\"\u0411\u0440\u0430\u0442\u043a\u0438 \u043d\u0430 \u0441\u043e\u0437\u0432\u043e\u043d\u0435\" width=\"598\" height=\"765\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/016\/feb\/6cb\/016feb6cb1fe84576c0ed054aaac201c.gif 780w,&#10;       https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/016\/feb\/6cb\/016feb6cb1fe84576c0ed054aaac201c.gif 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0411\u0440\u0430\u0442\u043a\u0438 \u043d\u0430 \u0441\u043e\u0437\u0432\u043e\u043d\u0435<\/figcaption><\/div>\n<\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 SFU \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043c\u0435\u0434\u0438\u0430 \u043f\u043e\u0442\u043e\u043a\u0430 \u043e\u0442 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435. <\/p>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u044e, \u0442\u043e \u043a\u0430\u043a \u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b Simulcast (\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u0435\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f), \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0433\u043e \u043a\u0430\u0434\u0440\u0430, \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043c\u0435\u043d\u044b \u0441\u0435\u0442\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0434\u0435\u0431\u0430\u0433\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<br \/>\u0412\u0441\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0442\u043e \u0447\u0442\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0434\u043e \u043a\u043e\u043d\u0446\u0430 \u043c\u043e\u0439 \u0434\u0435\u0431\u044e\u0442, \u0431\u0443\u0434\u0443 \u0440\u0430\u0434 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043a\u0440\u0438\u0442\u0438\u043a\u0435!<\/p>\n<\/div>\n<p>\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\/articles\/1055732\/\">https:\/\/habr.com\/ru\/articles\/1055732\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0441 Rust \u0443\u0436\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u0435\u0442. \u0412 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c &#8212; \u0431\u044d\u043a\u0435\u043d\u0434, \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u043c\u043d\u043e\u0433\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0442\u043e\u0447\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u0410 \u0435\u0449\u0451 \u044f \u0434\u0430\u0432\u043d\u043e \u0445\u043e\u0442\u0435\u043b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 WebRTC. \u041d\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 &#171;\u0432\u043e\u0442 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b, \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438&#187;, \u0430 \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441 \u043f\u0430\u043a\u0435\u0442\u0430\u043c\u0438, \u043a\u0430\u043a \u043e\u043d\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f, \u043a\u0430\u043a \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0438\u0440\u0443\u044e\u0442\u0441\u044f.\u0412 \u0438\u0442\u043e\u0433\u0435 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 SFU.\u041d\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430. \u041d\u0435 \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u043a\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 LiveKit \u0438\u043b\u0438 Jitsi. \u041f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u043e\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u0437\u043d\u0443\u0442\u0440\u0438. \u0418 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430 Rust &#8212; \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u043c\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0438 \u043c\u043d\u0435 \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043d \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u043e\u0434\u0430 \u0437\u0430\u0434\u0430\u0447.\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f &#8212; \u043d\u0435 \u0433\u0430\u0439\u0434 &#171;\u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c SFU \u0437\u0430 5 \u043c\u0438\u043d\u0443\u0442&#187;. \u042d\u0442\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0434\u043d\u0435\u0432\u043d\u0438\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412 \u043a\u043e\u043d\u0446\u0435 \u044f \u0431\u0443\u0434\u0443 \u0440\u0430\u0434, \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0443\u043a\u0430\u0436\u0435\u0442 \u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438\u043b\u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u043b\u0443\u0447\u0448\u0438\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u0446\u0435\u043b\u044c &#8212; \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0433\u043b\u0443\u0431\u0436\u0435.\u0421 \u0447\u0435\u0433\u043e \u0432\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c\u041f\u0430\u0440\u0443 \u043c\u0435\u0441\u044f\u0446\u0435\u0432 \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0441\u0442\u0430\u0442\u044c\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d WebRTC \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0412 \u043d\u0435\u0439 \u0431\u044b\u043b\u043e \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e SDP, ICE \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u044b, DTLS \u0440\u0443\u043a\u043e\u043f\u043e\u0436\u0430\u0442\u0438\u0435. \u042f \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b \u0438 \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432 \u043d\u0435 \u0437\u043d\u0430\u044e.\u0422\u043e\u0433\u0434\u0430 \u044f \u0440\u0435\u0448\u0438\u043b: \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u043d\u044f\u0442\u044c &#8212; \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0441\u0430\u043c\u043e\u043c\u0443.\u042f \u043d\u0430\u0447\u0430\u043b \u0441 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e: \u0432\u0437\u044f\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0webrtc-rs, \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u044d\u0445\u043e-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u043b \u0432\u0438\u0434\u0435\u043e \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u0435\u0433\u043e \u043e\u0431\u0440\u0430\u0442\u043d\u043e. \u0417\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e. \u041f\u043e\u0442\u043e\u043c \u044f \u0437\u0430\u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c \u0447\u0442\u043e \u043c\u043d\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0442\u0435\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u043d\u0435 \u043f\u043e\u043d\u044f\u043b \u0438\u0437 \u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0432\u0441\u0435 \u0442\u0430\u043a\u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u044b, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0435\u043e\u0440\u0438\u0438.\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 SFU \u0438 \u0437\u0430\u0447\u0435\u043c \u043e\u043d \u043d\u0443\u0436\u0435\u043dWebRTC \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u0434\u0443\u043c\u044b\u0432\u0430\u043b\u0441\u044f \u043a\u0430\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043c\u0438. Peer-to-peer. \u041e\u0434\u0438\u043d \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0442\u043e\u043a \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0434\u0440\u0443\u0433\u043e\u043c\u0443. \u0412\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e, \u043f\u043e\u043a\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u0432\u043e\u0435.\u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0445 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0442\u0440\u043e\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443. \u042d\u0442\u043e \u0443\u0436\u0435 \u0442\u0440\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430. \u0414\u043b\u044f \u0447\u0435\u0442\u044b\u0440\u0451\u0445 &#8212; \u0448\u0435\u0441\u0442\u044c. \u0414\u043b\u044f \u0434\u0435\u0441\u044f\u0442\u0438 &#8212; \u0441\u043e\u0440\u043e\u043a \u043f\u044f\u0442\u044c. \u042d\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f mesh-\u0442\u043e\u043f\u043e\u043b\u043e\u0433\u0438\u044f, \u0438 \u043e\u043d\u0430 \u043d\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u0442\u0441\u044f &#8212; \u0440\u0430\u0441\u0442\u0451\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0438 \u0440\u0430\u0441\u0445\u043e\u0434 \u0442\u0440\u0430\u0444\u0438\u043a\u0430.SFU (Selective Forwarding Unit) \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443. \u042d\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0438\u0434\u0438\u0442 \u043c\u0435\u0436\u0434\u0443 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c\u0438. \u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 &#8212; \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043a \u0438 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 \u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c.\u041a\u0430\u043a\u0438\u0435 \u0444\u0438\u0447\u0438 \u044f \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u041a\u043e\u0433\u0434\u0430 \u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u0441\u0432\u043e\u0439 SFU, \u044f \u0441\u0440\u0430\u0437\u0443 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b, \u0447\u0442\u043e \u0445\u043e\u0447\u0443:1. \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043d\u0430 \u043b\u0435\u0442\u0443\u041d\u0435 \u0443 \u0432\u0441\u0435\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u0430\u043d\u0430\u043b. \u041a\u0442\u043e-\u0442\u043e \u0441\u0438\u0434\u0438\u0442 \u0441 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430, \u043a\u0442\u043e-\u0442\u043e &#8212; \u0441 \u043e\u043f\u0442\u043e\u0432\u043e\u043b\u043e\u043a\u043d\u0430. SFU \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c 1080p \u0442\u0435\u043c, \u043a\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0435\u0433\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u044c, \u0438 360p &#8212; \u0442\u0435\u043c, \u0443 \u043a\u043e\u0433\u043e \u043f\u043b\u043e\u0445\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u0418 \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.2. \u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043a\u0430\u043d\u0430\u043b\u0430\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442, \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0443\u0445\u0443\u0434\u0448\u0438\u0442\u044c\u0441\u044f. SFU \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u044c \u044d\u0442\u043e (\u043f\u043e\u0442\u0435\u0440\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438) \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u043d\u0438\u0436\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0432\u043e\u043d\u043e\u043a \u043d\u0435 \u043f\u0440\u0435\u0440\u0432\u0430\u043b\u0441\u044f.3. \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430SFU \u043d\u0435 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u0442 \u0432\u0438\u0434\u0435\u043e (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 MCU &#8212; Multipoint Control Unit). \u041e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0435\u0442 \u043f\u0430\u043a\u0435\u0442\u044b. \u042d\u0442\u043e \u0434\u0430\u0451\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u0438 \u043d\u0438\u0437\u043a\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440.\u041a\u0430\u043a \u044d\u0442\u0438 \u0444\u0438\u0447\u0438 \u043f\u043e\u0432\u043b\u0438\u044f\u043b\u0438 \u043d\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443\u042d\u0442\u0438 \u0442\u0440\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0432\u0441\u0451, \u0447\u0442\u043e \u044f \u0434\u0435\u043b\u0430\u043b \u0434\u0430\u043b\u044c\u0448\u0435.\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e, \u0447\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e \u0441 \u0440\u0430\u0437\u043d\u044b\u043c \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u0438\u043d \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0438\u0434\u0435\u043e \u0432 1080p, 720p \u0438 360p \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. SFU \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u043e\u0442\u0434\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0443.\u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u0430, \u0447\u0442\u043e \u044f \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0430\u043d\u0430\u043b\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430. \u0415\u0441\u043b\u0438 \u0443\u0445\u0443\u0434\u0448\u0430\u0435\u0442\u0441\u044f &#8212; \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u043d\u0438\u0437\u043a\u043e\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e. \u0415\u0441\u043b\u0438 \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u0442\u0441\u044f &#8212; \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e.\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430\u00a0\u043e\u0437\u043d\u0430\u0447\u0430\u043b\u0430, \u0447\u0442\u043e \u044f \u043d\u0435 \u043c\u043e\u0433\u0443 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0422\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u0430\u0442\u044c RTP-\u043f\u0430\u043a\u0435\u0442\u044b. \u042d\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u043b\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443, \u043d\u043e \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u043b\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f: \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430, \u0430 \u043d\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\u042d\u0442\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043c\u0435\u043d\u044f \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435:1. \u041a\u043e\u043c\u043d\u0430\u0442\u0430 \u043a\u0430\u043a \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 RTP-\u043f\u0430\u043a\u0435\u0442\u044b \u0438 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 \u0438\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c \u0431\u0435\u0437 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a. \u041a\u043e\u043c\u043d\u0430\u0442\u0430 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0432 \u0440\u043e\u043b\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430: \u043e\u043d\u0430 \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u0443\u0435\u0442 \u043c\u0435\u0434\u0438\u0430 \u043e\u0442 Publisher \u0432\u043e \u0432\u0441\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.2. User (\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c)\u041a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c User, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0440\u043e\u043b\u044f\u0445 &#8212; \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044f (Publisher) \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 (Subscriber). \u0420\u043e\u043b\u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043b\u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0438 \u0442\u043e, \u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. User \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u044d\u0442\u0438 \u0440\u043e\u043b\u0438 \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u0445 \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043a\u043e\u043c\u043d\u0430\u0442\u043e\u0439.3. Publisher (\u0418\u0437\u0434\u0430\u0442\u0435\u043b\u044c)\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u044f. Publisher \u0445\u0440\u0430\u043d\u0438\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0434\u0435\u043e\u0442\u0440\u0435\u043a\u043e\u0432 \u0440\u0430\u0437\u043d\u043e\u0433\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0438 \u0430\u0443\u0434\u0438\u043e\u0442\u0440\u0435\u043a, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0445 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0438 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0441\u043b\u043e\u0439 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430.4. QualityMonitor (\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430)\u041d\u0443\u0436\u0435\u043d \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0430\u043d\u0430\u043b\u0430 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e \u0441\u043c\u0435\u043d\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430. QualityMonitor \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c (\u043f\u043e\u0442\u0435\u0440\u044f \u043f\u0430\u043a\u0435\u0442\u043e\u0432, \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438) \u0438 \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 Publisher, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.5. \u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 (VideoSubscription \/ AudioSubscription)\u041d\u0443\u0436\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043a\u0430\u043d\u0430\u043b\u044b \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 &#8212; \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a \u043d\u0435 \u0437\u0430\u043c\u0435\u0442\u0438\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430. VideoSubscription \u0445\u0440\u0430\u043d\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0441\u043b\u043e\u0438 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0440\u0435\u043a. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 Publisher \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0441\u043b\u043e\u0439, \u0430 \u043a\u043e\u043c\u043d\u0430\u0442\u0435 &#8212; \u0440\u0435\u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u044f \u043f\u0440\u043e\u0439\u0434\u0443\u0441\u044c \u043b\u0438\u0448\u044c \u043f\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432, \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u0441\u043c\u0435\u043d\u0443 \u0441\u043b\u043e\u0435\u0432 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0447\u0430\u0441\u0442\u0438.\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0439 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442\u041c\u043d\u0435 \u043d\u0443\u0436\u0435\u043d \u0431\u044b\u043b \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434. \u041f\u0440\u044f\u043c\u044b\u0435 tokio-\u0442\u0430\u0441\u043a\u0438, Arc&lt;Mutex&gt;  \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u0433\u043e\u043d\u043a\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0445: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u0438\u043d \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0447\u0438\u0442\u0430\u043b \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432, \u043f\u043e\u043a\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0435\u0433\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043b. \u041d\u0443\u0436\u043d\u0430 \u0431\u044b\u043b\u0430 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e.\u0411\u0440\u0430\u0442\u044c Actix \u0440\u0430\u0434\u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0431\u044b\u043b\u043e \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u043d\u0430\u0434\u00a0tokio::spawn\u00a0+\u00a0mpsc::channel\u00a0\u0432 \u0434\u0443\u0445\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 Active Object:\u041a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0436\u0438\u0432\u0451\u0442 \u0432 \u0441\u0432\u043e\u0435\u0439 \u0442\u0430\u0441\u043a\u0435\u0412\u0441\u0451 \u043e\u0431\u0449\u0435\u043d\u0438\u0435 &#8212; \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u043d\u0430\u043b\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e &#8212; \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043d\u0443\u0442\u0440\u0438 \u0442\u0430\u0441\u043a\u0438, \u0431\u0435\u0437 \u043c\u044c\u044e\u0442\u0435\u043a\u0441\u043e\u0432\u041d\u0438\u043a\u0430\u043a\u043e\u0439 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 \u0441\u0443\u043f\u0435\u0440\u0432\u0438\u0437\u043e\u0440\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u043e\u0432 \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u0442 &#8212; \u0434\u043b\u044f \u043c\u043e\u0435\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u044f \u044d\u0442\u043e \u043d\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c. \u041f\u0430\u0434\u0435\u043d\u0438\u0435 \u0442\u0430\u0441\u043a\u0438 WebRTC-\u043f\u0438\u0440\u0430 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043b\u0441\u044f, \u0438 \u044d\u0442\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043a\u043e\u043c\u043d\u0430\u0442\u044b.pub trait Actor: Sized + Send + &#8216;static {    type Message: Sized + Send + &#8216;static;    fn handle(&amp;mut self, ctx: &amp;mut Ctx&lt;&#8216;_, Self&gt;, m: Self::Message) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn starting(&amp;mut self, ctx: &amp;Ctx&lt;&#8216;_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn stopping(self, ctx: &amp;Ctx&lt;&#8216;_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send;    fn stop(&amp;mut self, ctx: &amp;mut Ctx&lt;&#8216;_, Self&gt;) -&gt; impl Future&lt;Output = ()&gt; + Send {        async move {            ctx.should_stop = true;        }    }    fn start(self) -&gt; Addr&lt;Self&gt; {        Addr::spawn(self, 32)    }    fn start_with_capacity(self, capacity: usize) -&gt; Addr&lt;Self&gt; {        Addr::spawn(self, capacity)    }}\u041a\u0430\u0436\u0434\u044b\u0439 \u0430\u043a\u0442\u043e\u0440 \u0436\u0438\u0432\u0435\u0442 \u0432 \u0441\u0432\u043e\u0439 tokio \u0442\u0430\u0441\u043a\u0435, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e mpsc \u043a\u0430\u043d\u0430\u043b\u0443. \u0427\u0442\u043e\u0431\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u043c\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u043c\u0435\u0442\u044c \u0435\u0433\u043e \u0430\u0434\u0440\u0435\u0441.pub struct Addr&lt;A: Actor&gt; {    id: Uuid,    requests: tokio::sync::mpsc::Sender&lt;&lt;A as Actor&gt;::Message&gt;,    terminate_call: Arc&lt;Mutex&lt;Option&lt;tokio::sync::oneshot::Sender&lt;()&gt;&gt;&gt;&gt;}impl&lt;A: Actor&gt; Addr&lt;A&gt; {    fn spawn(mut actor: A, capacity: usize) -&gt; Self {        let (requests, messages) = tokio::sync::mpsc::channel(capacity);        let (terminate_call, terminate) = tokio::sync::oneshot::channel();        let addr = Addr { id: Uuid::new_v4(), requests, terminate_call: Arc::new(Mutex::new(Some(terminate_call))) };        tokio::spawn({            let ctx_addr = addr.clone();            async move {                let mut ctx = Ctx {addr: &amp;ctx_addr, should_stop: false};                actor.starting(&amp;ctx).await;                tokio::select! {                    _ = handle_messages(&amp;mut actor, &amp;mut ctx, messages) =&gt; {},                    _ = terminate =&gt; {}                }                actor.stopping(&amp;ctx).await;            }        });        addr    }    pub async fn send(&amp;self, m: &lt;A as Actor&gt;::Message) -&gt; Result&lt;(), SendError&lt;&lt;A as Actor&gt;::Message&gt;&gt; {        self.requests.send(m).await?;        Ok(())    }}\u0410\u043a\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d (\u0438 \u0434\u0440\u043e\u043f\u043d\u0443\u0442) \u0432 \u0434\u0432\u0443\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445: \u043d\u0435 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 \u0443 \u0430\u0434\u0440\u0435\u0441\u0430 \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u043c\u0435\u0442\u043e\u0434 terminate. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u044f \u043b\u044e\u0431\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 send \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0431\u044b\u043b \u0434\u0440\u043e\u043f\u043d\u0443\u0442.\u0421\u0435\u0440\u0432\u0435\u0440-\u043a\u043b\u0438\u0435\u043d\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 sfu \u0432\u043a\u0440\u0430\u0442\u0446\u0435 \u043e\u043f\u0438\u0448\u0443 \u0442\u043e \u043a\u0430\u043a \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u041a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043d\u0430\u0442. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u0430\u043a \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0438 \u0437\u0430\u0439\u0442\u0438 \u0432 \u043d\u0443\u0436\u043d\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u0442\u0430\u043a \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u044e POST \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c. \u0426\u0435\u043b\u044c\u044e \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0441 WEBRTC \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u043d\u0430\u0442 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u043d\u0435 \u0431\u044b\u043b\u043e, \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430.\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043c\u043d\u0430\u0442\u0443, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0432 OBS \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443 \u0441 \u0433\u0438\u0444\u043a\u043e\u0439 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c\u0441\u044f c \u0434\u0440\u0443\u0433\u0438\u0445 \u0432\u043a\u043b\u0430\u0434\u043e\u043a.\u041f\u043e\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u0441\u044f \u0432 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u0440 WEBRTC\u0418\u0442\u0430\u043a \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a \u043a\u043e\u043c\u043d\u0430\u0442\u0435 \u0441 \u0434\u0432\u0443\u0445 \u0432\u043a\u043b\u0430\u0434\u043e\u043a \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430 \u043e\u0431\u043e\u0438\u0445 \u0438\u0437 \u043d\u0438\u0445 \u043d\u0430\u0448\u0430 \u0432\u0438\u0440\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u0430. \u0414\u0443\u043c\u0430\u044e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.\u041f\u043e \u0441\u0443\u0442\u0438 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u043e\u0439 rtp \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u043e\u0442 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u0441\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u0435\u0440\u0435\u0434\u0430\u043b \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430 \u0442\u043e\u0442 \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0443\u0436\u0435 \u0441\u0432\u044f\u0437\u0430\u043b \u0435\u0433\u043e \u0441 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0438 \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442.\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443\u041f\u043e\u043f\u0430\u0432 \u0432 \u043a\u043e\u043c\u043d\u0430\u0442\u0443 \u043c\u044b c\u043e\u0437\u0434\u0430\u0435\u043c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0430\u0439\u0434\u0438 \u043a\u043e\u043c\u043d\u0430\u0442\u044bfunction createWebSocket (room_id) {    const hostname = window.location.hostname;    if (hostname === &#8216;localhost&#8217;) {        return new WebSocket(`ws:\/\/${hostname}:8080\/api\/room\/${room_id}`);    } else {   &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-486228","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/486228","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=486228"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/486228\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=486228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=486228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=486228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}