@sctlib/rtc

0.0.16 • Public • Published

@sctlib/rtc

Prototype web-components to establish webrtc peer connections, without the need of a predefined signaling server.

There are different signaling methods, some like matrix will call a matrix server.

Usage in code

To connect with peers using WebRTC, visit the website. From the interface, select a "signaling method" and follow the instructions, to connect with an other peer (a friend, or yourself in the same browser tab, an other tab, an other brwoser, an other device, from an other network etc. )

This project is available as a web page, and a javascript/html/css/web-component npm package.

  1. npm i @sctlib/rtc
  2. check this project index.html file to see how to init the components, it is this demo page.

CDN and DOM usage

The web-components definitions can be imported from a CDN, and inserted in the HTML DOM with:

<script type="module" src="https://cdn.jsdelivr.net/npm/@sctlib/rtc"></script>
<rtc-user></rtc-user>

About the project and manual signaling

To establish a webrtc connection between two peers (A, instanciating the connection, and B with whom we are trying to connect), data has two be exchanged between them, this is the signaling part of the connection.

  1. peer A creates a RTCPeerConnection, and an offer
  2. peer A signals the offer to peer B
  3. peer B receives/read the offer, creates its own RTCPeerconnection, and creates and answer
  4. peer B signals the answer to peer A
  5. peer A receive/reads the answer, adds it to its RTCPeerconnection
  6. a webrtc connection between peer A and peer B is opened

To signal the offer and answer between peer A and B, we could use a server (which would transmit the data for us, the "signaling" part of a WebRTC connection, before the RTCConnection is established), but we want to be able to to that without relying on a specific server somewhere (which might be offline, unavailable, or we just want to be independant, decentralized, autonomous, p2p...).

For this reason we want to "manually signal" the data (see peer data section) ourselves in different ways. These signaling forms can be composed to establish a connection, so each independant part could be transmitted with different method (as long as peer A and B get what they need, in the correct order).

copy/paste & third party signaling server

  • peer A copy/paste the offer data, singaling it to peer B over a third party signaling server
  • peer B copy/paste the offer from peer A, into the app to generate the peer anwser
  • peer B copy/paste the answer and signals it to peer A over a third party signaling server
  • peer A copy/paste the answer into the app, to finish the connection

Third party manual signaling servers could be:

  • matrix, signal, whatsapp, telegram, instagram, steam, in app chat etc.
  • bookmark synchronisation service, to share the data as URL between devices
  • (push) notifications, such as ntfy; (problem is, there are rate limit for non paying user, so rolled out currently).

qr-code(s)

For devices which support displaying QR codes, and/or reading them, it is possible to generate a QR-code representation of all peer data that needs to be signaled.

A device can create peer offerbundle, and display it as a qr-code. It can also display it "unbundled", with multiple qr-codes representing the same data.

A device can also read the qr-code of peer data (bundled or not), in one qr-code, or multiple qr-codes displayed sequencecially. This feature is possible thanks to the barcode-detector-polyfill and zbar-wasm qr-code scanning modules (not bundled into the app, but served from a CDN because of their licences).

window.postMessage()

  • when receiving an offer, we can signal the peer contained in the ?data query parameter, to our local peer, so it creates an answer (to be signaled back to the instanciating peer)
  • when receiving an answer, we can also signal the incoming peer data contained in the ?data query param, to our local peer (so it can finish opening the connection)

Peer data (being signaled)

The peer(ing) data, is the data sent between peers to instanciate a webrtc peer connection. It is composed of:

  • a webrtc peer offer or answer, (a javascript object) description of the peer with RTC capabilities
  • a list of ICECandidates, the possibe routes to our peer on the network

For signaling purpose, this data is transformed as a string of text, so it can:

  • fit into the URL/URI scheme (here as a ?data= query parameter), and not be too long (gitlab pages only support ±1048 chars urls; we moved the app to cloudflare pages).
  • fit into QR-codes and their maximum size
  • be accessible, received and read by various (people and) devices, with multiple form factors and capabilities

researching how to best encode, compress, transmit, sign, the data in these "manual ways" so it is relyiable.

  • right it uses lz-strings to compress and encode the data for the URL.
  • used to be using base64 (atob and btoa, with JSON.parse() and JSON.stringify())
  • could research protobuff, brocli, or UDP/TCP (or other data transmition protocol), to experiment with multi QR-code send/receive (device facing device, both doing both actions).

The data can also be signaled:

  • "bundled", an object with offer/answer + candidates, (answerbundle and offerbundle)
  • "unbundled", several objects, offer/answer, candidates, type

Development

To use the code locally, or in other applications.

Local server

Run dev server with:

npm install
npm run dev

Web components

There is currently only one "public" web component though it is composed of a few others, that are undocumented, but should all work independantly.

A few HTML attributes allow to customize the experience of the rtc user.

Some are set in the DOM by the user, some other are set by the app, as reflection of its current state, and can be used to style the interface (but not for changing the state).

rtc-user

When inserted in the DOM, <rtc-user></rtc-user> will create an interface to allow two peers to connect, with various signaling methods.

Attributes

  • user-name = String, only a display for the local user peer's
  • signaling-methods = JSON.stringify(Array[signaling-methods]), a list of allowed signaling methods IDs.
  • matrix-peers = JSON.stringify(Array[matrix_user_id]), is a list of Matrix.org user IDs that are allowed for incoming RTC offers (and preffiled for outgoing RTC offer/answers)
  • search-params = JSON.stringify(Array[<rtc-user ATTRIBUTE/>]) than can be preffiled from the current web page's URL search params (hash params too?)

Examples:

<rtc-user user-name="peer-a" signaling-methods='["copypaste"]'></rtc-user>

<rtc-user user-name="peer-b" search-params='["matrix-peers"]'></rtc-user>

Events

Currently only two events are sent outside.

const $user = document.querySelector('rtc-user')
$user.addEventListener('dataChannel', (event)=> {
	const {detail} = event
	console.log('Peer connected, received data channel', event)
	console.log('Peer connection data channel', detail)
})
$user.addEventListener('channelMessage', (event)=> {
	const {detail} = event
	console.log(Peer received message from data channel, event)
	console.log(Peer message , detail)
})

Methods

When a data channel is open, there are several methods that can be used on the rtc-user:

  • send(data) accepts data to be sen through the data channel. As a convenience, if it typeof data === "object", JSON.stringify(data) will be applied, so the data can be send as a Javascript string.

Slots

The rtc-user has two slots, that can be used to render HTML elements as it's children; logs and send:

<rtc-user>
	<output slot="logs"></output>
	<form slot="send">
		<fieldset>
			<legend>Send data (custom send)</legend>
			<input name="text" />
			<button type="submit">Send message</button>
		</fieldset>
	</form>
</rtc-user>

The slots, do not handle automatically events (as we might expect; for this we'd have to clearely define the events/methods api for inserted elements into the slots; so the rtc-user can communicate with them directly).

To handle the slots, such as what's done with their default slot value (a textarea to send data, and a list of the messages as logs):

// the HTML elements we need for displaying rtc events
const $user = document.querySelector("rtc-user");
const $userLog = $user.querySelector('[slot="logs"]');
const $userSend = $user.querySelector('[slot="send"]');

// output the opened data channel (in/out) to the logs
$user.addEventListener("dataChannel", (event) => {
	if ($userLog) {
		const $log = document.createElement("p");
		$log.innerText = event.detail.type;
		$userLog.append($log);
	}
});

// listen to rtc data channel messages in; append all as logs
$user.addEventListener("channelMessage", (event) => {
	console.log("outside user receive channel message", event);
	if ($userLog) {
		const $log = document.createElement("p");
		$log.innerText = event.detail;
		$userLog.append($log);
	}
});
// listen to the form "submit" event, send content through rtc data channel out
if ($userSend) {
	$userSend.addEventListener("submit", (event) => {
		event.preventDefault();
		const formData = new FormData(event.target);
		const formDataObject = Object.fromEntries(formData);
		$user.send({
			detail: formDataObject,
		});
	});
}

Connection scenarios

This project is an experiment in establishing a web RTCPeerConnection, between two peers in their browsers, without the need of a predefined server to establing signaling

  • Peer sends a connection offer to Peer B
  • Peer B receives and uses the offer sent by Peer A, to create an answer, and sends it to Peer A
  • Peer A receives the answer, and associate it to its peer.
  • The RTCPeerConnection succeeds (or fails), the two peers are now connected for real time communication, without intermediary server.

To get started, send an offer to a friend, and wait for them to send you an answer back.

As a first test, try the "copy/paste" signaling method. It is also possible to try signaling with QR-code(s). Open the developer console for debugging information.

We're using a bunch of rtc components to connect peers with webrtc, without the need of servers.

There are different scenarios we're trying to cover.

  1. 1 peer alone -> no data channel
  2. 2 peers, on the same browser, in 1 page
  3. 2 peers, on the same browser, in 1 page, in 2 iframes
  4. 2 peers, on the same browser, in 2 pages (tabs which are publich; not private browsing)
  5. 2 peers, on the same browser, 1 public page, and 1 in private browsing page
  6. 2 peers, on different browsers (private or public pages), on the same computer
  7. 2 peers, on different browsers (private or public pages), on different computers, on the same network
  8. 2 peers, on different browsers (private or public tabs), on different computers, on different networks (behind NAT/ROUTER)
  9. 2 peers, different computer/browser/network, through private VPN
  10. X peers, <- start again from 1.

It would be nice to have most of them working without the need of any server (signaling/TURN; STUN, since free are okay)?

Status:

  • done: 1, 2, 3, 4
  • doing: 5, 6, 7, (8 & 9)
  • after: 10

(a)GPLv3 https://www.gnu.org/licenses/gpl-3.0.en.html

Dependencies (5)

Dev Dependencies (2)

Package Sidebar

Install

npm i @sctlib/rtc

Weekly Downloads

0

Version

0.0.16

License

GPL-3.0-or-later

Unpacked Size

271 kB

Total Files

6

Last publish

Collaborators

  • user-09876