Camera/Screen Sharing Works Locally but Not on UAT Server — No Errors in Mediasoup Logs

Hi team,

We’ve been integrating Mediasoup into our online proctoring system with a React frontend and Node.js backend. Everything works perfectly in local development, including webcam and screen sharing (candidate → proctor). However, when deployed to our UAT server, neither webcam nor screen video shows up on the proctor side — despite the Mediasoup logs showing no errors.

Environment Overview:

  • Local Setup (Works):
    • NODE_ENV=development
    • WebRTC works with webcam and screen sharing.
    • STUN-only configuration is enough.
  • UAT Server (Fails):
    • Hosted behind NGINX (SSL Termination)
    • NODE_ENV=uat and UAT_ENVIRONMENT=true
    • WebSocket (wss://...) connection is successful.
    • :locked: STUN + TURN servers configured (fallback included).
    • Candidate’s webcam stream shows { active: true, tracks: [...] } but video doesn’t render on proctor’s side.

What We’ve Verified So Far:

  1. Socket.io new-producer event emits successfully when candidate shares webcam.
  2. Mediasoup producer is created and logged with valid RTP parameters.
  3. Consumer is created successfully (Mediasoup logs confirm this too).
  4. ICE and DTLS events show state transitions (connected/connected).
  5. webcamStream.getTracks() logs correctly on candidate side.
  6. No errors in mediaSoupSocket.js or server logs.
  7. Mediasoup logs show no warnings/errors — all transports/producers/consumers created as expected.

Key Mediasoup Server Config (UAT)

// mediasoup/server.js
transportOptions = {
  listenIps: [{ ip: process.env.UAT_LISTEN_IP || '0.0.0.0', announcedIp: process.env.UAT_ANNOUNCED_IP }],
  enableUdp: true,
  enableTcp: true,
  preferUdp: true,
  iceServers: [
    {
      urls: [
        'stun:stun.l.google.com:19302',
        'stun:stun1.l.google.com:19302',
        ...
      ]
    },
    {
      urls: ['turn:localhost:3478'],
      username: 'mediasoup',
      credential: 'password123'
    }
  ]
};

NGINX Config (UAT):

location /socket.io/ {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
}

:white_check_mark: WebSocket successfully connects (confirmed in browser).

What We Suspect:

  • Could be firewall blocking UDP on server?
  • Possible TURN server misconfiguration (although fallback exists)?
  • Could we be missing consumer.resume() at the right point?
  • Any known Mediasoup bug where tracks are live but not rendering?

:folded_hands: What We Need Help With:

  1. What else should we validate if consumer is created, but video is black?
  2. How can we debug whether the media is reaching the proctor side correctly?
  3. Any must-have server-level ports or firewall rules needed for Mediasoup to relay media properly?

Here the attached screen of mediasoup’s log from server

Can you check the webrtc status for the browser on the proctor end to see if any media stream is being received. For chrome that will be chrome://webrtc-internals and for Firefox it will be about:webrtc.

Thanks for the reply @BlueMagnificent
here the report

{
  "bandwidthEstimations": [],
  "codecStats": [],
  "dataChannelStats": [],
  "iceCandidatePairStats": [],
  "iceCandidateStats": [],
  "inboundRtpStreamStats": [],
  "mediaSourceStats": [],
  "outboundRtpStreamStats": [],
  "peerConnectionStats": [
    {
      "id": "{811ab57c-d48c-4b73-9bf1-e2a9845c1923}",
      "timestamp": 1753672176635,
      "type": "peer-connection",
      "dataChannelsClosed": 0,
      "dataChannelsOpened": 0
    }
  ],
  "rawLocalCandidates": [],
  "rawRemoteCandidates": [],
  "remoteInboundRtpStreamStats": [],
  "remoteOutboundRtpStreamStats": [],
  "rtpContributingSourceStats": [],
  "trickledIceCandidateStats": [],
  "videoFrameHistories": [],
  "videoSourceStats": [],
  "browserId": 19,
  "closed": true,
  "configuration": {
    "bundlePolicy": "max-bundle",
    "certificatesProvided": true,
    "iceServers": [],
    "iceTransportPolicy": "all",
    "peerIdentityProvided": false
  },
  "iceRestarts": 0,
  "iceRollbacks": 0,
  "offerer": false,
  "pcid": "{811ab57c-d48c-4b73-9bf1-e2a9845c1923} 1753672176353605 (id=25769803781 url=https://uat-proctoring.enfusesolutions.com/candidate/687f1a9b2e0d4c523dcbf6a2)",
  "sdpHistory": [],
  "timestamp": 1753672176635
}

That’s not what we’re looking for, in webrtc-internals (in browser) will provide you inbound-rtp stats suggesting if the content is actually making it to the user, bunch of other identifiers there like if connection state: new => connected all sorts.

You can usually access internals by entering browser name first.
browser-name://webrtc-internals

If the transport is looking groovy, you may have auto-play issues if this is a custom client.

For the server the user is connected when there’s DTLS is connected. Client though can have problems though (auto-play, invalid signaling, all sorts). Cheers.