Skip to content

BenchPress200/webrtc-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebRTC Tutorial

This repository serves as a hands-on guide for WebRTC. It aims to clearly demonstrate how peer-to-peer connections are established and how video streams are exchanged using WebRTC.

Note

You can find a more detailed explanation of the basic concepts of WebRTC and this tutorial’s code in my blog post.
🔗 BenchPress200's Tech Blog


🌃 Preview


🔧 Stack

Java Spring Boot
TypeScript React Styled-Components Zustand
WebSocket WebRTC



📌 Before You Start

Test Environment

This tutorial code can be tested in a local development environment. To run the source code, you will need to have the following tools installed.

  • VS Code
  • Chrome
  • node (v22.10.0)
  • npm (v10.9.0)
  • IntelliJ IDEA
  • JDK 17

WebRTC Communication Workflow

This tutorial handles not only signaling but all server communications via WebSocket. To establish a peer-to-peer connection, a signaling process is required. The steps involved in the signaling process to connection establishment are as follows.

  1. Both Peer A and Peer B set up their own media streams(microphone and video) - Peer A, Peer B
  2. One peer (Peer A) creates an SDP(offer) - Peer A
  3. Peer A sets the generated SDP(offer) as its local description and sends it to Peer B - Peer A
  4. Peer B receives the SDP(offer) and sets it as its remote description - Peer B
  5. Peer B creates an SDP(answer) in response to the received SDP(offer) - Peer B
  6. Peer B sets its generated SDP(answer) as its local description and sends it to Peer A - Peer B
  7. Peer A receives the SDP(answer) and sets it as its remote description - Peer A
  8. Both peers exchange the ICE candidates they collected and add the remote candidates - Peer A, Peer B
    • Each peer begins gathering its own ICE candidates after completing SDP creation. A peer can only add the other peer's candidates after setting the remote SDP
  9. Once ICE candidate exchange is complete and a viable candidate pair is found, the connection is successfully established - Peer A, Peer B

Key APIs required for the signaling process for video calling

  • new RTCPeerConnection()
    • Creates a new WebRTC connection instance for peer-to-peer communication
  • peerConnection.ontrack = (event) => {}
    • Triggered when a media track (audio or video) is received from the remote peer
  • peerConnection.onicecandidate = (event) => {}
    • Called when a new ICE candidate is discovered
  • peerConnection.oniceconnectionstatechange = (event) => {}
    • Monitors changes in the ICE connection state (e.g., connected, disconnected)
  • navigator.mediaDevices.getUserMedia()
    • Requests access to the user's camera and microphone
  • peerConnection.createOffer()
    • Creates an SDP offer to initiate a WebRTC connection with another peer
  • peerConnection.createAnswer()
    • Creates an SDP answer in response to an offer from another peer
  • peerConnection.setLocalDescription()
    • Sets the local peer’s SDP (offer or answer) for signaling
  • peerConnection.setRemoteDescription()
    • Applies the received SDP from the remote peer
  • peerConnection.addIceCandidate()
    • Adds an ICE candidate received from the remote peer to establish connectivity

Custom hooks created to utilize these key APIs in accordance with the tutorial code

A custom WebRTC hook was implemented to establish a peer-to-peer connection through user interaction in the call page component. It is located at /frontend/src/hooks/useWebRTC.ts


Requirements for Production Deployment

When deploying a WebRTC-based feature to a production environment, there are several critical considerations beyond what is covered in this tutorial:

  • Automated Signaling Flow
    In this tutorial, the signaling process is manually triggered by user interactions (e.g., button clicks). However, in a real-world service, signaling should be initiated and completed automatically—such as sending an offer and receiving an answer—without requiring direct user actions. This ensures a seamless connection experience.

  • Ping-Pong Mechanism
    Since signaling is handled over WebSocket in this tutorial, production environments must account for possible disconnections caused by NATs, firewalls, load balancers, or web servers that enforce idle timeouts. If the WebSocket connection is terminated during signaling, the peer connection cannot be established. To resolve this, a ping-pong mechanism should be implemented to keep the signaling connection alive.

  • TURN Server Deployment
    Unlike local environments, real-world peer networks are often restricted by NATs or firewalls, which can prevent peers from exchanging usable IP and port information. In such cases, a TURN (Traversal Using Relays around NAT) server acts as a relay to facilitate media transmission. Deploying a TURN server is essential to ensure reliable connectivity across various network conditions.



🏁 Getting Started

1. Clone Repository

git clone https://github.com/BenchPress200/webrtc-tutorial

Clone the tutorial repository to your local development environment.


2. Open the Frontend Code

Open webrtc-tutorial/frontend directory in VSCode to run the React app.


3. Install Required Packages

스크린샷 2025-05-24 오후 8 11 56

npm install

In the VSCode window you just opened, open the terminal and run npm install to install the necessary packages.


4. Run the React App

스크린샷 2025-05-24 오후 8 13 43 스크린샷 2025-05-24 오후 8 23 09

npm start

After the installation is complete, run npm start in the terminal to launch the React application.


5. Open the Backend Code in IntelliJ

Note

If you don’t plan to look into the source code or test it directly, and just want to run the backend app without using an IDE like IntelliJ, you can simply open a terminal or PowerShell, navigate to webrtc-tutorial/backend, and run ./gradlew bootRun.
Then, you can skip the backend app setup steps below and start from step 8 🙂

스크린샷 2025-05-24 오후 8 17 44 스크린샷 2025-05-24 오후 8 18 23

To run the Spring Boot app, open webrtc-tutorial/backend directory as a Gradle project in IntelliJ.


6. Configure Lombok

스크린샷 2025-05-24 오후 8 25 38 스크린샷 2025-05-24 오후 8 24 08

Check the IntelliJ settings to ensure Lombok is working properly.


7. Run the Spring Boot App

스크린샷 2025-05-24 오후 8 28 58 스크린샷 2025-05-24 오후 8 29 28

Run the Spring Boot application.


8. Access the React App

스크린샷 2025-05-24 오후 8 32 55

http://localhost:3000

Open your browser and launch two tabs with http://localhost:3000.


9. Allow Access to Microphone and Camera

스크린샷 2025-05-24 오후 8 34 36
Allow the browser to access media devices (camera and microphone). If access is not granted, errors may occur.


10. Register Test Users

스크린샷 2025-05-24 오후 8 34 36
Register as Patrick and SpongeBob, respectively.


11. Initiate a Call

스크린샷 2025-05-24 오후 8 34 36
Call SpongeBob, who then accepts the call. After that, both users are taken to the call page.


12. Signaling Process

스크린샷 2025-05-24 오후 8 34 36

  1. Patrick and SpongeBob each set up their own video and microphone.
  2. SpongeBob, who receives the call, waits for Patrick’s offer. Patrick, who initiates the call, creates and sends the offer first.
    • When Patrick creates the offer, his ICE candidates start to gather. As each candidate is collected, it is immediately sent to SpongeBob.
  3. SpongeBob receives Patrick’s offer and sets it as the remote description.
    • Once the remote offer is set, SpongeBob is ready to add ICE candidates from Patrick. He adds any candidates that were buffered during the wait and continues to add new ones as they arrive.
  4. Patrick, after sending the offer, waits for SpongeBob’s answer. SpongeBob creates and sends the answer to Patrick.
    • When SpongeBob creates the answer, his ICE candidates start to gather. As each candidate is collected, it is immediately sent to Patrick.
  5. Patrick receives SpongeBob’s answer and sets it as the remote description.
    • Once the remote offer is set, Patrick is ready to add ICE candidates from SpongeBob. He adds any candidates that were buffered during the wait and continues to add new ones as they arrive.
  6. Once both SDP messages have been exchanged and set, and ICE candidate exchange and connection are complete, the final step is marked as 'Done', and the video call becomes active.

13. Start the Video Call


Start the video call.



⚠️ Note on ICE Connection Behavior During Local Testing

In local test environments, you may observe that the callee (the user who receives the call) reaches an iceConnectionState === 'connected' status before the caller has finished setting the callee’s answer or adding ICE candidates.

This is not a bug, but a valid outcome under WebRTC’s behavior.


✅ Why this happens

WebRTC uses Trickle ICE by default, where ICE candidates are exchanged after the offer/answer is set.

However, if a viable ICE candidate (e.g., a host candidate on the same LAN) is already included in the SDP, a peer connection may reach connected even if the other side has not completed their setup.

This can also occur more often in local networks (with no NAT or STUN/TURN needed), where direct connectivity is easily established.


🔁 What this means

The callee can technically enter a connected state and begin sending media before the caller has finished applying the answer or ICE candidates.

Once the caller completes setting the callee’s answer and adds the pending candidates, the connection becomes fully established from both sides.

As a result, the call can proceed successfully even if the callee’s connection appears to be completed slightly earlier.

🛠️ Example Scenario

  1. Patrick (caller) creates an offer and starts gathering candidates.
  2. SpongeBob (callee) receives the offer, sets it, creates an answer, and sends it back.
  3. As SpongeBob gathers and sends his ICE candidates, he may already reach connected if a usable candidate pair is found (e.g., host-host).
  4. Patrick, who hasn't yet set the answer or added candidates, appears “in progress.”
  5. Once Patrick applies SpongeBob’s answer and adds his candidates, the connection is established from his side as well.

Both users can now proceed to a stable call state with no functional issues.



💥 Issue

If you encounter any issues or have questions about the tutorial code, I'd really appreciate it if you could open an issue using the "Issues" tab at the top of the repository page. I'll respond as soon as possible. To create an issue, please follow these steps:

  1. Click the Issues tab 스크린샷 2025-05-23 오전 11 47 53

  2. Click New Issue 스크린샷 2025-05-23 오전 11 48 35

  3. Select the custom issue template 스크린샷 2025-05-23 오전 11 49 22

  4. Fill in a clear and relevant title, complete each section, and click Submit new issue 스크린샷 2025-05-23 오전 11 49 51

Thank you for your contribution!



About

A WebRTC tutorial for learning peer-to-peer audio/video communication with custom signaling.

Topics

Resources

License

Stars

Watchers

Forks

Languages