Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of configuring iceServers to specify a turn server #14

Open
vincentfretin opened this issue Jun 13, 2021 · 2 comments
Open

Example of configuring iceServers to specify a turn server #14

vincentfretin opened this issue Jun 13, 2021 · 2 comments
Labels
documentation Improvements or additions to documentation

Comments

@vincentfretin
Copy link
Member

vincentfretin commented Jun 13, 2021

I have an elixir backend where I can retrieve the ice servers from a turn server provider or a self hosted coturn, it's a graphql query that return something like this:

           {
              "iceServers": {
                "credential": "TheCredential",
                "urls": [
                  "stun:{turn_host}",
                  "turn:{turn_host}:80?transport=udp",
                  "turn:{turn_host}:3478?transport=udp",
                  "turn:{turn_host}:80?transport=tcp",
                  "turn:{turn_host}:3478?transport=tcp",
                  "turns:{turn_host}:443?transport=tcp",
                  "turns:{turn_host}:5349?transport=tcp"
                ],
                "username": "TheUserName"
              }
            }
          }

I have a global setupPeerConnectionConfig function.

const setupPeerConnectionConfig = (adapter, iceServers) => {
  const qs = new URLSearchParams(location.search);
  const forceTurn = qs.get("force_turn");
  const forceTcp = qs.get("force_tcp");
  const peerConnectionConfig = {};

  if (iceServers && iceServers.urls !== null) {
    if (forceTcp) {
      iceServers.urls = iceServers.urls.filter((url) => url.startsWith("turn") && url.endsWith("tcp"));
    }
    const hasStunServer = !!iceServers.urls.find((url) => url.startsWith('stun'));
    const newIceServers = [];
    if (!forceTcp && !forceTurn && !hasStunServer) {
      newIceServers.push({ urls: "stun:stun1.l.google.com:19302" });
    }
    // remove __typename from the graphql query
    newIceServers.push({username: iceServers.username, credential: iceServers.credential, urls: iceServers.urls});

    peerConnectionConfig.iceServers = newIceServers;

    if (forceTurn || forceTcp) {
      peerConnectionConfig.iceTransportPolicy = "relay";
    }
  } else {
    peerConnectionConfig.iceServers = [
      { urls: "stun:stun1.l.google.com:19302" },
      { urls: "stun:stun2.l.google.com:19302" }
    ];
  }

  adapter.setPeerConnectionConfig(peerConnectionConfig);
};

To test that your config is right, you can force going through turn server (iceTransportPolicy = "relay")
by adding in the url ?force_turn=true. To force turn via tcp, use ?force_tcp=true. Look at about:webrtc in Firefox to see the selected candidate.

In your adapter-ready listener, call the setupPeerConnectionConfig with the retrieved iceServers.

const iceServers = await callToServerToGetIceServers();

scene.addEventListener("adapter-ready", ({ detail: adapter }) => {
  // don't use any await here, it won't be awaited before connecting
  setupPeerConnectionConfig(adapter, iceServers);
  // ... do your other things
});

scene.emit("connect");

Getting an iceServers config asynchronously and using connectOnLoad: true are incompatible, there is no await for adapter-ready listener in https://github.com/networked-aframe/networked-aframe/blob/41a6b484c8208ecb24e85855542a3fa40faf7bec/src/components/networked-scene.js#L33 so the user will connect before we get the config.

If your turn credential is temporary, you will need to call regularly

  const iceServers = await callToServerToGetIceServers();
  setupPeerConnectionConfig(NAF.connection.adapter, iceServers);

otherwise new peer connections will use expired turn credential and the connection won't succeed. You can use a setTimout similar to the JWT I explained here mozilla/janus-plugin-sfu#77 (comment)

I should probably push both the linked comment and this issue into a document that I push in this repo.

@vincentfretin
Copy link
Member Author

Putting it here if somebody wants to test, using coturn and janus on the same nginx on port 443 on a single server:
https://groups.google.com/g/meetecho-janus/c/lsN5va3RVtc/m/dQASri1QAgAJ
I'll probably test this way on my next project.

@vincentfretin
Copy link
Member Author

coturn has an official docker image https://github.com/coturn/coturn/tree/master/docker/coturn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

1 participant