10.2 Advanced Peer-to-peer Example with Warm-up

When two peers decide they are going to set up a connection to each other and want to have the ICE, DTLS, and media connections “warmed up” such that they are ready to send and receive media immediately, they both go through these steps.

Example 10

  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. let pc;
  5. let audio;
  6. let video;
  7. let started = false;
  8. // Call warmup() before media is ready, to warm-up ICE, DTLS, and media.
  9. async function warmup(isAnswerer) {
  10. pc = new RTCPeerConnection(configuration);
  11. if (!isAnswerer) {
  12. audio = pc.addTransceiver('audio');
  13. video = pc.addTransceiver('video');
  14. }
  15. // send any ice candidates to the other peer
  16. pc.onicecandidate = ({candidate}) => signaling.send({candidate});
  17. // let the "negotiationneeded" event trigger offer generation
  18. pc.onnegotiationneeded = async () => {
  19. try {
  20. await pc.setLocalDescription();
  21. // send the offer to the other peer
  22. signaling.send({description: pc.localDescription});
  23. } catch (err) {
  24. console.error(err);
  25. }
  26. };
  27. pc.ontrack = async ({track, transceiver}) => {
  28. try {
  29. // once media for the remote track arrives, show it in the video element
  30. event.track.onunmute = () => {
  31. // don't set srcObject again if it is already set.
  32. if (!remoteView.srcObject) {
  33. remoteView.srcObject = new MediaStream();
  34. }
  35. remoteView.srcObject.addTrack(track);
  36. }
  37. if (isAnswerer) {
  38. if (track.kind == 'audio') {
  39. audio = transceiver;
  40. } else if (track.kind == 'video') {
  41. video = transceiver;
  42. }
  43. if (started) await addCameraMicWarmedUp();
  44. }
  45. } catch (err) {
  46. console.error(err);
  47. }
  48. };
  49. try {
  50. // get a local stream, show it in a self-view and add it to be sent
  51. selfView.srcObject = await navigator.mediaDevices.getUserMedia(constraints);
  52. if (started) await addCameraMicWarmedUp();
  53. } catch (err) {
  54. console.error(err);
  55. }
  56. }
  57. // call start() after warmup() to begin transmitting media from both ends
  58. function start() {
  59. signaling.send({start: true});
  60. signaling.onmessage({data: {start: true}});
  61. }
  62. // add camera and microphone to already warmed-up connection
  63. async function addCameraMicWarmedUp() {
  64. const stream = selfView.srcObject;
  65. if (audio && video && stream) {
  66. await Promise.all([
  67. audio.sender.replaceTrack(stream.getAudioTracks()[0]),
  68. video.sender.replaceTrack(stream.getVideoTracks()[0]),
  69. ]);
  70. }
  71. }
  72. signaling.onmessage = async ({data: {start, description, candidate}}) => {
  73. if (!pc) warmup(true);
  74. try {
  75. if (start) {
  76. started = true;
  77. await addCameraMicWarmedUp();
  78. } else if (description) {
  79. await pc.setRemoteDescription(description);
  80. // if we got an offer, we need to reply with an answer
  81. if (description.type == 'offer') {
  82. await pc.setLocalDescription();
  83. signaling.send({description: pc.localDescription});
  84. }
  85. } else {
  86. await pc.addIceCandidate(candidate);
  87. }
  88. } catch (err) {
  89. console.error(err);
  90. }
  91. };