# Milestone 5: WebRTC Bridge **Status**: Not Started **Goal**: Cross-device communication via WebRTC for calls, messaging, and file sharing. --- ## Overview WebRTC enables: - Voice/video calls between virtual phones - Text messaging across different games - File sharing between devices - Screen sharing - Connection to real smartphones (companion app) --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Virtual Phone A │ │ (VR Game on User's PC) │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ WebRTCBridge │ │ │ │ ├── DataChannel (messages, files) │ │ │ │ ├── AudioTrack (voice) │ │ │ │ └── VideoTrack (camera/screen share) │ │ │ └───────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ │ │ WebRTC (UDP/DTLS-SRTP) │ ┌─────────┴─────────┐ │ Signaling Server │ │ (WebSocket) │ └─────────┬─────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Virtual Phone B │ │ Virtual Phone C │ │ Real Smartphone │ │ (Different Game)│ │ (Same Game) │ │ (Companion App) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` --- ## Dependencies ### libdatachannel C++ WebRTC library for data channels, audio, and video. **vcpkg installation**: ```bash vcpkg install libdatachannel ``` **CMakeLists.txt**: ```cmake find_package(LibDataChannel CONFIG REQUIRED) target_link_libraries(mosis-kernel PRIVATE LibDataChannel::LibDataChannel) ``` --- ## Core Components ### WebRTC Bridge **File**: `src/main/kernel/include/webrtc_bridge.h` ```cpp namespace mosis { struct PeerInfo { std::string peer_id; std::string display_name; bool is_online; }; struct CallState { std::string peer_id; bool is_active; bool is_video; bool is_muted; int64_t start_time; }; class IWebRTCBridge { public: virtual ~IWebRTCBridge() = default; // Connection virtual void Connect(const std::string& signaling_server_url) = 0; virtual void Disconnect() = 0; virtual bool IsConnected() const = 0; // Identity virtual void SetIdentity(const std::string& phone_id, const std::string& display_name) = 0; virtual std::string GetPhoneId() const = 0; // Peer discovery virtual std::vector GetOnlinePeers() = 0; // Messaging using MessageCallback = std::function; virtual void SetMessageCallback(MessageCallback callback) = 0; virtual void SendMessage(const std::string& to, const std::string& message) = 0; // Voice calls using CallCallback = std::function; virtual void SetCallCallback(CallCallback callback) = 0; virtual void StartCall(const std::string& peer_id, bool with_video) = 0; virtual void AnswerCall(const std::string& peer_id) = 0; virtual void EndCall(const std::string& peer_id) = 0; virtual void MuteCall(bool muted) = 0; // File transfer using FileTransferCallback = std::function& data)>; virtual void SetFileTransferCallback(FileTransferCallback callback) = 0; virtual void SendFile(const std::string& to, const std::string& filename, const std::vector& data) = 0; }; } // namespace mosis ``` ### Implementation **File**: `src/main/kernel/src/webrtc_bridge.cpp` ```cpp #include #include "webrtc_bridge.h" namespace mosis { class WebRTCBridgeImpl : public IWebRTCBridge { public: WebRTCBridgeImpl() { rtc::InitLogger(rtc::LogLevel::Warning); } void Connect(const std::string& signaling_url) override { m_ws = std::make_shared(); m_ws->onOpen([this]() { SendSignaling({{"type", "register"}, {"id", m_phone_id}}); }); m_ws->onMessage([this](std::variant msg) { if (auto* str = std::get_if(&msg)) { HandleSignaling(nlohmann::json::parse(*str)); } }); m_ws->open(signaling_url); } void SendMessage(const std::string& to, const std::string& message) override { if (auto it = m_peers.find(to); it != m_peers.end()) { auto& peer = it->second; if (peer.data_channel && peer.data_channel->isOpen()) { nlohmann::json msg = { {"type", "message"}, {"text", message} }; peer.data_channel->send(msg.dump()); } } } private: struct PeerConnection { std::shared_ptr pc; std::shared_ptr data_channel; std::shared_ptr audio_track; std::shared_ptr video_track; }; void CreatePeerConnection(const std::string& peer_id) { rtc::Configuration config; config.iceServers.emplace_back("stun:stun.l.google.com:19302"); auto pc = std::make_shared(config); pc->onStateChange([this, peer_id](rtc::PeerConnection::State state) { // Handle connection state changes }); pc->onLocalDescription([this, peer_id](rtc::Description desc) { SendSignaling({ {"type", desc.typeString()}, {"to", peer_id}, {"sdp", std::string(desc)} }); }); pc->onLocalCandidate([this, peer_id](rtc::Candidate cand) { SendSignaling({ {"type", "candidate"}, {"to", peer_id}, {"candidate", std::string(cand)} }); }); m_peers[peer_id] = {pc, nullptr, nullptr, nullptr}; } void HandleSignaling(const nlohmann::json& msg); void SendSignaling(const nlohmann::json& msg); std::string m_phone_id; std::shared_ptr m_ws; std::map m_peers; MessageCallback m_message_cb; CallCallback m_call_cb; }; } // namespace mosis ``` --- ## Signaling Protocol ### Message Types ```json // Register with server {"type": "register", "id": "phone_123", "name": "John's Phone"} // Peer discovery {"type": "get_peers"} {"type": "peers", "list": [{"id": "phone_456", "name": "Jane's Phone", "online": true}]} // WebRTC signaling {"type": "offer", "to": "phone_456", "sdp": "v=0\r\n..."} {"type": "answer", "to": "phone_123", "sdp": "v=0\r\n..."} {"type": "candidate", "to": "phone_456", "candidate": "candidate:..."} // Call signaling {"type": "call_request", "to": "phone_456", "video": false} {"type": "call_accept", "to": "phone_123"} {"type": "call_reject", "to": "phone_123", "reason": "busy"} {"type": "call_end", "to": "phone_456"} ``` ### Signaling Server **Simple Node.js reference implementation**: ```javascript const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); const peers = new Map(); wss.on('connection', (ws) => { ws.on('message', (data) => { const msg = JSON.parse(data); if (msg.type === 'register') { peers.set(msg.id, { ws, name: msg.name }); broadcast({ type: 'peer_joined', id: msg.id, name: msg.name }); } else if (msg.to) { // Forward to recipient const peer = peers.get(msg.to); if (peer) { msg.from = [...peers.entries()].find(([id, p]) => p.ws === ws)?.[0]; peer.ws.send(JSON.stringify(msg)); } } }); ws.on('close', () => { const id = [...peers.entries()].find(([_, p]) => p.ws === ws)?.[0]; if (id) { peers.delete(id); broadcast({ type: 'peer_left', id }); } }); }); function broadcast(msg) { peers.forEach((peer) => peer.ws.send(JSON.stringify(msg))); } ``` --- ## Lua API ```lua -- Connect to signaling server mosis.webrtc.connect("wss://signal.mosis.dev") -- Set identity mosis.webrtc.setIdentity("user_12345", "John's Phone") -- Get online peers local peers = mosis.webrtc.getPeers() for _, peer in ipairs(peers) do print(peer.id, peer.name, peer.online) end -- Send message mosis.webrtc.sendMessage("peer_id", "Hello!") -- Receive messages mosis.webrtc.onMessage(function(from, message) print("Message from " .. from .. ": " .. message) end) -- Make a call mosis.webrtc.call("peer_id", { video = false }) -- Handle incoming call mosis.webrtc.onIncomingCall(function(from, hasVideo) -- Show call UI -- Accept: mosis.webrtc.answerCall(from) -- Reject: mosis.webrtc.rejectCall(from) end) ``` --- ## Real Smartphone Bridge ### Companion App A mobile app (Android/iOS) that: 1. Connects to same signaling server 2. Bridges to real phone's contacts, messages 3. Allows receiving calls from virtual phone 4. Sends notifications to virtual phone **Use Cases**: - Receive real SMS in virtual phone - Make real calls from VR - Sync contacts between real and virtual phone - Get push notifications in VR --- ## Implementation Plan ### Phase 1: Core WebRTC - [ ] Add libdatachannel to vcpkg.json - [ ] Implement WebRTCBridgeImpl - [ ] Basic signaling protocol ### Phase 2: Messaging - [ ] Data channel messaging - [ ] Message history storage - [ ] Messages app integration ### Phase 3: Voice Calls - [ ] Audio track setup - [ ] Microphone/speaker integration - [ ] Dialer app integration ### Phase 4: Signaling Server - [ ] Production signaling server - [ ] User authentication - [ ] Peer discovery service ### Phase 5: Companion App - [ ] Android companion app - [ ] iOS companion app (future) - [ ] Contact/message sync --- ## Network Requirements - UDP ports for WebRTC media - WebSocket for signaling - TURN server for NAT traversal (optional) **TURN/STUN Configuration**: ```cpp rtc::Configuration config; config.iceServers.emplace_back("stun:stun.l.google.com:19302"); config.iceServers.emplace_back("turn:turn.mosis.dev:3478", "user", "pass"); ``` --- ## Acceptance Criteria - [ ] Two virtual phones can exchange messages - [ ] Voice calls work between virtual phones - [ ] Files can be transferred between devices - [ ] Connection survives game restarts (rejoin) - [ ] Messages app shows real-time chat - [ ] Dialer app can place/receive calls