Spring Boot WebRTC Guide | WebRTC Spring Boot | Video Chat Sinhala | Spring Boot Real-time

Spring Boot WebRTC Guide | WebRTC Spring Boot | Video Chat Sinhala | Spring Boot Real-time

Spring Boot WebRTC Integration SC Guide

කොහොමද යාලුවනේ! ඉතින් මේ දවස්වල ගොඩක් දෙනෙක් කතා කරන, ඒ වගේම අපේ දෛනික ජීවිතේටත් නැතුවම බැරි දෙයක් තමයි real-time communication කියන්නේ. Zoom calls, WhatsApp video calls, Facebook Messenger calls... මේ හැමදේකම පදනම තමයි real-time communication.

Software Engineers විදියට අපි මේ වගේ දෙයක් අපේම Application එකකට එකතු කරගන්නේ කොහොමද කියලා කල්පනා කරලා තියෙනවද? සමහරවිට ඔයා හිතනවා ඇති ඒක ලොකු වැඩක්, ගොඩක් අමාරු දෙයක් කියලා. හැබැයි අද මම ඔයාලට කියල දෙනවා Spring Boot එකත් WebRTC එකත් එකට පාවිච්චි කරලා කොහොමද සරල real-time video chat feature එකක් හදාගන්නේ කියලා. බය වෙන්න එපා, හිතන තරම් අමාරු නෑ!

මේ article එකේදී අපි බලමු:

  • WebRTC කියන්නේ මොකක්ද?
  • Spring Boot එක මේකට කොහොමද උදව් කරන්නේ?
  • සරල video chat application එකක් හදන හැටි (code එක්කම!)
  • මේකේ තියෙන අභියෝග සහ ඒවාට මුහුණ දෙන හැටි.

එහෙනම් අපි පටන් ගමුද?

WebRTC කියන්නේ මොකක්ද?

හරි, මුලින්ම බලමු මේ WebRTC කියන්නේ මොකක්ද කියලා. WebRTC කියන්නේ Web Real-Time Communication කියන එකේ කෙටි යෙදුමක්. මේක Google එකෙන් හඳුන්වා දුන්නු open-source project එකක්. මේකෙන් අපිට පුළුවන් web browsers අතරට (peer-to-peer) audio, video සහ generic data সরাসরি හුවමාරු කරගන්න. ඒකට වෙන කිසිම plugin එකක්, third-party software එකක් ඕනේ වෙන්නේ නෑ. Chrome, Firefox, Safari වගේ ගොඩක් popular browsers වලට මේක inbuilt වෙලා තියෙනවා.

මේකේ විශේෂත්වය තමයි traffic එක server එකක් හරහා යවන්නේ නැතුව, direct browser to browser යන එක. ඒ කියන්නේ latency එක අඩුයි, quality එක හොඳයි. හැබැයි මේ browsers දෙක අතරට direct connection එකක් හදාගන්න කලින්, පොඩි process එකක් තියෙනවා. ඒකට අපි කියන්නේ Signaling කියලා. Signaling කියන්නේ browsers දෙකට එකිනෙකාට සම්බන්ධ වෙන්න අවශ්‍ය information හුවමාරු කරගන්න එකයි. මේ information වලට සාමාන්‍යයෙන් SDP (Session Description Protocol) offers, SDP answers, සහ ICE (Interactive Connectivity Establishment) candidates වගේ දේවල් අදාළ වෙනවා.

මේ Signaling process එකට තමයි අපිට server එකක් (මේකේදී Spring Boot server එකක්) ඕන වෙන්නේ. ඒ server එක WebRTC data stream එකට සම්බන්ධ වෙන්නේ නෑ. ඒක කරන්නේ browsers දෙකට එකිනෙකාට කතා කරගන්න පාර හදලා දෙන එක විතරයි. මේකට අපි WebSocket වගේ real-time communication protocol එකක් පාවිච්චි කරනවා.

තව වැදගත් දෙයක් තමයි NAT traversal. ලංකාවේ අපි දන්නවනේ ගොඩක් වෙලාවට අපේ network public IP addresses නෙවෙයි, private IP addresses. එතකොට browsers දෙකක් අතර direct connection එකක් හදාගන්න අමාරුයි. මේකට අපිට STUN (Session Traversal Utilities for NAT) සහ TURN (Traversal Using Relays around NAT) servers උදව් වෙනවා. STUN servers කරන්නේ අපේ public IP address එක හොයාගන්න උදව් කරන එක. TURN servers කරන්නේ STUN server එකට බැරි වුනොත් data traffic එක තමන් හරහා relay කරන එක.

Spring Bootයි WebRTCයි එකට යාළුවො කරගමු!

දැන් බලමු Spring Boot මේකට කොහොමද fit වෙන්නේ කියලා. Spring Boot කියන්නේ Java developersලා අතරේ හරිම popular framework එකක්. ඒකෙන් පුළුවන් හරිම ලේසියෙන් standalone, production-ready applications හදන්න. WebRTC Signaling server එකක් විදියට Spring Boot පාවිච්චි කරන එකට තියෙන වාසි ගොඩයි:

  • සරලයි (Simple): Spring Boot වලට WebSocket support එකක් inbuilt තියෙනවා. ඒක හරිම ලේසියෙන් setup කරගන්න පුළුවන්.
  • Strong (Strong Ecosystem): Spring ecosystem එක හරිම ලොකුයි. ඒකේ security, data access වගේ ගොඩක් දේවල් තියෙනවා.
  • Scalable (Scalable): Spring Boot applications scalable කරන්න ලේසියි. ඒක නිසා usersලා ගොඩක් වැඩි වුනොත් උනත් handle කරන්න පුළුවන්.

අපේ Spring Boot server එකේ ප්‍රධාන කාර්යය වෙන්නේ WebRTC clients (browsers) අතරේ Signaling messages හුවමාරු කරන එක. මේකට අපි Spring Boot's WebSocket API එක පාවිච්චි කරනවා. Client A යවන SDP offer එකක් server එකට ඇවිත්, server එක ඒක Client B ට යවනවා. ඒ වගේම Client B යවන SDP answer එකක් server එකට ඇවිත්, server එක ඒක Client A ට යවනවා. ඒ වගේම ICE candidates වගේ දේවලුත් මේ විදියටම හුවමාරු වෙනවා.

වැදගත් දේ තමයි, මේ Spring Boot server එක real-time audio/video stream එකට සම්බන්ධ වෙන්නේ නෑ. ඒක කරන්නේ browsers දෙකට direct connection එකක් හදාගන්න අවශ්‍ය information හුවමාරු කරන එක විතරයි. Connection එක establish වුනාට පස්සේ, video/audio stream එක server එක පැත්තෙන් යන්නේ නෑ. ඒක direct peer-to-peer යනවා.

පියවරෙන් පියවර සරල Video Chat එකක් හදමු!

හරි, දැන් අපි මේක practically කරලා බලමු. අපි සරල video chat application එකක් හදමු. මේකට අපිට Spring Boot (backend) සහ HTML/JavaScript (frontend) ඕනේ වෙනවා.

1. Backend (Spring Boot Signaling Server)

මුලින්ම අපි Spring Boot project එකක් හදාගමු. Spring Initializr (https://start.spring.io/) එකට ගිහින් පහත dependencies add කරගන්න:

  • Spring Web
  • Spring WebSocket

Project එක generate කරගෙන, IDEA එකට import කරගන්න.

WebSocket Configuration

දැන් අපි WebSocket configuration එක ලියමු. මේකෙන් අපි WebSocket endpoint එක define කරනවා.

package com.example.webrtcspringboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // Enable a simple in-memory message broker
        config.enableSimpleBroker("/topic", "/user");
        // Define prefix for messages from clients to server
        config.setApplicationDestinationPrefixes("/app");
        // Define prefix for user-specific messages
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // Register the WebSocket endpoint
        // With CORS enabled for client testing
        registry.addEndpoint("/webrtc")
                .setAllowedOriginPatterns("*") // Allow all origins for simplicity in development
                .withSockJS(); // Use SockJS for broader browser support
    }
}

මේ configuration එකෙන් අපි /webrtc කියන endpoint එක register කරනවා. clientsලා මේ endpoint එකට තමයි connect වෙන්නේ. enableSimpleBroker එකෙන් අපි message broker එක define කරනවා, /topic කියන්නේ broadcast messages වලට, /user කියන්නේ specific user කෙනෙක්ට යවන messages වලට.

WebSocket Controller (Signaling)

දැන් අපි Signaling messages handle කරන්න Controller එකක් හදමු.

package com.example.webrtcspringboot.controller;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Controller
public class SignalingController {

    private final SimpMessagingTemplate messagingTemplate;
    private final Map<String, String> userSessionMap = new HashMap<>(); // Maps session ID to username (for simplicity)

    public SignalingController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageMapping("/offer")
    public void handleOffer(@Payload String offer, SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        System.out.println("Received offer from sessionId: " + sessionId + ": " + offer);

        // For a simple two-user chat, we need to find the other user.
        // In a real app, you'd have a more robust way to match users or rooms.
        String otherUserSessionId = userSessionMap.values().stream()
                .filter(sId -> !sId.equals(sessionId))
                .findFirst()
                .orElse(null);

        if (otherUserSessionId != null) {
            // Send the offer to the other connected user
            messagingTemplate.convertAndSendToUser(otherUserSessionId, "/topic/webrtc/offer", offer);
            System.out.println("Forwarded offer to: " + otherUserSessionId);
        } else {
            System.out.println("No other user found to send offer to.");
        }
    }

    @MessageMapping("/answer")
    public void handleAnswer(@Payload String answer, SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        System.out.println("Received answer from sessionId: " + sessionId + ": " + answer);

        String otherUserSessionId = userSessionMap.values().stream()
                .filter(sId -> !sId.equals(sessionId))
                .findFirst()
                .orElse(null);

        if (otherUserSessionId != null) {
            messagingTemplate.convertAndSendToUser(otherUserSessionId, "/topic/webrtc/answer", answer);
            System.out.println("Forwarded answer to: " + otherUserSessionId);
        } else {
            System.out.println("No other user found to send answer to.");
        }
    }

    @MessageMapping("/ice-candidate")
    public void handleIceCandidate(@Payload String candidate, SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        System.out.println("Received ICE candidate from sessionId: " + sessionId + ": " + candidate);

        String otherUserSessionId = userSessionMap.values().stream()
                .filter(sId -> !sId.equals(sessionId))
                .findFirst()
                .orElse(null);

        if (otherUserSessionId != null) {
            messagingTemplate.convertAndSendToUser(otherUserSessionId, "/topic/webrtc/ice-candidate", candidate);
            System.out.println("Forwarded ICE candidate to: " + otherUserSessionId);
        } else {
            System.out.println("No other user found to send ICE candidate to.");
        }
    }

    // Simple user registration for two-party chat
    @MessageMapping("/register")
    public void registerUser(SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        if (userSessionMap.size() < 2 && !userSessionMap.containsValue(sessionId)) {
            String username = "User" + (userSessionMap.size() + 1);
            userSessionMap.put(username, sessionId);
            System.out.println("Registered: " + username + " with session ID " + sessionId);
            if (userSessionMap.size() == 2) {
                // Notify both users that a call can now be initiated (simplified)
                System.out.println("Two users connected. Call can be initiated.");
            }
        } else if (userSessionMap.size() >= 2) {
            System.out.println("Maximum users reached for simple demo. Session ID: " + sessionId);
        }
    }
}

මේ Controller එකෙන් අපි /app/offer, /app/answer, /app/ice-candidate, සහ /app/register කියන endpoints වලට එන messages handle කරනවා. SimpMessagingTemplate එක පාවිච්චි කරලා අපි messages අනිත් client එකට යවනවා. මේක simple demo එකක් නිසා අපි usersලා දෙන්නෙක් විතරක් handle කරන විදියට හදලා තියෙන්නේ. real application එකක නම් room concepts, user authentication වගේ දේවල් add කරන්න ඕනේ.

2. Frontend (HTML/JavaScript)

දැන් අපි client side එක හදමු. මේකට සරල HTML file එකක් (index.html) සහ JavaScript file එකක් (app.js) ඕනේ.

index.html

මේකේදී අපි video elements දෙකක් හදාගන්නවා - එකක් local stream එකට, අනිත් එක remote stream එකට. ඒ වගේම Signaling messages යවන්න බොත්තම් කිහිපයක්. අපි SockJS සහ StompJS libraries පාවිච්චි කරනවා WebSocket connection එක handle කරන්න.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spring Boot WebRTC Video Chat</title>
    <style>
        body { font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; margin-top: 20px; }
        video { width: 48%; border: 1px solid #ccc; background-color: #eee; }
        #video-container { display: flex; justify-content: space-around; width: 80%; margin-bottom: 20px; }
        button { padding: 10px 20px; margin: 5px; cursor: pointer; }
        #messages { margin-top: 20px; border: 1px solid #ddd; padding: 10px; width: 80%; max-height: 150px; overflow-y: auto; }
    </style>
</head>
<body>
    <h1>Spring Boot WebRTC Video Chat</h1>
    
    <div id="video-container">
        <div>
            <h3>Local Video</h3>
            <video id="localVideo" autoplay muted></video>
        </div>
        <div>
            <h3>Remote Video</h3>
            <video id="remoteVideo" autoplay></video>
        </div>
    </div>

    <div>
        <button id="startCall">Start Call</button>
        <button id="endCall">End Call</button>
    </div>

    <div id="messages">
        <p>Status Messages:</p>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.0/sockjs.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
    <script src="app.js"></script>
</body>
</html>

app.js

මේක තමයි core logic එක. මේකේදී අපි WebRTC API එකයි (RTCPeerConnection, getUserMedia) WebSocket API එකයි එකට පාවිච්චි කරනවා.

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
const startCallButton = document.getElementById('startCall');
const endCallButton = document.getElementById('endCall');
const messagesDiv = document.getElementById('messages');

let localStream;
let peerConnection;
let stompClient = null;

const STUN_SERVER = 'stun:stun.l.google.com:19302'; // A public STUN server

function logMessage(message) {
    const p = document.createElement('p');
    p.textContent = message;
    messagesDiv.prepend(p);
}

// 1. Connect to WebSocket Server
function connectWebSocket() {
    logMessage('Attempting to connect to WebSocket...');
    const socket = new SockJS('/webrtc');
    stompClient = Stomp.over(socket);

    stompClient.connect({}, function (frame) {
        logMessage('Connected: ' + frame);
        stompClient.subscribe('/user/topic/webrtc/offer', function (message) {
            logMessage('Received offer: ' + message.body);
            handleOffer(JSON.parse(message.body));
        });
        stompClient.subscribe('/user/topic/webrtc/answer', function (message) {
            logMessage('Received answer: ' + message.body);
            handleAnswer(JSON.parse(message.body));
        });
        stompClient.subscribe('/user/topic/webrtc/ice-candidate', function (message) {
            logMessage('Received ICE candidate: ' + message.body);
            handleIceCandidate(JSON.parse(message.body));
        });
        
        // Register with the server (simplified for two-party demo)
        stompClient.send("/app/register", {}, '');
        logMessage('Registered with server.');

    }, function (error) {
        logMessage('STOMP Error: ' + error);
        setTimeout(connectWebSocket, 5000);
    });
}

// 2. Get User Media (Camera & Mic)
async function getLocalStream() {
    try {
        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
        localVideo.srcObject = localStream;
        logMessage('Local stream obtained.');
    } catch (e) {
        logMessage('Error accessing media devices: ' + e);
        console.error('getUserMedia error:', e);
    }
}

// 3. Create RTCPeerConnection
function createPeerConnection() {
    const configuration = { 'iceServers': [{ 'urls': STUN_SERVER }] };
    peerConnection = new RTCPeerConnection(configuration);

    // Add local stream tracks to the peer connection
    localStream.getTracks().forEach(track => {
        peerConnection.addTrack(track, localStream);
    });

    // Handle ICE candidates
    peerConnection.onicecandidate = (event) => {
        if (event.candidate) {
            logMessage('Sending ICE candidate.');
            stompClient.send("/app/ice-candidate", {}, JSON.stringify(event.candidate));
        }
    };

    // Handle remote stream when it's added
    peerConnection.ontrack = (event) => {
        logMessage('Received remote track.');
        if (remoteVideo.srcObject !== event.streams[0]) {
            remoteVideo.srcObject = event.streams[0];
        }
    };

    logMessage('Peer connection created.');
}

// 4. Handle Offer (Caller creates, Callee receives)
async function createOffer() {
    try {
        const offer = await peerConnection.createOffer();
        await peerConnection.setLocalDescription(offer);
        logMessage('Sending offer to peer.');
        stompClient.send("/app/offer", {}, JSON.stringify(offer));
    } catch (e) {
        logMessage('Error creating offer: ' + e);
        console.error('createOffer error:', e);
    }
}

async function handleOffer(offer) {
    try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        logMessage('Sending answer to peer.');
        stompClient.send("/app/answer", {}, JSON.stringify(answer));
    } catch (e) {
        logMessage('Error handling offer: ' + e);
        console.error('handleOffer error:', e);
    }
}

// 5. Handle Answer (Callee creates, Caller receives)
async function handleAnswer(answer) {
    try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
        logMessage('Remote description set (answer).');
    } catch (e) {
        logMessage('Error handling answer: ' + e);
        console.error('handleAnswer error:', e);
    }
}

// 6. Handle ICE Candidates
async function handleIceCandidate(candidate) {
    try {
        await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
        logMessage('ICE candidate added.');
    } catch (e) {
        logMessage('Error adding ICE candidate: ' + e);
        console.error('addIceCandidate error:', e);
    }
}

// Call Start/End Logic
startCallButton.onclick = async () => {
    logMessage('Starting call...');
    await getLocalStream();
    createPeerConnection();
    // Only create offer if we are the 'caller'. 
    // In a two-party system, one client initiates. For simplicity, we just create offer on button click.
    // In a more robust system, a 'join room' or 'call user' mechanism would trigger this.
    createOffer(); 
};

endCallButton.onclick = () => {
    logMessage('Ending call...');
    if (peerConnection) {
        peerConnection.close();
        peerConnection = null;
    }
    if (localStream) {
        localStream.getTracks().forEach(track => track.stop());
        localStream = null;
    }
    localVideo.srcObject = null;
    remoteVideo.srcObject = null;
    // Disconnect WebSocket if needed
    if (stompClient && stompClient.connected) {
        stompClient.disconnect();
        stompClient = null;
        logMessage('WebSocket disconnected.');
    }
};

// Initial connection to WebSocket
connectWebSocket();

දැන් මේක run කරමු!

  1. මුලින්ම Spring Boot application එක run කරන්න. (mvn spring-boot:run or from your IDE)
  2. index.html file එක browser දෙකක (හෝ Incognito windows දෙකක) open කරන්න.
  3. එක් browser එකක Start Call button එක click කරන්න.
  4. දැන් ඔයාට පේනවා ඇති local video stream එක, ඒ වගේම Signaling messages console එකේ පේනවා ඇති.
  5. ටික වෙලාවකින් අනිත් browser එකේත් video stream එක පේන්න පටන් ගනීවි.

ඔන්න ඔහොමයි සරල video chat එකක් Spring Boot සහ WebRTC පාවිච්චි කරලා හදාගන්නේ! මේක හරිම basic version එකක්. මේක further develop කරන්න ගොඩක් දේවල් තියෙනවා.

අභියෝග සහ විසඳුම්

මේ සරල demo එකෙන් අපිට WebRTC වල basics තේරුණාට, real-world application එකක් හදනකොට අභියෝග ගොඩක් තියෙනවා.

1. NAT Traversal (STUN/TURN Servers)

මම කලින් කිව්වා වගේ, ගොඩක් networks වලට private IPs තියෙන්නේ. direct peer-to-peer connection එකක් හදාගන්න අමාරුයි. ඒකට STUN සහ TURN servers ඕනේ. අපේ demo එකේදී අපි public STUN server එකක් පාවිච්චි කළා (stun:stun.l.google.com:19302). හැබැයි production environment එකකදී ඔයාට ඔයාගේම STUN/TURN server එකක් deploy කරන්න වෙනවා. Google Cloud, AWS වගේ platforms වලට TURN server solutions තියෙනවා, නැත්නම් open-source coturn වගේ servers පාවිච්චි කරන්න පුළුවන්.

2. Multi-Party Calls (SFU/MCU)

අපේ demo එකේදී අපි usersලා දෙන්නෙක් අතරේ video chat එකක් හැදුවා. හැබැයි Zoom වගේ applications වල usersලා ගොඩක් එකම call එකට සම්බන්ධ වෙනවා. ඒක WebRTC වලින් direct කරන්න අමාරුයි. ඒකට server side architecture එකක් ඕනේ. මේකට ප්‍රධාන concepts දෙකක් තියෙනවා:

  • SFU (Selective Forwarding Unit): මේකේදී server එකට හැම user කෙනෙක්ගෙන්ම stream එකක් එනවා. server එක ඒ streams අනිත් හැම user කෙනෙක්ටම forward කරනවා. Stream transcoding කරන්නේ නෑ, data save වෙනවා. (Kurento, Janus Gateway, Mediasoup වගේ frameworks මේකට පාවිච්චි කරනවා.)
  • MCU (Multipoint Control Unit): මේකේදී server එකට හැම user කෙනෙක්ගෙන්ම stream එකක් එනවා. server එක ඒ හැම stream එකක්ම එකට මිශ්‍ර කරලා, transcoding කරලා, single stream එකක් විදියට අනිත් හැම user කෙනෙක්ටම යවනවා. Server එකට CPU power ගොඩක් ඕනේ. (මේක සාමාන්‍යයෙන් SFU වලට වඩා අඩුවෙන් පාවිච්චි කරනවා, මොකද scalability අමාරුයි.)

Spring Boot application එකට මේ SFU/MCU capabilities direct add කරන්න බෑ. ඒකට වෙනම media server software එකක් (Kurento, Janus) Spring Boot එකත් එක්ක integrate කරන්න වෙනවා.

3. Security and Authentication

අපේ demo එකේ කිසිම user authentication එකක් නෑ. Real application එකකදී usersලා authenticate කරලා, authorization mechanism එකක් දාන්න ඕනේ. Spring Security මේකට පාවිච්චි කරන්න පුළුවන්.

4. Error Handling and UI/UX

Demo එක හරිම සරලයි. Production-ready application එකකට හොඳ error handling, reconnection logic, user friendly UI/UX එකක් ඕනේ. ඒ වගේම network fluctuations, device issues වගේ දේවල් handle කරන්නත් ඕනේ.

අවසන් වශයෙන්

ඉතින් යාලුවනේ, ඔන්න ඔහොමයි Spring Boot එකත් WebRTC එකත් එකට පාවිච්චි කරලා real-time video communication feature එකක් හදාගන්නේ. මේකේදී අපිට WebRTC වල basics, Signaling server එකක අවශ්‍යතාවය, සහ Spring Boot එකෙන් කොහොමද Signaling handle කරන්නේ කියන එක පැහැදිලි වුනා කියලා හිතනවා.

ඔයාත් Software Engineer කෙනෙක් නම්, නැත්නම් මේ පැත්තට අලුතින් එන කෙනෙක් නම්, මේ concept එක හරිම වටිනවා. මේක තවත් දියුණු කරන්න පුළුවන් දේවල් ගොඩක් තියෙනවා. Group calls, screen sharing, file sharing, recording වගේ features add කරන්න පුළුවන්. ඒ හැමදේටම පදනම තමයි අද අපි කතා කරපු මේ basic setup එක.

මේ code ටික ඔයාගේ පැත්තෙන් run කරලා බලන්න. මොකක් හරි ප්‍රශ්නයක් ආවොත්, අමාරු තැනක් තියෙනවා නම්, පහළින් comment එකක් දාන්න අමතක කරන්න එපා. මම පුළුවන් විදියට උදව් කරන්නම්. ඒ වගේම මේ වගේ තවත් guide එකක් ගැන ඔයාට අදහසක් තියෙනවා නම්, ඒකත් comment එකක් විදියට දාන්න!

එහෙනම්, ආයෙත් මේ වගේම පට්ට technical guide එකකින් හමුවෙමු! Happy Coding!