DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

WebRTC in Firefox: Video Calls, Data Channels, Privacy Concerns, and about:webrtc Debugging

WebRTC enables real-time communication directly in the browser — video calls, audio, data channels, and peer-to-peer file transfer. Firefox has strong WebRTC support. Here's the practical guide.

What WebRTC Can Do

  • Video/audio calls: getUserMedia captures camera/mic, RTCPeerConnection handles the connection
  • Screen sharing: getDisplayMedia captures screen/window/tab
  • P2P data: RTCDataChannel for low-latency peer-to-peer data transfer
  • File transfer: Binary data over data channels (no server needed for transfer)

WebRTC in Firefox: Permissions

Camera/Microphone: Firefox prompts for permission on first use. Permission is per-origin.

Check permissions: Click lock icon → Connection → More Information → Permissions → Access your Camera.

Screen sharing (getDisplayMedia): Always prompts with a picker — can't be pre-authorized.

Privacy tip: After calls, revoke permissions from the address bar. Or in Settings → Privacy → Permissions → Camera/Microphone → Settings.

Basic getUserMedia

// Request camera + microphone
const stream = await navigator.mediaDevices.getUserMedia({
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 },
    frameRate: { max: 30 }
  },
  audio: {
    echoCancellation: true,
    noiseSuppression: true
  }
});

// Attach to video element
const video = document.querySelector('video');
video.srcObject = stream;
video.play();

// Stop all tracks when done (release camera)
stream.getTracks().forEach(track => track.stop());
Enter fullscreen mode Exit fullscreen mode

Enumerating Devices

// List available cameras and microphones
const devices = await navigator.mediaDevices.enumerateDevices();

devices.filter(d => d.kind === 'videoinput').forEach(d => {
  console.log('Camera:', d.label, d.deviceId);
});

devices.filter(d => d.kind === 'audioinput').forEach(d => {
  console.log('Microphone:', d.label, d.deviceId);
});

// Use a specific device
const stream = await navigator.mediaDevices.getUserMedia({
  video: { deviceId: { exact: specificDeviceId } }
});
Enter fullscreen mode Exit fullscreen mode

Firefox behavior: Device labels are only shown after permission is granted. Before permission: deviceId is visible but label is empty (for privacy).

Screen Sharing

// Request screen/window/tab capture
const screenStream = await navigator.mediaDevices.getDisplayMedia({
  video: {
    cursor: 'always',  // Show cursor in recording
    displaySurface: 'window'  // 'monitor', 'window', 'browser'
  },
  audio: false  // Screen audio (supported in Chrome but limited in Firefox)
});

const video = document.querySelector('video');
video.srcObject = screenStream;
Enter fullscreen mode Exit fullscreen mode

Firefox vs Chrome screen audio: Chrome supports capturing system audio with screen sharing. Firefox does not capture system audio with getDisplayMedia. This is a known limitation.

Firefox allows sharing specific tab: In Firefox's screen picker, you can select a specific browser tab to share, same as Chrome.

RTCPeerConnection (Video Calls)

A minimal video call setup:

// Signaling server (WebSocket) handles exchanging SDP and ICE candidates
// This example uses a hypothetical signaling channel

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }
  ]
});

// Add local stream
stream.getTracks().forEach(track => pc.addTrack(track, stream));

// Handle incoming stream
pc.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};

// Exchange ICE candidates via signaling
pc.onicecandidate = (event) => {
  if (event.candidate) {
    signalingChannel.send({ candidate: event.candidate });
  }
};

// Caller creates offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signalingChannel.send({ sdp: offer });

// Callee receives offer, creates answer
pc.setRemoteDescription(remoteOffer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
signalingChannel.send({ sdp: answer });

// Both sides add received ICE candidates
pc.addIceCandidate(receivedCandidate);
Enter fullscreen mode Exit fullscreen mode

Data Channels

P2P data transfer without a media stream:

const pc = new RTCPeerConnection({ iceServers: [...] });

// Create data channel on the caller side
const dataChannel = pc.createDataChannel('my-channel', {
  ordered: true,     // Guaranteed delivery (TCP-like)
  maxRetransmits: 0  // 0 = unreliable (UDP-like, use for gaming)
});

dataChannel.onopen = () => console.log('Channel open');
dataChannel.onmessage = (e) => console.log('Received:', e.data);

// Send data
dataChannel.send('Hello!');
dataChannel.send(JSON.stringify({ type: 'ping' }));

// Binary data (file transfer)
const buffer = await file.arrayBuffer();
dataChannel.send(buffer);

// On the receiving side
pc.ondatachannel = (event) => {
  const channel = event.channel;
  channel.onmessage = (e) => {
    if (e.data instanceof ArrayBuffer) {
      // Handle file chunk
    }
  };
};
Enter fullscreen mode Exit fullscreen mode

WebRTC and Privacy: IP Leaks

The IP leak problem: WebRTC can reveal your local network IP (192.168.x.x) and your real public IP even when using a VPN.

Why: WebRTC ICE (Interactive Connectivity Establishment) gathers all network interface addresses to find the best connection path. This includes addresses that bypass the VPN tunnel.

Firefox mitigation:
In about:config:

media.peerconnection.ice.default_address_only = true
media.peerconnection.ice.no_host = true
Enter fullscreen mode Exit fullscreen mode

default_address_only = true: Only use the default network interface (not all interfaces), reducing leak surface.
ice.no_host = true: Don't include local/host addresses at all.

Test for leaks: Use a WebRTC IP leak test site. With these settings, your real IP should not appear when behind a VPN.

Full disable (breaks WebRTC):

media.peerconnection.enabled = false
Enter fullscreen mode Exit fullscreen mode

Debugging WebRTC in Firefox

about:webrtc: Firefox's built-in WebRTC diagnostics page. Shows:

  • Active ICE connections
  • ICE candidates gathered
  • Statistics per peer connection (packet loss, jitter, bandwidth)
  • Session logging

To use:

  1. Open a WebRTC-based app (video call, etc.)
  2. In another tab: about:webrtc
  3. See all active connections with their statistics

This is useful for:

  • Diagnosing video call quality issues
  • Checking which ICE candidates were used
  • Verifying TURN/STUN server selection

TURN Servers for Firewalls

ICE can fail when both peers are behind strict firewalls/NAT. TURN servers relay the traffic:

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.example.com:3478' },
    {
      urls: 'turn:turn.example.com:3478',
      username: 'user',
      credential: 'password'
    }
  ]
});
Enter fullscreen mode Exit fullscreen mode

Free TURN servers: None reliable for production. For production apps, use Twilio, Xirsys, or self-host coturn.

In development: localhost connections usually work without TURN (same machine or local network).

Firefox WebRTC vs Chrome

Codec support:

  • Both support VP8, VP9, H.264
  • Firefox supports AV1 video codec
  • Firefox supports Opus (audio), G.711

Simulcast: Firefox supports simulcast (sending multiple quality streams simultaneously). Important for video conferences with many participants.

Screen audio: Chrome can capture system audio with getDisplayMedia. Firefox cannot (platform limitation).

WebRTC Stats API: Both support RTCPeerConnection.getStats(). Firefox and Chrome return slightly different stat names — check for both names in cross-browser code.

Top comments (0)