10.1 Simple Peer-to-peer Example

When two peers decide they are going to set up a connection to each other, they both go through these steps. The STUN/TURN server configuration describes a server they can use to get things like their public IP address or to set up NAT traversal. They also have to send data for the signaling channel to each other using the same out-of-band mechanism they used to establish that they were going to communicate in the first place.

Example 9

  1. const signaling = new SignalingChannel(); // handles JSON.stringify/parse
  2. const constraints = {audio: true, video: true};
  3. const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
  4. const pc = new RTCPeerConnection(configuration);
  5. // send any ice candidates to the other peer
  6. pc.onicecandidate = ({candidate}) => signaling.send({candidate});
  7. // let the "negotiationneeded" event trigger offer generation
  8. pc.onnegotiationneeded = async () => {
  9. try {
  10. await pc.setLocalDescription();
  11. // send the offer to the other peer
  12. signaling.send({description: pc.localDescription});
  13. } catch (err) {
  14. console.error(err);
  15. }
  16. };
  17. pc.ontrack = ({track, streams}) => {
  18. // once media for a remote track arrives, show it in the remote video element
  19. track.onunmute = () => {
  20. // don't set srcObject again if it is already set.
  21. if (remoteView.srcObject) return;
  22. remoteView.srcObject = streams[0];
  23. };
  24. };
  25. // call start() to initiate
  26. function start() {
  27. addCameraMic();
  28. }
  29. // add camera and microphone to connection
  30. async function addCameraMic() {
  31. try {
  32. // get a local stream, show it in a self-view and add it to be sent
  33. const stream = await navigator.mediaDevices.getUserMedia(constraints);
  34. for (const track of stream.getTracks()) {
  35. pc.addTrack(track, stream);
  36. }
  37. selfView.srcObject = stream;
  38. } catch (err) {
  39. console.error(err);
  40. }
  41. }
  42. signaling.onmessage = async ({data: {description, candidate}}) => {
  43. try {
  44. if (description) {
  45. await pc.setRemoteDescription(description);
  46. // if we got an offer, we need to reply with an answer
  47. if (description.type == 'offer') {
  48. if (!selfView.srcObject) {
  49. // blocks negotiation on permission (not recommended in production code)
  50. await addCameraMic();
  51. }
  52. await pc.setLocalDescription();
  53. signaling.send({description: pc.localDescription});
  54. }
  55. } else if (candidate) {
  56. await pc.addIceCandidate(candidate);
  57. }
  58. } catch (err) {
  59. console.error(err);
  60. }
  61. };