Glimesh / broadcast-box

A broadcast, in a box.
MIT License
689 stars 58 forks source link

Cannot get STUN/TURN function working #27

Closed LanTenDragon closed 1 year ago

LanTenDragon commented 1 year ago

I saw this comment, and tried to replicate it, but without any success.

What I have tried:

  1. Grab the main branch, run broadcast-box on localhost:8080, and added TURN severs according to the linked comment.
  2. Setup a VM on Google Cloud, opening all ports in both directions
  3. Setup a reverse proxy from desktop port 8080 to GCP VM port 58080 using ssh ssh -R 58080:127.0.0.1:8080 foo@bar.com
  4. We can now access the stream via http://bar.com:58080/yourStreamkey (no https, it should not matter, right?)
  5. The HTML and JS is rendered correctly, so the proxy is working, but the actual stream is not loaded.

Scenario 1

To simulate a scenario where both the broadcast and the viewer are behind symmetric NAT, and to prevent LAN candidate pairs being picked, I performed the steps above with one extra change, setting ICETransportPolicy: webrtc.ICETransportPolicyRelay in webrtc.Config. Visit http://bar.com:58080/yourStreamkey in Chrome on the same PC as broadcast-box is running or Chrome on a laptop in the same LAN. The stream cannot be loaded. Looking at chrome://webrtc-internals, I get a red box with connectionstatechange: failed. candidateType=relay entries are present in both local and remote stats table

chrome_qQbXzMa7xI

Scenario 2

For a more realistic scenario, I used 4G LTE mobile data and Chrome on Android to view the broadcast. I did steps 1-5, this time not touching ICETransportPolicy. Looking at chrome://webrtc-internals on android, I get the same red box with connectionstatechange: failed. Similarly, candidateType=relay entries are present in both local and remote stats table.

Both scenarios have been repeated with two different TURN servers

peerConnection, err := api.NewPeerConnection(webrtc.Configuration{
    // ICETransportPolicy: webrtc.ICETransportPolicyRelay,
    ICEServers: []webrtc.ICEServer{
        {
            URLs: []string{
                "turn:bar.com:3478?transport=udp",
            },
            Username: "username",
            Credential: "password",
            CredentialType: webrtc.ICECredentialTypePassword,
        }
    }
})
Chrome Desktop Version 115
Chrome Android Version 114
LanTenDragon commented 1 year ago

Turns out I do not need a TURN server, STUN is enough even for both parties behind symmetric NAT. Im not sure how hole punching is achieved with just STUN,

The main problem is my misunderstanding of the WebRTC steps and behaviours. (CMIIW)

I think something like this could work as a solution.

1. On the frontend (browser-side), perform peerConnection.createOffer() as usual
2. Listen on peerConnection.onicegatheringstatechange() event
3. On ice gathering completion, add the SDP and browser ICE candidates into the API call's body
4. Perform peerConnection.addIceCandidate() on the server side.