{"id":13268,"date":"2022-03-31T07:00:17","date_gmt":"2022-03-31T05:00:17","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=13268"},"modified":"2023-06-14T16:01:21","modified_gmt":"2023-06-14T14:01:21","slug":"webrtc-what-stands-behind-most-video-conferencing-software-setting-up-a-simple-connection-with-javascript-kotlin","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/webrtc-what-stands-behind-most-video-conferencing-software-setting-up-a-simple-connection-with-javascript-kotlin\/","title":{"rendered":"WebRTC: what stands behind most video conferencing software? Setting up a simple connection with JavaScript\/Kotlin"},"content":{"rendered":"\n<p>WebRTC is a free and open-source video\/audio communication technology. Simply put, it allows to incorporate video conferencing solutions on web pages and in phones, without any additional software or plugins.<\/p>\n\n\n\n<p> It is supported by giants like Google, Apple, or Mozilla, and is getting more popular. A variety of applications used by millions around the globe are built upon it and here are just some examples of them:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Google Hangouts, Google Meet, Google Duo<\/li>\n\n\n\n<li>Facebook Messenger<\/li>\n\n\n\n<li>WhatsApp<\/li>\n\n\n\n<li>Discord<\/li>\n\n\n\n<li>Snapchat<\/li>\n\n\n\n<li>Microsoft Teams<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Essential features:<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Low latency \u2013 in its superior form, WebRTC uses UDP sockets for media transfer. This guarantees the lowest possible latencies \u2013 even less than 500ms.<\/li>\n\n\n\n<li>No additional plugins. Since it is built into browsers \u2013 we don\u2019t need any additional software to run WebRTC solutions.<\/li>\n\n\n\n<li>As part of the HTML5 specification \u2013 <a href=\"https:\/\/caniuse.com\/?search=webrtc\" rel=\"nofollow\" >WebRTC is currently supported by most of the modern browsers<\/a>: Edge 15+, Firefox 22+, Chrome 23+, Safari 11+, Opera18+ etc.<\/li>\n\n\n\n<li>WebRTC connection requires a secure HTTPS protocol by default.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">How does it work?<\/h2>\n\n\n\n<p>While for the end-user, WebRTC based solutions are most often seamlessly integrated into a webpage, thus making it particularly easy to use, its development side is more complex.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">WebRTC APIs<\/h3>\n\n\n\n<p>Let&#8217;s start with what browsers provide us with in terms of setting up a proper WebRTC connection. We do get a set of JavaScript APIs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>MediaStream<\/em>: this API is responsible for gaining access to webcams and microphones of the peer\u2019s device.<\/li>\n\n\n\n<li><em>RTCPeerConnection<\/em>: the main component of WebRTC technology. It provides codec handling, peer-to-peer communication capabilities, security, bandwidth management. It allows to set up the connection (not without intermediaries though).<\/li>\n\n\n\n<li><em>RTCDataChannel<\/em>: designed to provide the possibility for bi-directional data transfer.<\/li>\n<\/ul>\n\n\n\n<p>To set up a simple peer-to-peer connection, we would only need to use the first two APIs. The third one, RTCDataChannel, is useful only when we want to exchange additional (apart from the media) data between peers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Signaling<\/h3>\n\n\n\n<p>Now that we know what browsers provide us with out of the box, let&#8217;s get into details of what we have to provide ourselves:<\/p>\n\n\n\n<p>Due to its browser-based peer-to-peer nature, WebRTC requires an additional intermediate negotiator \u2013 called Signaling. The good news is that we can use any technology we like to implement it, e.g., REST or WebSockets. Take a look at the diagram:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-13361\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/Przechwytywanie.png\"><img decoding=\"async\" width=\"641\" height=\"219\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/Przechwytywanie.png\" alt=\"Diagram of Signalling\" class=\"wp-image-13361\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/Przechwytywanie.png 641w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/Przechwytywanie-300x102.png 300w\" sizes=\"(max-width: 641px) 100vw, 641px\" \/><\/a><figcaption class=\"wp-element-caption\">Fig. 1 Signaling<\/figcaption><\/figure>\n\n\n\n<p>As you can see, signaling is something that stands next to the main audio\/video communication flow, but in fact, it is equally important. Its main purpose is to send the data required to initiate the connection. What exact data need to be sent by signaling? Let\u2019s start with what could be named metadata in the world of WebRTC communication:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SDP &#8211; Session Description Protocol<\/h3>\n\n\n\n<p>This special set of data contains all the information about the type of media a peer is ready to both transmit and receive. Also includes compression codecs the endpoint is capable of decoding and IP addresses prepared to receive the incoming media stream (although this section is not mandatory because there is another mechanism for this purpose \u2013 ICE).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ICE &#8211; Internet Connectivity Establishment<\/h3>\n\n\n\n<p>This might be the trickiest part of the whole WebRTC concept. To understand why \u2013 take a look at some facts: peers will most likely be located behind some NAT (Network Address Translation) and\/or firewall. This means that they won\u2019t have public IP addresses themselves but will rather be available through some translated paths defined by the router (NAT) or that opening a direct connection simply won\u2019t be possible (firewall).<\/p>\n\n\n\n<p>When it comes to WebRTC, it is crucial to know under what IP addresses peers are available. So, in order to bind the connection, each peer has to first make use of the so-called ICE protocol, to find out their publicly available addresses, as well as the shortest path for media to travel between endpoints. ICE protocol achieves this with the help of two other technologies: STUN\/TURN servers. Let\u2019s take a look at the diagram to analyze the STUN scenario:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-13362\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-1.png\"><img decoding=\"async\" width=\"636\" height=\"293\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-1.png\" alt=\"Diagram of STUN server\" class=\"wp-image-13362\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-1.png 636w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-1-300x138.png 300w\" sizes=\"(max-width: 636px) 100vw, 636px\" \/><\/a><figcaption class=\"wp-element-caption\">Fig. 2 STUN server<\/figcaption><\/figure>\n\n\n\n<p><strong>STUN<\/strong> server is used to allow peers to learn their public NAT IP address and port. Once the peer has those details, it can send them to the other peer through the signaling server. This scenario is more desirable because in the end media transfer will happen in a peer-to-peer manner. However, sometimes STUN server is not enough. For some network topologies, we\u2019d need the other solution based on the TURN server:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full wp-image-13363\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-1.png\"><img decoding=\"async\" width=\"653\" height=\"347\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-1.png\" alt=\"Diagram of TURN server\" class=\"wp-image-13363\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-1.png 653w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-1-300x159.png 300w\" sizes=\"(max-width: 653px) 100vw, 653px\" \/><\/a><figcaption class=\"wp-element-caption\">Fig. 3 TURN server<\/figcaption><\/figure>\n\n\n\n<p><strong>TURN<\/strong> is an intermediary server that allows peers to send and receive media. We can imagine that this scenario is less desirable, as it would introduce additional latency and the TURN server itself would have to handle a lot of traffic. This might however be necessary e.g., in topologies behind symmetric NAT.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">WebRTC in practice<\/h2>\n\n\n\n<p>We now have all the pieces required to set up at least a simple connection between two peers. Let\u2019s build the solution.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Preconditions:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>client (peer) side code written in JavaScript,<\/li>\n\n\n\n<li>signaling server \u2013 based on WebSockets, Java\/Kotlin,<\/li>\n\n\n\n<li>STUN\/TURN server &#8211; <a href=\"https:\/\/github.com\/coturn\/coturn\" rel=\"nofollow\" >free open source COTURN project,<\/a><\/li>\n\n\n\n<li>Whenever there is a field \u201cpublicIP\u201d in the code \u2013 it is a placeholder for your public IP address. If you don\u2019t have one, you can use your local LAN address, but then your solution will be limited to the local LAN network.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Client-side code<\/h3>\n\n\n\n<p>First, let\u2019s define some constants:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2.png\"><img decoding=\"async\" width=\"3992\" height=\"544\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2.png\" alt=\"part of code\" class=\"wp-image-13364\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2.png 3992w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2-300x41.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2-1024x140.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2-768x105.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2-1536x209.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/1-2-2048x279.png 2048w\" sizes=\"(max-width: 3992px) 100vw, 3992px\" \/><\/a><\/figure>\n\n\n\n<p>We have a <em>signaling server object, media constraints, STUN server url, RTCPeerConnection<\/em> and two <em>HTML video elements<\/em>.<\/p>\n\n\n\n<p>Now, we will make use of one of the WebRTC APIs provided by the browser. Handling the following events should be enough in our case:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3.png\"><img decoding=\"async\" width=\"3992\" height=\"528\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3.png\" alt=\"Code snippet\" class=\"wp-image-13369\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3.png 3992w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3-300x40.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3-1024x135.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3-768x102.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3-1536x203.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/2-3-2048x271.png 2048w\" sizes=\"(max-width: 3992px) 100vw, 3992px\" \/><\/a><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>onicecandidate<\/em> event will be called whenever ICE protocol finds an ICE candidate. Our handler will automatically send it to the other peer through the signaling server.<\/li>\n\n\n\n<li><em>ontrack<\/em> is called when we receive a stream from the remote peer.<\/li>\n\n\n\n<li><em>onnegotiationended <\/em>this event would be called when there is a local media device detected and acquired through the WebRTC media API.<\/li>\n<\/ul>\n\n\n\n<p>The code for handling signaling messages looks as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2.png\"><img decoding=\"async\" width=\"3992\" height=\"2396\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2.png\" alt=\"Code snippet\" class=\"wp-image-13370\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2.png 3992w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2-300x180.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2-1024x615.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2-768x461.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2-1536x922.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/3-2-2048x1229.png 2048w\" sizes=\"(max-width: 3992px) 100vw, 3992px\" \/><\/a><\/figure>\n\n\n\n<p>We distinguish two types of messages: <em>description<\/em> (for the SDP description that would come from the remote peer) and <em>candidate<\/em> for ICE Candidate objects.<\/p>\n\n\n\n<p>And now the last piece of the client JavaScript code:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1.png\"><img decoding=\"async\" width=\"3992\" height=\"1324\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1.png\" alt=\"Code snippet\" class=\"wp-image-13371\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1.png 3992w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1-300x99.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1-1024x340.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1-768x255.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1-1536x509.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/4-1-2048x679.png 2048w\" sizes=\"(max-width: 3992px) 100vw, 3992px\" \/><\/a><\/figure>\n\n\n\n<p>We\u2019ve attached a handler that makes use of the WebRTC media API. This function should acquire a local camera device and initiate the connection procedure (through the <em>onnegotiationended<\/em> event).<\/p>\n\n\n\n<p>Our HTML markup is super simple:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1.png\"><img decoding=\"async\" width=\"3252\" height=\"1428\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1.png\" alt=\"Code snippet\" class=\"wp-image-13372\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1.png 3252w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1-300x132.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1-1024x450.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1-768x337.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1-1536x674.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/5-1-2048x899.png 2048w\" sizes=\"(max-width: 3252px) 100vw, 3252px\" \/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Signaling code<\/h3>\n\n\n\n<p>Ok, now we will take a look at how to implement a simple WebSocket-based signaling server. First, the configuration class:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1.png\"><img decoding=\"async\" width=\"2916\" height=\"1232\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1.png\" alt=\"Code snippet\" class=\"wp-image-13373\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1.png 2916w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1-300x127.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1-1024x433.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1-768x324.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1-1536x649.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/6-1-2048x865.png 2048w\" sizes=\"(max-width: 2916px) 100vw, 2916px\" \/><\/a><\/figure>\n\n\n\n<p>And the handler bean implementation:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1.png\"><img decoding=\"async\" width=\"2916\" height=\"1872\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1.png\" alt=\"Code snippet\" class=\"wp-image-13374\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1.png 2916w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1-300x193.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1-1024x657.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1-768x493.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1-1536x986.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/7-1-2048x1315.png 2048w\" sizes=\"(max-width: 2916px) 100vw, 2916px\" \/><\/a><\/figure>\n\n\n\n<p>The handler is prepared to handle two WebSocket sessions. Any message that comes from session one is being forwarded to session two and messages from session two are being sent to session one. Methods for registering\/unregistering sessions have been omitted for readability.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SSL<\/h3>\n\n\n\n<p>We can\u2019t forget about the SSL certificates, as the WebRTC wouldn\u2019t work for an unsecured connection. For the signaling part, for demonstration purposes it is enough to generate a self-signed certificate and store it in a .jks file. Having this, we only need to set the following properties in application.properties file:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1.png\"><img decoding=\"async\" width=\"2696\" height=\"440\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1.png\" alt=\"Code snippet\" class=\"wp-image-13375\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1.png 2696w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1-300x49.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1-1024x167.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1-768x125.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1-1536x251.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/8-1-2048x334.png 2048w\" sizes=\"(max-width: 2696px) 100vw, 2696px\" \/><\/a><\/figure>\n\n\n\n<p>For the client part, we need to choose a web server with HTTPS capabilities. Dockerized Nginx seems to be a perfect choice. Here is the Dockerfile:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1.png\"><img decoding=\"async\" width=\"2696\" height=\"434\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1.png\" alt=\"Code snippet\" class=\"wp-image-13376\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1.png 2696w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1-300x48.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1-1024x165.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1-768x124.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1-1536x247.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/9-1-2048x330.png 2048w\" sizes=\"(max-width: 2696px) 100vw, 2696px\" \/><\/a><\/figure>\n\n\n\n<p>As you can see, there is a COPY command for copying the SSL certificate and private key to the Nginx internal structures. Nginx expects SSL files (localhost.crt, localhost.key) defined in the following configuration:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1.png\"><img decoding=\"async\" width=\"2696\" height=\"1584\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1.png\" alt=\"Code snippet\" class=\"wp-image-13377\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1.png 2696w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1-300x176.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1-1024x602.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1-768x451.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1-1536x902.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/10-1-2048x1203.png 2048w\" sizes=\"(max-width: 2696px) 100vw, 2696px\" \/><\/a><\/figure>\n\n\n\n<p>Now we can finally build the image and run the container:<\/p>\n\n\n\n<p><em>docker build -t nginx .<\/em><\/p>\n\n\n\n<p><em>docker run -p 1234:443 nginx<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Working solution<\/h2>\n\n\n\n<p>Below we have an example of the solution in practice. I\u2019ve set up two laptops next to each other. The camera of one of them points to the stopwatch and the second one is connected through our WebRTC app.<\/p>\n\n\n\n<p>To add some spice, i.e. to test the solution in a more real live environment, the two machines are connected to separate internet networks, and one of them is additionally proxied through the VPN server located in Portugal, which gives us roughly 2800km distance. The example is shown as a couple-seconds video, with a pause in-between, to show the actual delay:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"webrtc\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/jIjuHjLlFWo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>So, as you can see, we were able to achieve a delay of around 200ms which is a great result.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this article, we\u2019ve learnt the basics of WebRTC technology and built a solution upon it. The fact that WebRTC is being used and supported by giants like Google or Mozilla together with the result of the code presented here, which shows excellent results when it comes to media transmission latency, proves that WebRTC should be considered one of the first choices for video conferencing solutions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub repository:<\/h3>\n\n\n\n<p><a href=\"https:\/\/github.com\/tomaszfirlej\/webrtc-poc-sii\" rel=\"nofollow\" >Tomasz Firlej<\/a><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;13268&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;13&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;4.9&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;4.9\\\/5 ( votes: 13)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;WebRTC: what stands behind most video conferencing software? Setting up a simple connection with JavaScript\\\/Kotlin&quot;,&quot;width&quot;:&quot;136.6&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 136.6px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            4.9\/5 ( votes: 13)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>WebRTC is a free and open-source video\/audio communication technology. Simply put, it allows to incorporate video conferencing solutions on web &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/webrtc-what-stands-behind-most-video-conferencing-software-setting-up-a-simple-connection-with-javascript-kotlin\/\">Continued<\/a><\/p>\n","protected":false},"author":346,"featured_media":13274,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[],"class_list":["post-13268","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/03\/Web-RTC.png","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/13268"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/346"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=13268"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/13268\/revisions"}],"predecessor-version":[{"id":22310,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/13268\/revisions\/22310"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/13274"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=13268"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=13268"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=13268"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}