<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="https://web-engineering.info"  xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>web-engineering.info - HTML5</title>
 <link>https://web-engineering.info/taxonomy/term/42</link>
 <description></description>
 <language>en</language>
<item>
 <title>Drag&amp;Drop HTML5/JS API Summary</title>
 <link>https://web-engineering.info/node/73</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot; property=&quot;content:encoded&quot;&gt;&lt;h2&gt;Test the Drag &amp;amp; Drop Mechanism&lt;/h2&gt;

&lt;script&gt;
  function handleDragStart( evt) {
    evt.dataTransfer.dropEffect = &#039;copy&#039;;
    evt.dataTransfer.setData(&quot;text/plain&quot;, evt.target.id);
  }
  function handleDragOver( evt) {
    // allow dropping by preventing the default behavior
    evt.preventDefault();
  }
  function handleDrop( evt) {
    var elId = evt.dataTransfer.getData(&quot;text/plain&quot;),
        el = document.getElementById( elId),
        x = evt.clientX, y = evt.clientY;
    el.style.position = &quot;absolute&quot;;
    el.style.left = x +&quot;px&quot;;
    el.style.top = y +&quot;px&quot;;
    el.textContent = &quot;Moved to &quot;+ x +&quot;/&quot;+ y;
    evt.preventDefault();
  }
 &lt;/script&gt;
&lt;div id=&quot;dropZone&quot; ondragover=&quot;handleDragOver(event)&quot; ondrop=&quot;handleDrop(event)&quot; style=&quot;width: 800px; height: 300px; background-color: pink; margin-bottom:1em&quot;&gt;
&lt;div draggable=&quot;true&quot; id=&quot;drag&quot; ondragstart=&quot;handleDragStart(event)&quot; style=&quot;width: 100px; height: 100px; padding: 10px; background-color: deeppink; z-index: 1000; cursor: move;&quot;&gt;Move me into the pink area (the drop zone).&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The HTML specification defines an event-based &lt;a href=&quot;https://html.spec.whatwg.org/multipage/dnd.html&quot;&gt;&lt;em&gt;drag-and-drop&lt;/em&gt;&lt;/a&gt; (D&amp;amp;D) mechanism, which is supported by all current major browsers (including IE11 and iOS/Safari), except on Android, according to &lt;a href=&quot;http://caniuse.com/#feat=dragndrop&quot;&gt;caniuse&lt;/a&gt;. The main concepts of the HTML D&amp;amp;D mechanism are:&lt;/p&gt;

&lt;ol&gt;
	&lt;li&gt;
	&lt;p&gt;A &lt;em&gt;drag-and-drop&lt;/em&gt; operation is a complex user action composed of a &lt;em&gt;drag&lt;/em&gt; user action performed on a source element (the &quot;drag element&quot;) followed by a subsequent &lt;em&gt;drop&lt;/em&gt; user action performed on a target element (the &quot;drop zone&quot;).&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;Using a pointing device (such as a mouse), a &lt;em&gt;drag&lt;/em&gt; user action consists of a &lt;code&gt;mousedown&lt;/code&gt; event on a drag element followed by a series of &lt;code&gt;mousemove&lt;/code&gt; events, while the subsequent &lt;em&gt;drop&lt;/em&gt; user action normally consists of the mouse being released on the drop zone element.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;When a browser detects a &lt;em&gt;drag&lt;/em&gt; user action, it raises a &lt;code&gt;dragstart&lt;/code&gt; event followed by a series of &lt;code&gt;dragover&lt;/code&gt; events while the user keeps dragging the drag element.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;When a browser detects a &lt;em&gt;drop&lt;/em&gt; user action, it raises a &lt;code&gt;dragend&lt;/code&gt; event followed by a &lt;code&gt;drop&lt;/code&gt; event.&lt;/p&gt;
	&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A minimum setup for a D&amp;amp;D operation is obtained by taking the following steps:&lt;/p&gt;

&lt;ol&gt;
	&lt;li&gt;
	&lt;p&gt;HTML elements can be made draggable by&amp;nbsp;setting their &lt;code&gt;draggable&lt;/code&gt; attribute to &lt;code&gt;true&lt;/code&gt;. Image (&lt;code&gt;img&lt;/code&gt;) and hyperlink (&lt;code&gt;a&lt;/code&gt;) elements, as well as text selections, are draggable by default. The&amp;nbsp;D&amp;amp;D API requires to transfer data (about the drag source element) by invoking an event handler for the&amp;nbsp;&lt;code&gt;dragstart&lt;/code&gt; event. So, we get the following HTML code: &amp;nbsp;&lt;/p&gt;

	&lt;pre&gt;
&amp;lt;div id=&quot;drag&quot; &lt;strong&gt;draggable=&quot;true&quot;&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;ondragstart&lt;/strong&gt;=&quot;handleDragStart(event)&quot;&amp;gt;Drag me...&amp;lt;/div&amp;gt;  &lt;/pre&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;When a draggable element is dragged somewhere, the D&amp;amp;D API requires to transfer data from the drag source element to the drop target element via the user interface events involved (mainly&amp;nbsp;&lt;code&gt;dragstart&lt;/code&gt; and &lt;code&gt;drop&lt;/code&gt;). So, we need to define a JS function that handles the&amp;nbsp;&lt;code&gt;dragstart&lt;/code&gt; event such that a data item of the event&#039;s &lt;code&gt;dataTransfer&lt;/code&gt; object is set:&lt;/p&gt;

	&lt;pre&gt;
function &lt;strong&gt;handleDragStart&lt;/strong&gt;( evt) {
  evt.dataTransfer.setData(&quot;text/plain&quot;, evt.target.id);
}&lt;/pre&gt;

	&lt;p&gt;Notice that we create a &lt;code&gt;dataTransfer&lt;/code&gt; item of type &quot;text/plain&quot; (normally, this should be a MIME type, but could also be a user-defined type keyword) with the drag element&#039;s &lt;code&gt;id&lt;/code&gt; as its value.&lt;/p&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;HTML elements can be turned into a &lt;em&gt;drop zone&lt;/em&gt; for a D&amp;amp;D operation by&amp;nbsp;invoking an event handler for the &lt;code&gt;dragover&lt;/code&gt; event and another one for the &lt;code&gt;drop&lt;/code&gt; event:&lt;/p&gt;

	&lt;pre&gt;
     &amp;lt;div id=&quot;dropZone&quot; &lt;strong&gt;ondrop&lt;/strong&gt;=&quot;handleDrop(event)&quot; &lt;strong&gt;ondragover&lt;/strong&gt;=&quot;handleDragOver(event)&quot;&amp;gt;&amp;lt;/div&amp;gt;
   &lt;/pre&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;When a draggable element is dragged over an element where it is supposed to be dropped (the &quot;drop zone&quot;), the&amp;nbsp;D&amp;amp;D API requires to prevent the browser&#039;s default behavior by invoking &lt;code&gt;preventDefault&lt;/code&gt; in the JS function that handles the &lt;code&gt;dragover&lt;/code&gt; event:&lt;/p&gt;

	&lt;pre&gt;
function &lt;strong&gt;handleDragOver&lt;/strong&gt;( evt) {
  evt.preventDefault();
}   &lt;/pre&gt;
	&lt;/li&gt;
	&lt;li&gt;
	&lt;p&gt;Finally, the &lt;code&gt;drop&lt;/code&gt; event needs to be handled by making use of the transfered data items for achieving some effect. For instance, we can use the transferred&amp;nbsp;&lt;code&gt;id&lt;/code&gt; value of the drag element for positioning it within the drop zone element:&lt;/p&gt;

	&lt;pre&gt;
function &lt;strong&gt;handleDrop&lt;/strong&gt;( evt) {
  var dragElId = evt.dataTransfer.getData(&quot;text/plain&quot;),
      dragEl = document.getElementById( dragElId),
      x = evt.clientX, y = evt.clientY;
  dragEl.style.position = &quot;absolute&quot;;
  dragEl.style.left = x +&quot;px&quot;;
  dragEl.style.top = y +&quot;px&quot;;  
  evt.preventDefault();
}&lt;/pre&gt;

	&lt;p&gt;Notice that the dataTransfer item type string (in our example &quot;text/plain&quot;) is a key for retrieving the value of the data item.&lt;/p&gt;
	&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The HTML D&amp;amp;D specification defines some unnecessarily complex features that seem to be simplified by browser implementations. For instance, it requires that the three events &lt;code&gt;dragstart&lt;/code&gt;,&amp;nbsp;&lt;code&gt;dragover&lt;/code&gt; and &lt;code&gt;drop&lt;/code&gt; are cancelled (by invoking&amp;nbsp;&lt;code&gt;preventDefault&lt;/code&gt; in the respective JS event handler) for allowing to drag into a drop zone, but it turns out to be sufficient to do this for&amp;nbsp;&lt;code&gt;dragover&lt;/code&gt; and &lt;code&gt;drop&lt;/code&gt;. The specification also defines a few additional features, which are, however, not that much important, like the&amp;nbsp;&lt;code&gt;dropEffect&lt;/code&gt; attribute for setting the drag shape (indicating either a move, a copy or a drag link operation).&lt;/p&gt;

&lt;p&gt;For indicating to the user that an element is&amp;nbsp;draggable, we set the cursor shape to a &quot;move&quot; symbol whenever the cursor is over a draggable element, using the following CSS code:&lt;/p&gt;

&lt;pre&gt;
[draggable=true] {
    cursor: move;
}&lt;/pre&gt;

&lt;p&gt;The following code of the complete example also contains CSS style rules, e.g., for positioning and sizing the drop zone.&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en&quot; lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
&amp;nbsp;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
&amp;nbsp;&amp;lt;title&amp;gt;Test Drag&amp;amp;Drop&amp;lt;/title&amp;gt;
&amp;nbsp;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale = 1.0&quot; /&amp;gt;
&amp;nbsp;&amp;lt;style&amp;gt;
&amp;nbsp; #drag {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; width: 100px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; height: 100px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; padding: 10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; background-color: deeppink;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;z-index: 1000;
&amp;nbsp; }
&amp;nbsp; [draggable=true] {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cursor: move;
&amp;nbsp; }
&amp;nbsp; #dropZone {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; position: fixed;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; width: 100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; height: 100%;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; background-color: pink;
&amp;nbsp; }
&amp;nbsp;&amp;lt;/style&amp;gt;
 &amp;lt;script&amp;gt;
&amp;nbsp; function handleDragStart( evt) {
&amp;nbsp;&amp;nbsp;&amp;nbsp; evt.dataTransfer.dropEffect = &#039;copy&#039;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; evt.dataTransfer.setData(&quot;text/plain&quot;, evt.target.id);
&amp;nbsp; }
&amp;nbsp; function handleDragOver( evt) {
&amp;nbsp;&amp;nbsp;&amp;nbsp; // allow dropping by preventing the default behavior
&amp;nbsp;&amp;nbsp;&amp;nbsp; evt.preventDefault();
&amp;nbsp; }
&amp;nbsp; function handleDrop( evt) {
&amp;nbsp;&amp;nbsp;&amp;nbsp; var elId = evt.dataTransfer.getData(&quot;text/plain&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; el = document.getElementById( elId),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; x = evt.clientX, y = evt.clientY;
&amp;nbsp;&amp;nbsp;&amp;nbsp; el.style.position = &quot;absolute&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; el.style.left = x +&quot;px&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; el.style.top = y +&quot;px&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; el.textContent = &quot;Moved to &quot;+ x +&quot;/&quot;+ y;
    evt.preventDefault();
&amp;nbsp; }
 &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;nbsp;&amp;lt;h1&amp;gt;Test Drag &amp;amp;amp; Drop&amp;lt;/h1&amp;gt;
&amp;nbsp;&amp;lt;div id=&quot;drag&quot; draggable=&quot;true&quot; ondragstart=&quot;handleDragStart(event)&quot;&amp;gt;
  Move me into the pink area (the drop zone).
 &amp;lt;/div&amp;gt;
&amp;nbsp;&amp;lt;div id=&quot;dropZone&quot; ondrop=&quot;handleDrop(event)&quot; ondragover=&quot;handleDragOver(event)&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;section class=&quot;field field-name-field-category field-type-taxonomy-term-reference field-label-above view-mode-rss&quot;&gt;&lt;h2 class=&quot;field-label&quot;&gt;Category:&amp;nbsp;&lt;/h2&gt;&lt;ul class=&quot;field-items&quot;&gt;&lt;li class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/taxonomy/term/42&quot; typeof=&quot;skos:Concept&quot; property=&quot;rdfs:label skos:prefLabel&quot; datatype=&quot;&quot;&gt;HTML5&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;&lt;div class=&quot;easy_social_box clearfix horizontal easy_social_lang_und&quot;&gt;
            &lt;div class=&quot;easy_social-widget easy_social-widget-twitter first&quot;&gt;&lt;a href=&quot;http://twitter.com/share&quot; class=&quot;twitter-share-button&quot;
data-url=&quot;https://web-engineering.info/node/73&quot;
data-count=&quot;horizontal&quot;
data-lang = &quot;en&quot;
data-via=&quot;&quot;
data-related=&quot;:Check it out!&quot;
data-text=&quot;Drag&amp;Drop HTML5/JS API Summary&quot;&gt;Tweet&lt;/a&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-facebook&quot;&gt;&lt;fb:like href=&quot;https://web-engineering.info/node/73&quot; send=&quot;true&quot; layout=&quot;button_count&quot; width=&quot;88&quot; show_faces=&quot;true&quot; action=&quot;like&quot; colorscheme=&quot;light&quot; font=&quot;&quot;&gt;&lt;/fb:like&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-googleplus&quot;&gt;&lt;div class=&quot;g-plusone&quot; data-size=&quot;medium&quot; data-annotation=&quot;bubble&quot; data-href=&quot;https://web-engineering.info/node/73&quot;&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-linkedin last&quot;&gt;&lt;script type=&quot;in/share&quot; data-url=&quot;https://web-engineering.info/node/73&quot; data-counter=&quot;right&quot;&gt;&lt;/script&gt;&lt;/div&gt;
  &lt;/div&gt; &lt;!-- /.easy_social_box --&gt;</description>
 <pubDate>Wed, 25 Oct 2017 14:27:13 +0000</pubDate>
 <dc:creator>gwagner</dc:creator>
 <guid isPermaLink="false">73 at https://web-engineering.info</guid>
 <comments>https://web-engineering.info/node/73#comments</comments>
</item>
<item>
 <title>Building a Video/Audio Chat Web App with WebRTC</title>
 <link>https://web-engineering.info/node/57</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot; property=&quot;content:encoded&quot;&gt;&lt;h2&gt;WebRTC State of the Art&lt;/h2&gt;

&lt;p&gt;WebRTC (Web Real Time Communication) is a new web standard currently supported by Google, Mozilla and Opera. It allows peer-to-peer communication between browsers. Its mission is to enable rich, high-quality RTC applications for the browser, mobile platforms, and the Web of Things (WoT), and allow them to communicate via a common set of protocols.&lt;/p&gt;

&lt;p&gt;One of the last major challenges for the web is to enable human communication via voice and video without using special plugins and without having to pay for these services. The first WebRTC implementation was built in May 2011 by Ericsson. WebRTC defines open standards for real-time, plugin-free video, audio and data communication. Curently, many web services already use RTC, but require downloads, native apps or plugins. These includes Skype, Facebook (which uses Skype) and Google Hangouts (which use the Google Talk plugin). Downloading, installing and updating plugins can be complex, error prone and annoying and it&#039;s often difficult to convince people to install plugins in the first place!&lt;/p&gt;

&lt;h2&gt;How does it work?&lt;/h2&gt;

&lt;p&gt;In general, a WebRTC-enabled application needs to:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;obtain an audio, video or other data stream;&lt;/li&gt;
	&lt;li&gt;gather network information (e.g., IP addresses and ports), and exchange this with other WebRTC clients;&lt;/li&gt;
	&lt;li&gt;a &quot;signaling&quot; communication is used to report errors, and initiate or close sessions;&lt;/li&gt;
	&lt;li&gt;clients must exchange information about media, such as resolution and codecs;&lt;/li&gt;
	&lt;li&gt;stream the audio, video or data;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebRTC implements three APIs:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;MediaStream - allows the client (e.g., the web browser) to access the stream, such as the one from a WebCam or microphone;&lt;/li&gt;
	&lt;li&gt;RTCPeerConnection - enable audio or video data transfer, with support for encryption and bandwidth management;&lt;/li&gt;
	&lt;li&gt;RTCDataChannel - enables peer-to-peer communication for any generic data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In theory it is possible to create a simple WebRTC application, without any server components for signaling. In practice such application does not make much of a sense because it can be used only on a single page, thus it shares data amoung the same peer.&lt;/p&gt;

&lt;h3&gt;MediaStream&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;http://w3c.github.io/mediacapture-main/getusermedia.html&quot;&gt;MediaStream&lt;/a&gt; represents a synchronized stream(s) of media. Each MediaStream has an input and an output. The &lt;code&gt;getUserMedia&lt;/code&gt; method has three parameters:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;a &lt;a href=&quot;http://w3c.github.io/mediacapture-main/getusermedia.html#the-model-sources-sinks-constraints-and-states&quot;&gt;constraints&lt;/a&gt; object;&lt;/li&gt;
	&lt;li&gt;a success callback method;&lt;/li&gt;
	&lt;li&gt;a failure callback method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a local WebCam stream can be shown in a HTML5 &lt;code&gt;video&lt;/code&gt; element:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script src=&quot;webrtc.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;title&amp;gt;WebRTC Test&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  
  &amp;lt;body&amp;gt;
    &amp;lt;video id=&quot;localVideo&quot; autoplay/&amp;gt;
    &amp;lt;script&amp;gt;
      window.addEventListener(&quot;load&quot;, function (evt) {
        &lt;strong&gt;navigator.getUserMedia({ audio: true, video: true},
          function(stream) {
            var video = document.getElementById(&#039;localVideo&#039;);
            video.src = window.URL.createObjectURL(stream);
          },
          function(err) {
            console.log(&quot;The following error occurred: &quot; + err.name);
          }
        );&lt;/strong&gt;
      });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;h3&gt;RTCPeerConnection&lt;/h3&gt;

&lt;p&gt;The RTCPeerConnection interface represents a WebRTC connection between the local computer and a remote peer. It is used to handle efficient streaming of data between the two peers. Both parties (the caller and the called party) need to set up their own &lt;code&gt;RTCPeerConnection&lt;/code&gt; instances to represent their end of the peer-to-peer connection. In general, we use a &lt;code&gt;RTCPeerConnection::onaddstream&lt;/code&gt; event callback to take care of dealing with the audio/video stream, e.g., assigning it to a HTML5 &lt;code&gt;video&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
var peerConn= new RTCPeerConnection();
&lt;strong&gt;peerConn.onaddstream&lt;/strong&gt; = function (evt) {
  var videoElem = document.createElement(&quot;video&quot;);
  document.appendChild(videoElem);
  videoElem.src = URL.createObjectURL(evt.stream);
};
&lt;/pre&gt;

&lt;p&gt;The initiator of the call (the caller), needs to create an offer and using a signaling service (e.g., a NodeJS server application using WebSockets) send it to the callee:&lt;/p&gt;

&lt;pre&gt;
navigator.getUserMedia({video: true}, function(stream) {
  videoElem.src = URL.createObjectURL(stream);
  peerConn.addStream(stream);

  &lt;strong&gt;peerConn.createOffer&lt;/strong&gt;(function(offer) {
    peerConn.setLocalDescription(new RTCSessionDescription(offer), function() {
      // send the offer to a server to be forwarded to the other peer
    }, error);
  }, error);
});
&lt;/pre&gt;

&lt;p&gt;The callee, which receives the offer and needs to &quot;answer&quot; the call has to create an answer and send it to the caller:&lt;/p&gt;

&lt;pre&gt;
navigator.getUserMedia({video: true}, function(stream) {
  videoElem.src = URL.createObjectURL(stream);
  peerConn.addStream(stream);

  peerConn.setRemoteDescription(new RTCSessionDescription(offer), function() {
    &lt;strong&gt;peerConn.createAnswer&lt;/strong&gt;(function(answer) {
      peerConn.setLocalDescription(new RTCSessionDescription(answer), function() {
        // send the answer to a server to be forwarded back to the caller
      }, error);
    }, error);
  }, error);
});
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;setLocalDescription&lt;/code&gt; method takes three parameters: a session description, a success callback method and an error callback method. This method changes the local description associated with a connection. A description defines the properties of the connection like for example the codec.&lt;/p&gt;

&lt;h3&gt;RTCPeerConnection and Servers&lt;/h3&gt;

&lt;p&gt;In a real application, WebRTC needs servers (in general simple) for the following purposes:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;users management;&lt;/li&gt;
	&lt;li&gt;exchange of information between peers;&lt;/li&gt;
	&lt;li&gt;data exchange about media, such as formats and video resolution:&lt;/li&gt;
	&lt;li&gt;the connections needs to traverse NAT gateways and firewalls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The STUN protocol and its extension TURN are used by the ICE framework to enable &lt;code&gt;RTCPeerConnection&lt;/code&gt; to cope with NAT traversal and other network specific details. ICE is a framework for connecting peers, such as two video chat clients. ICE tries to connect peers directly, with the lowest possible latency, via UDP. In this process, STUN servers have a single task: to enable a peer behind a NAT to find out its public address and port. Google and Mozilla provides a couple of STUN severs which can (for now) be used free of charge. For example, Google STUN servers are used to obtain ICE candidates, which are then forwarded to the other peer(s):&lt;/p&gt;

&lt;pre&gt;
var peerConnCfg =  {&#039;iceServers&#039;: [{&#039;url&#039;: &#039;stun:stun.l.google.com:19302&#039;}]},
    peerConn= new RTCPeerConnection(peerConnCfg),
    signalingChannel = new WebSocket(&#039;ws://my-websocket-server:port/&#039;);

peerConn.onicecandidate = function (evt) {
  // send any ice candidates to the other peer, i.e., evt.candidate
  signalingChannel.send(JSON.stringify({ &quot;candidate&quot;: evt.candidate }));
};

signalingChannel.onmessage = function (evt) {
  var signal = JSON.parse(evt.data);
  if (signal.sdp)
    peerConn.setRemoteDescription(new RTCSessionDescription(signal.sdp));
  else if (signal.candidate)
    peerConn.addIceCandidate(new RTCIceCandidate(signal.candidate));
};
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;signalingChannel&lt;/code&gt; represents the communication channel, based on &lt;code&gt;WebSockets&lt;/code&gt;, &lt;code&gt;XHR&lt;/code&gt; or something else, having the purpose of helping to exchange the required information for the peer-to-peer connection initialization.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;setRemoteDescription&lt;/code&gt; method takes three parameters: a session description, a success callback method and an error callback method. This method changes the remote description associated with a connection. A description defines the properties of the connection like for example the codec.&lt;/p&gt;

&lt;h3&gt;RTCDataChannel&lt;/h3&gt;

&lt;p&gt;The RTCDataChannel interface represents a bi-directional data channel between two peers of a connection. Objects of this type can be created using &lt;code&gt;RTCPeerConnection.createDataChannel()&lt;/code&gt;, or are received in a &lt;code&gt;datachannel&lt;/code&gt; event of type &lt;code&gt;RTCDataChannelEvent&lt;/code&gt; on an existing &lt;code&gt;RTCPeerConnection&lt;/code&gt;. Using a data channel capabilities is &quot;natural&quot;, and makes use of messaging style events based communication:&lt;/p&gt;

&lt;pre&gt;
var peerConn= new RTCPeerConnection(),
    dc = peerConn.createDataChannel(&quot;my channel&quot;);

dc.onmessage = function (event) {
  console.log(&quot;received: &quot; + event.data);
};

dc.onopen = function () {
  console.log(&quot;datachannel open&quot;);
};

dc.onclose = function () {
  console.log(&quot;datachannel close&quot;);
};
&lt;/pre&gt;

&lt;h2&gt;Build a Simple Audio/Video-Chat Web-Application&lt;/h2&gt;

&lt;p&gt;In this section we&#039;ll learn how to build a basic Audio/Video-Chat Web-Application. It allows to perform a video call between two peers and displays the local and remote video. In a real application one has to deal with complex situations, users management, and all kind of errors. In this tutorial we skip error situations, and keep our application simple:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Two friends located on different Earth locations need to have a video call;&lt;/li&gt;
	&lt;li&gt;They are able to use a modern Web Browser, such as Google Chrome or Firefox;&lt;/li&gt;
	&lt;li&gt;They are able access the web application URL using their available internet connection (DSL, 3G or any other type);&lt;/li&gt;
	&lt;li&gt;One of the users initiates the video call by clicking the &quot;Video Call&quot; button;&lt;/li&gt;
	&lt;li&gt;Both users allows the browser to access their WebCams and microphones;&lt;/li&gt;
	&lt;li&gt;Now they are able to see and hear each other until one of users clicks the &quot;End Call&quot; button.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The HTML5 Web UI&lt;/h3&gt;

&lt;p&gt;The HTML5 code is fairly easy. We only define the relevant elements, and for simplicity reasons we don&#039;t use CSS to style it:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &lt;strong&gt;&amp;lt;script src=&quot;webrtc.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/strong&gt;
    &amp;lt;title&amp;gt;WebRTC Audio/Video-Chat&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  
  &amp;lt;body&amp;gt;
    &lt;strong&gt;&amp;lt;video id=&quot;remoteVideo&quot; autoplay&amp;gt;&amp;lt;/video&amp;gt;&lt;/strong&gt;
    &lt;strong&gt;&amp;lt;video id=&quot;localVideo&quot; autoplay muted&amp;gt;&amp;lt;/video&amp;gt;&lt;/strong&gt;
    &lt;strong&gt;&amp;lt;input id=&quot;videoCallButton&quot; type=&quot;button&quot; disabled value=&quot;Video Call&quot;/&amp;gt;&lt;/strong&gt;
    &lt;strong&gt;&amp;lt;input id=&quot;endCallButton&quot; type=&quot;button&quot; disabled value=&quot;End Call&quot;/&amp;gt;&lt;/strong&gt;
    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      &lt;strong&gt;window.addEventListener(&quot;load&quot;, pageReady);&lt;/strong&gt;
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Only four HTML elements are relevant here: the two &lt;code&gt;video&lt;/code&gt; elements, used to display the remote and the local video and the two &lt;code&gt;input&lt;/code&gt; elements, used to create the &quot;Video Call&quot; and &quot;End Call&quot; buttons. The &lt;code&gt;script&lt;/code&gt; element at the end of the code registers a &lt;code&gt;load&lt;/code&gt; event listener (which executes when the page was fully loaded). The relevant code, including the content of the &lt;code&gt;pageReady&lt;/code&gt; method are part of the &lt;code&gt;webrtc.js&lt;/code&gt; file included with the help of a &lt;code&gt;script&lt;/code&gt; element (see &lt;code&gt;head&lt;/code&gt; element).&lt;/p&gt;

&lt;h3&gt;The NodeJS WebSockets-based Signaling Server&lt;/h3&gt;

&lt;p&gt;The NodeJS server application has a very simple job: receive messages from one client and broadcast them to all the others. These messages are the signaling information required by the peers in order to initiate a peer-to-peer connection. For this, we use &lt;code&gt;WebSockets&lt;/code&gt;, which is a built-in API in modern browsers, but requires to install the &lt;code&gt;ws&lt;/code&gt; module for NodeJS.&lt;/p&gt;

&lt;p&gt;At first we need to install the required NodeJS modules (e.g., &lt;code&gt;ws&lt;/code&gt;) by executing &lt;code&gt;npm install&lt;/code&gt; in a shell, inside the root folder of the NodeJS application. More information about this module are available on the &lt;a href=&quot;https://www.npmjs.com/package/ws&quot;&gt;npm ws module page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, create a file named &lt;code&gt;server.js&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;pre&gt;
const WebSocketServer = require(&#039;ws&#039;).Server,
  express = require(&#039;express&#039;),
  https = require(&#039;https&#039;),
  app = express(),
  fs = require(&#039;fs&#039;);

const pkey = fs.readFileSync(&#039;./ssl/key.pem&#039;),
  pcert = fs.readFileSync(&#039;./ssl/cert.pem&#039;),
  options = {key: pkey, cert: pcert, passphrase: &#039;123456789&#039;};
var wss = null, sslSrv = null;
 
// use express static to deliver resources HTML, CSS, JS, etc)
// from the public folder 
app.use(express.static(&#039;public&#039;));

// start server (listen on port 443 - SSL)
sslSrv = https.createServer(options, app).listen(443);
console.log(&quot;The HTTPS server is up and running&quot;);

// create the WebSocket server
wss = new WebSocketServer({server: sslSrv});  
console.log(&quot;WebSocket Secure server is up and running.&quot;);

/** successful connection */
wss.on(&#039;connection&#039;, function (client) {
  console.log(&quot;A new WebSocket client was connected.&quot;);
  /** incomming message */
  client.on(&#039;message&#039;, function (message) {
    /** broadcast message to all clients */
    wss.broadcast(message, client);
  });
});
// broadcasting the message to all WebSocket clients.
wss.broadcast = function (data, exclude) {
  var i = 0, n = this.clients ? this.clients.length : 0, client = null;
  if (n &amp;lt; 1) return;
  console.log(&quot;Broadcasting message to all &quot; + n + &quot; WebSocket clients.&quot;);
  for (; i &amp;lt; n; i++) {
    client = this.clients[i];
    // don&#039;t send the message to the sender...
    if (client === exclude) continue;
    if (client.readyState === client.OPEN) client.send(data);
    else console.error(&#039;Error: the client state is &#039; + client.readyState);
  }
};
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; since WebRTC works ONLY with SSL, for your convenience, we provide a free, self signed SSL certificate together with this application. This certificate shall not be used for other purposes outside playing with the provided demo application. Also, the web browsers will complain about the validity of the SSL certificate because it is not signed by a recognized authority. This means that you should add it to your exception list in order to be able to access the application. Otherwise, feel free to use your own certificate, meaning that you need to replace the two &lt;code&gt;.pem&lt;/code&gt; files from the &lt;code&gt;ssl&lt;/code&gt; subfolder.&lt;/p&gt;

&lt;p&gt;The application communicate via Secure WebSockets on port 443. You can modify this port with other one if required. The above code simply allows WebSocket connections and broadcasts all the messages received from one client, to all other clients (excluding the sender).&lt;/p&gt;

&lt;p&gt;To start the server application, execute &lt;code&gt;node server.js&lt;/code&gt; from the folder where you created the file with the above content. If all went fine, you should see no error message and the server waits for WebSocket connections.
Finally, use a Web Browser and navigate to &lt;a href=&quot;http://your.domain&quot;&gt;http://your.domain&lt;/a&gt; and you should see the application start page. Using &lt;code&gt;localhost&lt;/code&gt; only works for playing locally with the application, and for being able to have a WebRTC connection between two peers having internet connection, one need to use a live server with a public IP address.&lt;/p&gt;

&lt;p&gt;If you are behind a corporate firewall, it is possible that all ports excepting 80 (and maybe 443) are closed. In such a case, one can use the &lt;code&gt;mod_proxy_stunnel&lt;/code&gt; Apache module which allows to proxy WebSocket communication via the port 80. This module is bundled with Apache starting from version 2.4.5. However, most of the stable Linux systems, including CentOS 6.x provides only earlier Apache versons, such as 2.2.x. A pre-compiled version of this module, (Apache 2.2.15, available from the CentOS 6.7 repositories) is &lt;a href=&quot;http://web-engineering.info/tech/webrtc/mod_proxy_wstunnel.so&quot;&gt;available for download on our server&lt;/a&gt;. Further, you have to modify the Apache configuration file, i.e., &lt;code&gt;httpd.conf&lt;/code&gt; file (usually located under &lt;code&gt;/etc/httpd/conf/&lt;/code&gt;) and add the following lines:&lt;/p&gt;

&lt;pre&gt;
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

ProxyPass /websocket/ ws://localhost:3434/
ProxyPassReverse /websocket/ ws://localhost:3434/
&lt;/pre&gt;

&lt;p&gt;Last, restart the Apache Web Server by executing &lt;code&gt;service httpd restart&lt;/code&gt; command, for which you may need &lt;code&gt;root&lt;/code&gt; privileges (i.e., you may have to use &lt;code&gt;sudo&lt;/code&gt; or login as &lt;code&gt;root&lt;/code&gt;). The &quot;websocket&quot; path from the above configuration lines can be replaced with whatever you like, but keep in mind that this is the last part of the URL used by the WebSocket client app to access the server. Also remember to use the same port number as the one used in &lt;code&gt;server.js&lt;/code&gt; (e.g., 3434).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the above information and examples are provided for a CentOS 6.7 Linux distribution, running Apache Web Server 2.2.15 from the official CentOS 6.7 repository. Different Linux distribution or other Apache version may or may not work the same way, so we can&#039;t provide any guaranty on that.&lt;/p&gt;

&lt;h3&gt;The Client JavaScript Code&lt;/h3&gt;

&lt;p&gt;In this section we discuss about the content of the &lt;code&gt;webrtc.js&lt;/code&gt; file. The first part of this file defines the global variables:&lt;/p&gt;

&lt;pre&gt;
var localVideoElem = null, remoteVideoElem = null, localVideoStream = null,
    videoCallButton = null, endCallButton = null,
    peerConn = null, wsc = new WebSocket(&#039;ws://my-web-domain.de/websocket/&#039;),
    peerConnCfg = {&#039;iceServers&#039;: 
      [{&#039;url&#039;: &#039;stun:stun.services.mozilla.com&#039;}, {&#039;url&#039;: &#039;stun:stun.l.google.com:19302&#039;}]
    };
&lt;/pre&gt;

&lt;p&gt;The relevant variables are &lt;code&gt;wsc&lt;/code&gt;, representing a new &lt;code&gt;WebSocket&lt;/code&gt; connection (remember to replace &lt;code&gt;ws://my-web-domain.de/websocket/&lt;/code&gt; with your own URL) and &lt;code&gt;peerConnCfg&lt;/code&gt; which specify the configurations parameters used to initiate a new &lt;code&gt;RTCPeerConnection&lt;/code&gt;. We use Mozilla (and as a fallback Google) STUN services.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;localVideoElem&lt;/code&gt;, &lt;code&gt;remoteVideoElem&lt;/code&gt;, &lt;code&gt;videoCallButton&lt;/code&gt; and &lt;code&gt;endCallButton&lt;/code&gt; are used to get reference to HTML elements representing the local and remote &lt;code&gt;video&lt;/code&gt; containers (HTML5 &lt;code&gt;video&lt;/code&gt; elements) and the two buttons (HTML &lt;code&gt;input&lt;/code&gt; elements with &lt;code&gt;type=&quot;button&quot;&lt;/code&gt;) used to initiate and end a call. Last, the &lt;code&gt;localVideoStream&lt;/code&gt; will keep a reference to the local video stream, so we can close it (release the video and audio devices) when the call ends.&lt;/p&gt;

&lt;p&gt;Further, we define the &lt;code&gt;pageReady&lt;/code&gt; callback method assigned for the &lt;code&gt;load&lt;/code&gt; event:&lt;/p&gt;

&lt;pre&gt;
function pageReady() {
  videoCallButton = document.getElementById(&quot;videoCallButton&quot;);
  endCallButton = document.getElementById(&quot;endCallButton&quot;);
  localVideo = document.getElementById(&#039;localVideo&#039;);
  remoteVideo = document.getElementById(&#039;remoteVideo&#039;);
  // check browser WebRTC availability 
  &lt;strong&gt;if(navigator.getUserMedia)&lt;/strong&gt; {
    videoCallButton = document.getElementById(&quot;videoCallButton&quot;);
    endCallButton = document.getElementById(&quot;endCallButton&quot;);
    localVideo = document.getElementById(&#039;localVideo&#039;);
    remoteVideo = document.getElementById(&#039;remoteVideo&#039;);
    &lt;strong&gt;videoCallButton.removeAttribute(&quot;disabled&quot;);
    videoCallButton.addEventListener(&quot;click&quot;, initiateCall);
    endCallButton.addEventListener(&quot;click&quot;, function (evt) {
      wsc.send(JSON.stringify({&quot;closeConnection&quot;: true }));
    });&lt;/strong&gt;
  } else {
    alert(&quot;Sorry, your browser does not support WebRTC!&quot;)
  }
};
&lt;/pre&gt;

&lt;p&gt;Before taking any further actions, we need to check if the browser supports the required WebRTC features (avoid strange situations where nothing seems to work without an obvious reason). We do that by checking for the existence of the &lt;code&gt;getUserMedia&lt;/code&gt; method, in the &lt;code&gt;navigator&lt;/code&gt; global object. If no such method is found, the &quot;Video Call&quot; button remains disabled (no call can be initiated!) and we provide a warning/error message using &lt;code&gt;alert&lt;/code&gt;. If the WebRTC is supported, then we enable the &quot;Video Call&quot; button and assign a &lt;code&gt;click&lt;/code&gt; event listener to it, so the &lt;code&gt;initiateCall&lt;/code&gt; method is executed when the &quot;Video Call&quot; button is clicked. In the same way, a &lt;code&gt;click&lt;/code&gt; event listener is assigned to the &quot;Enc Call&quot; button (more details about this are discussed later on this tutorial).&lt;/p&gt;

&lt;p&gt;Next, we take care of the WebSocket message exchange between the caller and the callee peers:&lt;/p&gt;

&lt;pre&gt;
wsc.onmessage = function (evt) {
  var signal = JSON.parse(evt.data);
  if (!peerConn)
    answerCall();

  if (signal.sdp) {
    peerConn.setRemoteDescription(new RTCSessionDescription(signal.sdp));
  } else if (signal.candidate) {
    peerConn.addIceCandidate(new RTCIceCandidate(signal.candidate));
  } else if (signal.closeConnection){
      endCall();
  }
};
&lt;/pre&gt;

&lt;p&gt;A peer connection is created (and assigned to &lt;code&gt;peerConn&lt;/code&gt; variable) when the &quot;Video Call&quot; button is clicked. If no such (&lt;code&gt;RTCPeerConnection&lt;/code&gt;) object exists, it means that we deal with the callee case, so an incoming call, which in our simple application is automatically answered it by invoking the &lt;code&gt;answerCall&lt;/code&gt; method. In a more complex real world application, a ring audio signal may be used and the callee may answer the call by clicking an &quot;Answer Call&quot; button, but in our example we keep it simple, so the calls are automatically answered. Well, to be more exact, is more a semi-automatic answer, because the callee web browser asks about the permission to use the video and/or audio devices, so the human user can accept (or reject) these rights, in order to answer (or reject) the call.&lt;/p&gt;

&lt;p&gt;The two peers needs to exchange local and remote audio and video media information, such as resolution and codec capabilities. Signaling to exchange media configuration information is made by exchanging an offer and an answer using the Session Description Protocol (SDP).&lt;/p&gt;

&lt;h4&gt;Initiating a Call&lt;/h4&gt;

&lt;p&gt;Lets have now a look at the &lt;code&gt;initiateCall&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;
function initiateCall() {
  prepareCall();
  &lt;strong&gt;navigator.getUserMedia&lt;/strong&gt;({ &quot;audio&quot;: true, &quot;video&quot;: true }, function (stream) {
    localVideo.src = URL.createObjectURL(stream);
    peerConn.addStream(stream);
    &lt;strong&gt;createAndSendOffer();&lt;/strong&gt;
  }, function(error) { console.log(error);});
};
&lt;/pre&gt;

&lt;p&gt;First we make some initial preparations for the call (we explain more about this a bit later). Then, using &lt;code&gt;getUserMedia&lt;/code&gt; we obtain the local video stream and assign it to a &lt;code&gt;video&lt;/code&gt; element where we like to display it on our page (e.g., the &lt;code&gt;video&lt;/code&gt; element with id &lt;code&gt;localVideo&lt;/code&gt; in our case). Last we create and send a connection offer to the other peer, by invoking the &lt;code&gt;createAndSendOffer&lt;/code&gt; method, explained later in this tutorial.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;prepareCall&lt;/code&gt; method (see below), is responsible for creating the &lt;code&gt;RTCPeerConnection&lt;/code&gt; instance and assign the needed event listeners:&lt;/p&gt;

&lt;pre&gt;
function prepareCall() {
  peerConn = new RTCPeerConnection(peerConnCfg);
  peerConn.onicecandidate = onIceCandidateHandler;
  peerConn.onaddstream = onAddStreamHandler;
};

function onIceCandidateHandler(evt) {
  if (!evt || !evt.candidate) return;
  wsc.send(JSON.stringify({&quot;candidate&quot;: evt.candidate }));
};

function onAddStreamHandler(evt) {
  videoCallButton.setAttribute(&quot;disabled&quot;, true);
  endCallButton.removeAttribute(&quot;disabled&quot;); 
  remoteVideo.src = URL.createObjectURL(evt.stream);
};
&lt;/pre&gt;

&lt;p&gt;Any ICE candidate is forwarded to the signaling server for being sent to the other peer (see &lt;code&gt;onIceCandidateHandler&lt;/code&gt;) while when receiving a remote stream, we assign it to our &lt;code&gt;video&lt;/code&gt; element for being displayed (e.g., the &lt;code&gt;video&lt;/code&gt; element with id &lt;code&gt;remoteVideo&lt;/code&gt; in our case).&lt;/p&gt;

&lt;p&gt;TOne last step is required for the caller, that is to create a connection offer and send it to the other peer:&lt;/p&gt;

&lt;pre&gt;
function createAndSendOffer() {
  peerConn.createOffer(
    function (offer) {
      var off = new RTCSessionDescription(offer);
      &lt;strong&gt;peerConn.setLocalDescription&lt;/strong&gt;(new RTCSessionDescription(off), 
        function() {
          &lt;strong&gt;wsc.send(JSON.stringify({&quot;sdp&quot;: off }));&lt;/strong&gt;
        }, 
        function(error) { 
          console.log(error);
        }
      );
    }, 
    function (error) { 
      console.log(error);
    }
  );
};
&lt;/pre&gt;

&lt;p&gt;The offer contains information about how the two peers are about to be connected. The offer messages are forwarded by the signaling server to the other peer, which is being informed about this by using the &lt;code&gt;onmessage&lt;/code&gt; event listener, as described earlier on this tutorial.&lt;/p&gt;

&lt;h4&gt;Answering a Call&lt;/h4&gt;

&lt;p&gt;Similar with a call initiation, the RTCPeerConnection is created and the event listeners are assigned. Further, a local stream is obtained by using &lt;code&gt;getuserMedia&lt;/code&gt; and assigned to a &lt;code&gt;video&lt;/code&gt; element. Last an answer is created and sent, in response to the received offer:&lt;/p&gt;

&lt;pre&gt;
function answerCall() {
  prepareCall();
  // get the local stream, show it in the local video element and send it
  &lt;strong&gt;navigator.getUserMedia&lt;/strong&gt;({ &quot;audio&quot;: true, &quot;video&quot;: true }, function (stream) {
    localVideo.src = URL.createObjectURL(stream);
    peerConn.addStream(stream);
    &lt;strong&gt;createAndSendAnswer();&lt;/strong&gt;
  }, function(error) { console.log(error);});
};
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;createAndSendAnswer&lt;/code&gt; will prepare the answer and using the WebSocket channel will send it to the singnaling server, which then forwards it to the other peer, so the connection is completed:&lt;/p&gt;

&lt;pre&gt;
function createAndSendAnswer() {
  peerConn.createAnswer(
    function (answer) {
      var ans = new RTCSessionDescription(answer);
      &lt;strong&gt;peerConn.setLocalDescription&lt;/strong&gt;(ans, function() {
          &lt;strong&gt;wsc.send(JSON.stringify({&quot;sdp&quot;: ans }));&lt;/strong&gt;
        }, 
        function (error) { 
          console.log(error);
        }
      );
    },
    function (error) { 
      console.log(error);
    }
  );
}
&lt;/pre&gt;

&lt;h4&gt;Ending a Call&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; in theory, ending an WebRTC call may be slightly simpler: close the peer connection (i.e., calling &lt;code&gt;peerConn.close()&lt;/code&gt;) then use the callback method assigned to &lt;code&gt;peerConn.oniceconnectionstatechange&lt;/code&gt; and check if &lt;code&gt;peerConn.iceConnectionState === &quot;closed&quot;&lt;/code&gt;. However, we&#039;ve found two problems with this approach: 1) it does not seem to work (at least not all the times) with both, Google Chrome and Firefox, and 2) a &lt;code&gt;closed&lt;/code&gt; connection state may also occur when a temporarily break in the peer connection appears (bad internet connection, some big latencies, etc), which in many of the cases can be automatically restored (no need for additional code or management), so a &quot;call end&quot; may or may not be the exact situation. Because of this, we use the signaling server to notify the other peer about a &quot;real end call&quot; request.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;pageReady&lt;/code&gt; method (called when the HTML page is fully loaded), we&#039;ve added a &lt;code&gt;click&lt;/code&gt; event listener, where we send a &lt;code&gt;closeConnection&lt;/code&gt; signal to our signaling server, which forwards it to the other peers:&lt;/p&gt;

&lt;pre&gt;
function pageReady() {
  if(navigator.getUserMedia) {
    // ...some more code here...
    &lt;strong&gt;endCallButton.addEventListener(&quot;click&quot;, function (evt) {
      wsc.send(JSON.stringify({&quot;closeConnection&quot;: true }));
    });&lt;/strong&gt;
  } else {
    alert(&quot;Sorry, your browser does not support WebRTC!&quot;)
  }
};
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;endCall&lt;/code&gt; method has the following code:&lt;/p&gt;

&lt;pre&gt;
function endCall() {
  peerConn.close();
  localVideoStream.getTracks().forEach(function (track) {
    track.stop();
  });
  localVideo.src = &quot;&quot;;
  remoteVideo.src = &quot;&quot;;
  videoCallButton.removeAttribute(&quot;disabled&quot;);
  endCallButton.setAttribute(&quot;disabled&quot;, true);
};
&lt;/pre&gt;

&lt;p&gt;First step is to close the &lt;code&gt;RTCPeerConnection&lt;/code&gt; by calling the close method. Further, we stop all the (video) tracks and we reset the stream sources of the remote and local video, so nothing is displayed by the &lt;code&gt;video&lt;/code&gt; HTML5 elements (the last image frame remains visible if the source is not reset). Last, we take care to enable the &quot;Video Call&quot; button (allowing for a new call) and disable the &quot;End Call&quot; button.&lt;/p&gt;

&lt;h2&gt;Download the Code&lt;/h2&gt;

&lt;p&gt;The full client and server source code are available for download on &lt;a href=&quot;https://github.com/dimircea/WebRTC/tree/master/SimpleVideoChat&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Current Browsers Support&lt;/h2&gt;

&lt;p&gt;Not all the browsers support WebRTC. Mainly, one can use Google Chrome, Firefox and Opera. For iOS, &lt;a href=&quot;http://www.openwebrtc.org/bowser/&quot;&gt;Bowser&lt;/a&gt;, an Open Source web browser with WebRTC support, is available. Partial support is also available in EDGE web browser, and actually this technology is not supported at all by Safari. The complete list of WebRTC features supported by each web browser is available at &lt;a href=&quot;http://iswebrtcreadyyet.com/&quot;&gt;iswebrtcreadyyet.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; starting with 01.01.2016, using Google Chrome and Opera with WebRTC-based applications is possible only via a secure layer, thus HTTPS must be used instead HTTP!&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;section class=&quot;field field-name-field-category field-type-taxonomy-term-reference field-label-above view-mode-rss&quot;&gt;&lt;h2 class=&quot;field-label&quot;&gt;Category:&amp;nbsp;&lt;/h2&gt;&lt;ul class=&quot;field-items&quot;&gt;&lt;li class=&quot;field-item even&quot;&gt;&lt;a href=&quot;/taxonomy/term/42&quot; typeof=&quot;skos:Concept&quot; property=&quot;rdfs:label skos:prefLabel&quot; datatype=&quot;&quot;&gt;HTML5&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;field-item odd&quot;&gt;&lt;a href=&quot;/taxonomy/term/41&quot; typeof=&quot;skos:Concept&quot; property=&quot;rdfs:label skos:prefLabel&quot; datatype=&quot;&quot;&gt;WebRTC&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/section&gt;&lt;div class=&quot;easy_social_box clearfix horizontal easy_social_lang_und&quot;&gt;
            &lt;div class=&quot;easy_social-widget easy_social-widget-twitter first&quot;&gt;&lt;a href=&quot;http://twitter.com/share&quot; class=&quot;twitter-share-button&quot;
data-url=&quot;https://web-engineering.info/node/57&quot;
data-count=&quot;horizontal&quot;
data-lang = &quot;en&quot;
data-via=&quot;&quot;
data-related=&quot;:Check it out!&quot;
data-text=&quot;Building a Video/Audio Chat Web App with WebRTC&quot;&gt;Tweet&lt;/a&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-facebook&quot;&gt;&lt;fb:like href=&quot;https://web-engineering.info/node/57&quot; send=&quot;true&quot; layout=&quot;button_count&quot; width=&quot;88&quot; show_faces=&quot;true&quot; action=&quot;like&quot; colorscheme=&quot;light&quot; font=&quot;&quot;&gt;&lt;/fb:like&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-googleplus&quot;&gt;&lt;div class=&quot;g-plusone&quot; data-size=&quot;medium&quot; data-annotation=&quot;bubble&quot; data-href=&quot;https://web-engineering.info/node/57&quot;&gt;&lt;/div&gt;&lt;/div&gt;
          &lt;div class=&quot;easy_social-widget easy_social-widget-linkedin last&quot;&gt;&lt;script type=&quot;in/share&quot; data-url=&quot;https://web-engineering.info/node/57&quot; data-counter=&quot;right&quot;&gt;&lt;/script&gt;&lt;/div&gt;
  &lt;/div&gt; &lt;!-- /.easy_social_box --&gt;</description>
 <pubDate>Wed, 20 Jan 2016 13:48:19 +0000</pubDate>
 <dc:creator>mdiaconescu</dc:creator>
 <guid isPermaLink="false">57 at https://web-engineering.info</guid>
 <comments>https://web-engineering.info/node/57#comments</comments>
</item>
</channel>
</rss>
