Skip to main content
Markdown

Calls, group calls and the call screen

  • Calls between two people ring in every tab the callee has open, and stop ringing everywhere once one answers. They run directly between the two browsers, through TURN when a network needs it, or move into a room on WordPlus Cloud's media servers when nothing else works.
  • Group calls are rooms on WordPlus Cloud's media servers: 32 people with video, 50 audio only.
  • The call screen is a drop-in interface for both: call buttons, the ring and a call window, with no call code.

In this version, calls take integer user ids, as the realtime server's relay does. A user whose id is a string (a UUID) is refused calls with no_user; channels and presence take any id.

Who may call​

Your server owns call and room names by their start, as it owns channels:

realtime.calls( 'support-', async ( user, call, withUser ) => {
if ( withUser === null ) return isStaff( user ) ? { admin: true } : false; // a room: the staff only
return isStaff( user ) || isStaff( withUser ); // a call: with an agent only
} );
  • withUser ($with, with_user) is the other person of a call between two (whom user calls, or who calls user), or null for a room.
  • Answer false for no, true for yes, or an object: info (what the others see of user: your profile's name and avatar by default), and for a room publish and subscribe (true unless said), admin and hidden (false unless said).
  • Your server asks again when the callee answers, with the callee as user.

Calling​

const calls = await rt.calls();          // loads the call engine the first time

const call = await calls.start( 'support-42', { to: 34, type: 'video' } );
call.on( 'state', ( state ) => show( state ) ); // ringing, connecting, connected, reconnecting, ended
call.on( 'remote', ( remote ) => remote.camera?.attach( video ) );
await call.setTrack( 'camera', cameraTrack );
await call.setTrack( 'microphone', micTrack );
call.end();

calls.on( 'incoming', async ( incoming ) => {
// incoming.from, incoming.info ( { name, avatar } ), incoming.type
const call = await incoming.accept(); // or incoming.decline( 'busy' )
} );
  • A page holds its user's line, where their calls ring, unless connect() was given ring: false.
  • A call rings for 45 seconds, then ends with no_answer.
  • Each side's connection may drop for 45 seconds during a call and come back.

Group calls​

const room = await calls.room( 'support-staff', { type: 'video' } );
room.on( 'person', ( person ) => person.camera?.attach( tileFor( person ) ) ); // { user, info, camera, microphone, screen, speaking }
room.on( 'person_left', ( person ) => removeTile( person ) );
await room.setTrack( 'camera', cameraTrack );
room.leave();

Rooms need the room client served by your app (rooms: '/realtime-rooms.min.js' in connect(); npx wordplus-realtime copy public/ puts it there).

The call screen​

await rt.callScreen();   // the call window and the ring, on this page
<wordplus-call to="34" call="support-42" type="video" name="Ann" avatar="https://…/ann.jpg">Call Ann</wordplus-call>
<wordplus-room room="support-staff" type="video" title="Staff room">Join the staff room</wordplus-room>
  • A call rings only on pages that start the screen (or your own calls code): start it on every page a person may be called on.
  • Its words: rt.callScreen( { strings: { … } } ), every key of the screen's strings, in your page's language.
  • Its colours are CSS custom properties a page sets; it lives in a shadow root, so your styles don't reach in.
  • In React: <CallScreen />, <CallButton> and <RoomButton> (React).

Why a call ended​

call.endReason
hangupa side hung up
cancelledthe caller hung up before an answer
rejected, busythe callee declined, or said they were busy
no_answernobody answered within 45 seconds
page_closeda side closed its page
peer_losta side's connection didn't come back within 45 seconds
room_lostthe room it moved to gave up
not_included, forbidden, bad_token, expiredthe server refused to start it (errors)
no_call, call_endedanswered after the caller had given up
signaling_unavailablethe page's connection was down when it started

What WordPlus Cloud sees​

  • Your server decides every call: WordPlus Cloud takes only tokens it signed, for the people and the call it named.
  • The two browsers' negotiation is sealed with a key your server derives from its local key, which WordPlus Cloud never sees.
  • The media is encrypted end to end by WebRTC on a direct or relayed path. In a room, the media server forwards it.