WebRTC signaling

There is no standardized signaling protocol for WebRTC applications. For this reason,  the UV4L Streaming Server attempts to support or leverage a variety of them so that they can be used in different scenarios:

  1. built-in, “proprietary” message passing over websocket;
  2. Janus, a general purpose WebRTC Gateway;
  3. PeerJS (work-in-progress);
  4. XMPP Jingle (experimental, out of the scope of this doc.)
Built-in signaling

This signaling protocol is available with the UV4L Streaming Server module and allows a peer (e.g. a browser) to initiate a bidirectional, p2p audio-video-data call session with an uv4l instance. This protocol is based on message passing over websocket, which must stay open for the whole duration of the session. Messages are in JSON format and always include a mandatory what field identifying a request (from the initiator to uv4l) or an answer or message (from uv4l to the initiator), a data field specifying the content of the request or the answer, and may include an options field if a request supports it (e.g. a call request).

  • call request. This is the very first message in a session that the initiator has to send to uv4l to start a call:
   what: "call",
   options: {
      force_hw_vcodec: true,
      vformat: 30,
      trickle_ice: true

At the moment of writing, options may only include whether uv4l has to use the hardware encoder and decoder (if supported by the platform on which uv4l is running), what resolution and frame rate shall be used in this case and if trickle ICE optimization has to be used or not (see also the iceCandidate message below). Note that all other options affecting a WebRTC session can be modified, before it begins, through the RESTful API.

  • offer answer. After the call request has been sent, uv4l answers with an offer containing its SDP (the string reported as <uv4l-sdp> for simplicity below) :
       what: "offer",
       data: "<uv4l-sdp>"
  • generateIceCandidates request. Deprecated as implicit in the call request, so this is no longer needed and does nothing at the moment. (Old behaviour: after receiving the SDP from uv4l, the initiator had to ask uv4l to start to generate its own list of ICE candidates):
       what: "generateIceCandidates"
  • answer request. The initiator can in turn provide its local SDP with the following message:
       what: "answer",
       data: "<local-sdp>"
  • iceCandidates message. When trickle ICE is not used, after uv4l has automatically gathered all the ICE candidates, they are passed to the initiator all together as an array. As collecting all the candidates beforehand might require several seconds depending on the network environment, it is suggested to turn on and use the trickle_ice optimization (see the call and iceCandidate requests):
       what: "iceCandidates",
       data: <array-of-uv4l-ice-candidates>
  • iceCandidate message. When trickle ICE is used, whenever a new ICE candidate becomes available, uv4l immediately sends it to the initiator (an empty string in data means that the gathering has completed):
       what: "iceCandidate",
       data: <single-uv4l-ice-candidate or empty string>
  • addIceCandidate request. The initiator tells uv4l to add the one local ICE Candidate specified. This should be typically sent for each local ICE Candidate being generated by the initiator:
       what: "addIceCandidate",
       data: "<local-ice-candidate>"
  • message message. Messages from uv4l during the session (typically errors), before possibly unilaterally closing the socket, come in the following form:

       what: "message",
       data: "<error-message>"
  • hangup request. This can be sent by initiator to signal that the call has terminated. Alternatively, to hangup the initiator can just close the websocket:
   what: "hangup"

As you can see the protocol is reasonably simple and can be easily adapted or wrapped into more sophisticated signaling protocols for interoperability with third-party infrastructures.

An implementation of the above protocol in Javascript can be found in the source code of this simple web application.

Janus WebRTC Gateway

At the moment of writing, the UV4L Streaming Server supports the videoroom plugin:

This is a plugin implementing a videoconferencing SFU (Selective Forwarding Unit) for Janus, that is an audio/video/data router. This means that the plugin implements a virtual conferencing room peers can join and leave at any time. This room is based on a Publish/Subscribe pattern. Each peer can publish his/her own live audio/video/data feeds: this feed becomes an available stream in the room the other participants can attach to. This means that this plugin allows the realization of several different scenarios, ranging from a simple webinar (one speaker, several listeners) to a fully meshed video conference (each peer sending and receiving to and from all the others).

UV4L natively implements the underlying signaling protocol required to interact with a given videoroom hosted by a Janus Gateway and provides two alternative interfaces to do this via HTTP(S).

One is “action-based”, meaning that you essentially invoke a GET request towards the UV4L Streaming Server specifying, in the parameters of the request, whether you want to start a new session or stop the one in progress. Other parameters indicate the gateway URL, the room number, the username to be shown to others in the room, and some other optional things such as whether you want to be a publisher only, a subscriber or both. For more detailed informations with an example, please read this.

The second interface is based on ad-hoc RESTful API. It gives a more fine-grained control over the whole session and can be used to develop more sophisticated and interactive web applications. The documentation can be found here.

Note that the two interfaces described above are mutually exclusive, thus they cannot be used at the same time to control the same session.