Giter VIP home page Giter VIP logo

p2pcf's Introduction

image

P2PCF

P2PCF enables free (or cheap) serverless WebRTC signalling using a Cloudflare worker and a Cloudflare R2 bucket. The API is inspired by P2PT, but instead of using WebTorrent trackers, which may go down, a custom Cloudflare worker is provided whose level of I/O aims to be free for most use-cases, and otherwise very cheap.

The point is to allow people to deploy WebRTC-enabled applications without having to manage or worry (much) about a signalling server. Out of the box the library will "just work" using a free public worker (run by the author) that is subject to quota. However, setting up your own worker is easy and takes just a few minutes. Once it is deployed, signalling will work without any further maintenance.

P2PCF also has some additional features:

  • Room-based keying for easy connection management + acquisition
  • Minimal initial signalling (1 or 2 signalling messages) using the technique put together by @evan_brass
  • Subsequent signalling over DataChannels
  • Efficient chunking + delivery of DataChannel messages that exceed the ~16k limit
  • Peers handed back from the API are tiny-simple-peer instances which provides a simple API for interacting with the underlying PeerConnections (adding + removing media tracks, etc.)

Example + Usage

Out of the box, P2PCF will use a free public worker.

Once you're ready, you should set up your own worker on Cloudflare: https://github.com/gfodor/p2pcf/blob/master/INSTALL.md

A basic chat + video sharing example demonstrates the library at https://gfodor.github.io/p2pcf-demo (source)

Basic usage:

import P2PCF from 'p2pcf'

const client_id = 'MyUsername'
const room_id = 'MyRoom'

const p2pcf = new P2PCF(client_id, room_id, {
  // Worker URL (optional) - if left out, will use a public worker
  workerUrl: '<your worker url>',

  // STUN ICE servers (optional)
  // If left out, will use public STUN from Google + Twilio
  stunIceServers: { ... },
  
  // TURN ICE servers (optional)
  // If left out, will use openrelay public TURN servers from metered.ca
  turnIceServers: { ... },
  
  // Network change poll interval (milliseconds, optional, default: 15000, 15 seconds)
  // Interval to poll STUN for network changes + reconnect
  networkChangePollIntervalMs: ...,
  
  // State expiration interval (milliseconds, optional, default: 120000, 2 minutes)
  // Timeout interval for peers during polling
  stateExpirationIntervalMs: ...,
  
  // State heartbeat interval (milliseconds, optional, default: 30000, 30 seconds)
  // Time before expiration to heartbeat
  stateHeartbeatWindowMs: ...,
  
  // Fast polling duration (milliseconds, optional, default: 10000, 10 seconds)
  // How long we run fast polling after a state transition
  fastPollingDurationMs: ...,

  // Fast polling rate (milliseconds, optional, default: 1500)
  // Polling rate during state transitions
  fastPollingRateMs: ...,

  // Slow polling rate (milliseconds, optional, default: 5000, 1.5 seconds)
  // Polling rate when there has been no recent activity
  slowPollingRateMs: ...,

  // Idle polling delay (milliseconds, optional, default: never)
  // How long to wait for activity before switching to idle polling rate
  idlePollingAfterMs: ...,

  // Idle polling rate (milliseconds, optional, default: Infinity)
  // Polling rate when there has been no activity for idlePollingAfterMs milliseconds
  // Infinity will cause polling to stop, which is useful for idle clients left open.
  idlePollingAfterMs: ...,

  // Options to pass to RTCPeerConnection constructor (optional)
  rtcPeerConnectionOptions: {},

  // Proprietary constraints to pass to RTCPeerConnection constructor (optional)
  rtcPeerConnectionProprietaryConstraints: {},

  // SDP transform function (optional)
  sdpTransform: sdp => sdp
});

// Start polling
p2pcf.start()

p2pcf.on('peerconnect', peer => {
  // New peer connected
  
  // Peer is an instance of simple-peer (https://github.com/feross/simple-peer)
  //
  // The peer has two custom fields:
  // - id (a per session unique id)
  // - client_id (which was passed to their P2PCF constructor)
  
  console.log("New peer:", peer.id, peer.client_id)
  
  peer.on('track', (track, stream) => {
    // New media track + stream from peer
  })
  
  // Add a media stream to the peer to start sending it
  peer.addStream(new MediaStream(...))
})

p2pcf.on('peerclose', peer => {
  // Peer has disconnected
})

p2pcf.on('msg', (peer, data) => {
  // Received data from peer (data is an ArrayBuffer)
})

// Broadcast a message via data channel to all peers
p2pcf.broadcast(new ArrayBuffer(...))

// To send a message via data channel to just one peer:
p2pcf.send(peer, new ArrayBuffer(...))

// To stop polling + shut down (not necessary to call this typically, page transition suffices.)
p2pcf.destroy()

stunIceServers and turnIceServers are optional, but if provided, should be in the format of the iceServers option passed to RTCPeerConnection.

When a new peer joins, it can take up to slowPollingRateMs before negotiation will begin. If you want peers to connect more quickly, you can adjust slowPollingRateMs but it will result in increased worker requests and R2 reads.

Note that peers who are both on symmetric NATs (or one symmetric NAT + one port restricted NAT) will use TURN. If you do not specify a TURN server then the TURN server provided by Open Relay will be used. It's estimated 8% of visitors require TURN.

Estimated Cloudflare Usage

The worker provides signalling via HTTP polling (with backoff when the room is idle), and each request to the server performs a small number of reads from R2. Each join of a peer to the room will do at least 1 write to R2 and up to N + 1 writes (one for each peer, and a metadata update) in the worst-case where all peers are behind symmetric NATs and need to perform bi-directional hole punching to establish their initial DataChannel. Subsequent renegoations are performed over the DataChannel and so do not incur any R2 writes. Clients also heartbeat to maintain livliness every 90 seconds, which incurs an additional write each time.

R2's free tier offers 1M writes per month and 10M reads per month. Cloudflare workers offer ~3M free requests per month. In general, these free tiers should support any modest WebRTC application's signalling needs without the need to rely upon public signalling servers.

p2pcf's People

Contributors

bellisario avatar gfodor avatar thaunknown avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

p2pcf's Issues

Enhancement: Types

The project looks neat. Have you considered adding types and turning this into a Typescript project instead?

perf: migrate from simple-peer to polite-peer

Please consider migrating from simple-peer to polite-peer: https://github.com/jimmywarting/jimmy.warting.se/blob/master/packages/peer/perfect-negotiation.js [there isn't an NPM package for this but I could make one]

Why this matters:
simple-peer relies on stream which in turn relies on buffer, and process which relies on setTimeout. This is an important issue because those require to be polyfilled, and those polyfills are VERY slow. Since WebTorrent was mentioned, from my testing more than half of the app's CPU time was consumed by node's stream and buffer or actually it's polyfills. Another massive issue is the reliance on the slow process polyfill of most bundlers which uses setTimeouts for delying tasks instead of promises or microtask.

Problems:
polite-peer is a much simper package than simple-peer [oh the irony] and as such doesn't expose that many wrappers so it might require extra work. Another issue might be the immaturity of the package as very little people actually used it [not to be confused with it wasn't used a lot, because it was used in public projects a lot] so unexpected issues might arise.

bug: cf worker error 1101 causing JSON.parse error

Error 1101 Ray ID: 73427f0fcf93b363 • 2022-08-01 23:53:59 UTC
Worker threw exception
What happened?
You've requested a page on a website (p2pcf.minddrop.workers.dev) that is on the Cloudflare network. An unknown error occurred while rendering the page.

What can I do?
If you are the owner of this website:
you should login to Cloudflare and check the error logs for p2pcf.minddrop.workers.dev.

POST https://p2pcf.minddrop.workers.dev/ 500

causes:

SyntaxError: Unexpected token < in JSON at position 0

at:

const res = await fetch(this.workerUrl, {

Using KV instead of R2

I can't get it. Why would you use R2 while it's in beta and have stricter limits than KV. It's also a overkill for storing a bunch of JSON

More than two STUN/TURN

Everything work's perfect on the same wifi/network but when i try to connect peers from other network i get this warning

WebRTC: Using more than two STUN/TURN servers slows down discovery

then peer automatically disconnect with error and nothing works.

bug: cf worker shouldn't disconnect peers

the CF worker really shouldn't disconnect peers, this should happen between the peers themselves

the peer list often takes time to propagate tru cloudflare, or sometimes doesn't at all, leading to people randomly disconnecting with eachother

Listen for a stream removed

HI, I can see according to the examples that the "addStream" method works when you are starting your camera and this event is listened to thanks the "track" event like : "p2pcf.on('track', ..etc)" . My question is what is the event or how can i handle the event when a user turns off the camera. I have tried with the "removeStream" method but it's not working. Maybe i missing something. Thank you so much

bug: peerclose fires without emitting peerconnect

If a peer fails to connect [not sure why that happens yet], it will emit close, without ever emitting peerconnect

This case needs to be considered

code:

const map = {}
p2pcf.on('peerconnect', peer => {
  map[peer.id] = true
})
p2pcf.on('peerclose', peer => {
  if (map[peer.id]) {
    // handle
  } else {
    // this should never occur but does
  }
})

should there be a separate event for failed to init peer?

Why can't you send string using p2pcf.send

Just trying to learn, Is there a reason the send method only supports ArrayBuffer. I know I can convert string to ArrayBuffer with TextEncoder and vice versa but it's additional steps since I only need to send simple two characters messages. Is it more performant than sending strings directly? My use case is sending simple two character string with lowest latency possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.