Frontend Development 30 min read

Master WebRTC: Build P2P Video Calls with Vue, Node.js, and TURN

This tutorial explains WebRTC fundamentals, signaling via WebSocket, NAT traversal with STUN/TURN, peer‑to‑peer connection flow, media capture, screen sharing, recording, and deployment tips, providing complete Vue and Node.js code examples for building robust 1‑to‑1 and multi‑user video applications.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Master WebRTC: Build P2P Video Calls with Vue, Node.js, and TURN

Prerequisites

WebRTC (Web Real‑Time Communication) is a W3C standard API that enables browsers to perform real‑time audio, video, and screen sharing. It functions both as an API and a protocol, and requires a signaling channel—commonly implemented with WebSocket—to exchange offers, answers, and ICE candidates.

Key Concepts

WebRTC uses peer‑to‑peer (P2P) connections, meaning media streams flow directly between endpoints without server relay, although a TURN server may be used as a fallback when NAT traversal fails. NAT traversal techniques such as STUN (hole punching) and TURN (relay) are essential for establishing connections across different networks.

Connection Workflow

Both peers first open a WebSocket connection to a signaling server. They exchange SDP offers/answers and ICE candidates to negotiate media capabilities and network paths. The process includes creating an

RTCPeerConnection

, adding local streams, handling

onicecandidate

, and rendering remote streams.

<code>A and B connect via WebSocket, join the same channel, and can receive each other's messages and signaling.

A creates RTCPeerConnection, adds local stream, creates an offer (SDP), sets local description, and sends the offer through the signaling server.

B receives the offer, creates its own RTCPeerConnection, adds its local stream, sets remote description, creates an answer, sets local description, and sends the answer back.

Both peers exchange ICE candidates (STUN/TURN) to establish the optimal path, then render each other's media streams.</code>

Media Negotiation Completion

<code>STUN/TURN servers respond with ICE candidates containing public IP and port.
A and B add each other's candidates via addIceCandidate.
After candidate exchange, the best P2P path is selected.
If NAT traversal fails, TURN relays the media streams.
Both peers then play the received audio/video streams.</code>

Deep Dive

<code>Local LAN: No TURN needed; direct P2P works.
Online: STUN may succeed (≈70% abroad, <50% China). If it fails, TURN relays media.
Reverse connections can also establish P2P when one side has a public address.</code>

Implementation Details

Signaling with Socket.IO

<code>async connectSocket({ commit, state, dispatch }) {
  // LAN
  let socket = io.connect("http://172.28.74.16:3004");
  // Online
  // let socket = io.connect("https://www.codeting.top:3004");
  socket.on("connect", () => {
    commit(types.SET_SOCKET, socket);
    dispatch("handleChatData", state.friends);
  });
  socket.on("friendMessage", (res) => {
    if (res) {
      commit(types.SET_MESSAGE, res);
    } else {
      console.log("有问题");
    }
  });
  // ... other event listeners for apply, reply, ICE, offer, answer
}
</code>
<code>io.on("connect", function (socket) {
  socket.on('friendMessage', async function (res) {
    const roomId = res.userId > res.friendId ? res.userId + res.friendId : res.friendId + res.userId;
    io.to(roomId).emit('friendMessage', res);
  });
  socket.on('apply', async function (res) {
    io.to(res.roomId).emit('apply', res);
  });
  // ... other handlers for reply, ICE, offer, answer
});
</code>

Media Capture

<code>const constraints = {
  audio: { noiseSuppression: true, echoCancellation: true },
  video: true,
};
this.localstream = await navigator.mediaDevices.getUserMedia(constraints);
let video = document.querySelector("#rtcA");
video.srcObject = this.localstream;
</code>

For audio‑only, set

video: false

. For screen sharing, replace

getUserMedia

with

getDisplayMedia

.

Recording and Screenshot

Use

canvas.getContext('2d').drawImage

to capture a frame, and

MediaRecorder

to record audio/video streams.

<code>let picture = document.querySelector("#picture");
let rtcA = document.querySelector("#rtcA");
picture.getContext("2d").drawImage(rtcA, 0, 0, 200, 120);
</code>
<code>let options = { mimeType: "video/webm;codecs=vp8" };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
  console.error(`${options.mimeType} is not supported`);
}
this.mediaRecorder = new MediaRecorder(window.streamFriend, options);
this.mediaRecorder.ondataavailable = function (e) {
  if (e && e.data && e.data.size > 0) {
    that.bufferFriend.push(e.data);
  }
};
this.mediaRecorder.start(10);
</code>

Deployment Considerations

Production requires a domain name and HTTPS for both the web page and WebSocket (WSS). NAT traversal in the wild depends on a TURN server (e.g., coturn) configured with proper firewall rules.

Limitations and Optimizations

Common issues include environmental noise, echo, and network jitter. Enabling

noiseSuppression

and

echoCancellation

in the audio constraints mitigates some problems; further improvements rely on advanced algorithms, better hardware, and network quality.

Advanced Topics

Video effects can be added via canvas filters, though they do not modify the underlying media stream. For true stream‑level effects, processing must occur before the stream is sent, which is limited by JavaScript performance.

Multi‑User Video

For n participants, each client creates n‑1

RTCPeerConnection

instances. Mesh topology (full peer‑to‑peer) is simple but scales poorly; alternatives include MCU (media mixing) and SFU (selective forwarding) for larger rooms.

Conclusion

The article provides a step‑by‑step guide to understanding, building, and deploying a WebRTC‑based 1‑to‑1 video chat with Vue and Node.js, covering signaling, NAT traversal, media handling, recording, and scaling considerations, encouraging further exploration of quality‑of‑service techniques.

Node.jsVueWebSocketP2PWebRTCSTUNTURN
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.