mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:27:19 +00:00
- ModuleManager: Central tracking for installed marketplace modules - DataAnalyzerWidget: Real-time CPU/RAM/Battery/Storage widget (unlocked by Data Analyzer module) - BottomNavBar: Navigation bar for Projects/Chat/Marketplace/Settings - RootShell: Real root command execution utility - TerminalActivity: Full root shell with neofetch, sysinfo, real Linux commands - Terminal Pro module: Adds aliases (ll, la, h), command history - ArcadeActivity + SnakeGame: Pixel Arcade module unlocks retro games - fade_in/fade_out animations for smooth transitions
677 lines
15 KiB
Markdown
677 lines
15 KiB
Markdown
# PHASE 4: VOICE & VIDEO CALLS - DOCUMENTATION
|
|
|
|
## Overview
|
|
|
|
Phase 4 implements WebRTC-based voice and video calling with support for:
|
|
- 1-on-1 audio and video calls
|
|
- Group calls with up to 20 participants
|
|
- Screen sharing
|
|
- TURN/STUN servers for NAT traversal
|
|
- Real-time media controls (mute, video toggle)
|
|
- Connection quality monitoring
|
|
- Call recording support (infrastructure)
|
|
|
|
## Architecture
|
|
|
|
### WebRTC Topology
|
|
|
|
**1-on-1 Calls**: Mesh topology with direct peer-to-peer connections
|
|
**Group Calls**: SFU (Selective Forwarding Unit) using Mediasoup (placeholder for future implementation)
|
|
|
|
### Components
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ Client A │◄───────►│ Server │◄───────►│ Client B │
|
|
│ (Browser) │ WebRTC │ Socket.io │ WebRTC │ (Browser) │
|
|
│ │ Signals │ Signaling │ Signals │ │
|
|
└─────────────┘ └─────────────┘ └─────────────┘
|
|
▲ │ ▲
|
|
│ │ │
|
|
│ ┌──────────▼──────────┐ │
|
|
└───────────►│ TURN/STUN Server │◄───────────┘
|
|
│ (NAT Traversal) │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
### Calls Table
|
|
|
|
```sql
|
|
CREATE TABLE calls (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
|
|
type VARCHAR(20) NOT NULL DEFAULT 'audio', -- 'audio', 'video', 'screen'
|
|
status VARCHAR(20) NOT NULL DEFAULT 'initiated',
|
|
-- Status: 'initiated', 'ringing', 'active', 'ended', 'missed', 'rejected', 'failed'
|
|
initiated_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
started_at TIMESTAMPTZ,
|
|
ended_at TIMESTAMPTZ,
|
|
duration_seconds INTEGER,
|
|
end_reason VARCHAR(50),
|
|
sfu_room_id VARCHAR(255),
|
|
recording_url TEXT,
|
|
quality_stats JSONB,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Call Participants Table
|
|
|
|
```sql
|
|
CREATE TABLE call_participants (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
call_id UUID NOT NULL REFERENCES calls(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'invited',
|
|
-- Status: 'invited', 'ringing', 'joined', 'left', 'rejected', 'missed'
|
|
joined_at TIMESTAMPTZ,
|
|
left_at TIMESTAMPTZ,
|
|
ice_candidates JSONB,
|
|
media_state JSONB DEFAULT '{"audioEnabled": true, "videoEnabled": true, "screenSharing": false}',
|
|
media_stats JSONB,
|
|
connection_quality VARCHAR(20),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### TURN Credentials Table
|
|
|
|
```sql
|
|
CREATE TABLE turn_credentials (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
username VARCHAR(255) NOT NULL,
|
|
credential VARCHAR(255) NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Auto-cleanup function
|
|
CREATE OR REPLACE FUNCTION cleanup_expired_turn_credentials()
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
DELETE FROM turn_credentials WHERE expires_at < NOW();
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### 1. POST `/api/calls/initiate`
|
|
|
|
Initiate a new call.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"conversationId": "uuid",
|
|
"type": "video", // or "audio"
|
|
"participantIds": ["uuid1", "uuid2"]
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"callId": "uuid",
|
|
"status": "initiated",
|
|
"participants": [
|
|
{
|
|
"userId": "uuid",
|
|
"userName": "John Doe",
|
|
"userIdentifier": "john@example.com",
|
|
"status": "invited"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 2. POST `/api/calls/:callId/answer`
|
|
|
|
Answer an incoming call.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"callId": "uuid",
|
|
"status": "active",
|
|
"startedAt": "2025-01-10T14:30:00Z"
|
|
}
|
|
```
|
|
|
|
### 3. POST `/api/calls/:callId/reject`
|
|
|
|
Reject an incoming call.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"callId": "uuid",
|
|
"status": "rejected"
|
|
}
|
|
```
|
|
|
|
### 4. POST `/api/calls/:callId/end`
|
|
|
|
End an active call.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"callId": "uuid",
|
|
"status": "ended",
|
|
"duration": 120,
|
|
"endReason": "ended-by-user"
|
|
}
|
|
```
|
|
|
|
### 5. PATCH `/api/calls/:callId/media`
|
|
|
|
Update media state (mute/unmute, video on/off).
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"audioEnabled": true,
|
|
"videoEnabled": false,
|
|
"screenSharing": false
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"mediaState": {
|
|
"audioEnabled": true,
|
|
"videoEnabled": false,
|
|
"screenSharing": false
|
|
}
|
|
}
|
|
```
|
|
|
|
### 6. GET `/api/calls/turn-credentials`
|
|
|
|
Get temporary TURN server credentials.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"credentials": {
|
|
"urls": ["turn:turn.example.com:3000"],
|
|
"username": "1736517600:username",
|
|
"credential": "hmac-sha1-hash"
|
|
},
|
|
"expiresAt": "2025-01-11T14:00:00Z"
|
|
}
|
|
```
|
|
|
|
### 7. GET `/api/calls/:callId`
|
|
|
|
Get call details.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"call": {
|
|
"id": "uuid",
|
|
"conversationId": "uuid",
|
|
"type": "video",
|
|
"status": "active",
|
|
"initiatedBy": "uuid",
|
|
"startedAt": "2025-01-10T14:30:00Z",
|
|
"participants": [...]
|
|
}
|
|
}
|
|
```
|
|
|
|
## WebSocket Events
|
|
|
|
### Client → Server
|
|
|
|
#### `call:offer`
|
|
Send WebRTC offer to peer.
|
|
|
|
```javascript
|
|
socket.emit('call:offer', {
|
|
callId: 'uuid',
|
|
targetUserId: 'uuid',
|
|
offer: RTCSessionDescription
|
|
});
|
|
```
|
|
|
|
#### `call:answer`
|
|
Send WebRTC answer to peer.
|
|
|
|
```javascript
|
|
socket.emit('call:answer', {
|
|
callId: 'uuid',
|
|
targetUserId: 'uuid',
|
|
answer: RTCSessionDescription
|
|
});
|
|
```
|
|
|
|
#### `call:ice-candidate`
|
|
Send ICE candidate to peer.
|
|
|
|
```javascript
|
|
socket.emit('call:ice-candidate', {
|
|
callId: 'uuid',
|
|
targetUserId: 'uuid',
|
|
candidate: RTCIceCandidate
|
|
});
|
|
```
|
|
|
|
### Server → Client
|
|
|
|
#### `call:incoming`
|
|
Notify user of incoming call.
|
|
|
|
```javascript
|
|
socket.on('call:incoming', (data) => {
|
|
// data: { callId, conversationId, type, initiatedBy, participants }
|
|
});
|
|
```
|
|
|
|
#### `call:offer`
|
|
Receive WebRTC offer from peer.
|
|
|
|
```javascript
|
|
socket.on('call:offer', (data) => {
|
|
// data: { callId, fromUserId, offer }
|
|
});
|
|
```
|
|
|
|
#### `call:answer`
|
|
Receive WebRTC answer from peer.
|
|
|
|
```javascript
|
|
socket.on('call:answer', (data) => {
|
|
// data: { callId, fromUserId, answer }
|
|
});
|
|
```
|
|
|
|
#### `call:ice-candidate`
|
|
Receive ICE candidate from peer.
|
|
|
|
```javascript
|
|
socket.on('call:ice-candidate', (data) => {
|
|
// data: { callId, fromUserId, candidate }
|
|
});
|
|
```
|
|
|
|
#### `call:ended`
|
|
Notify that call has ended.
|
|
|
|
```javascript
|
|
socket.on('call:ended', (data) => {
|
|
// data: { callId, reason, endedBy }
|
|
});
|
|
```
|
|
|
|
#### `call:participant-joined`
|
|
Notify that participant joined group call.
|
|
|
|
```javascript
|
|
socket.on('call:participant-joined', (data) => {
|
|
// data: { callId, userId, userName, userIdentifier }
|
|
});
|
|
```
|
|
|
|
#### `call:participant-left`
|
|
Notify that participant left group call.
|
|
|
|
```javascript
|
|
socket.on('call:participant-left', (data) => {
|
|
// data: { callId, userId }
|
|
});
|
|
```
|
|
|
|
#### `call:media-state-changed`
|
|
Notify that participant's media state changed.
|
|
|
|
```javascript
|
|
socket.on('call:media-state-changed', (data) => {
|
|
// data: { callId, userId, mediaState }
|
|
});
|
|
```
|
|
|
|
## Frontend Integration
|
|
|
|
### WebRTC Manager Usage
|
|
|
|
```javascript
|
|
import WebRTCManager from './utils/webrtc';
|
|
|
|
// Initialize
|
|
const webrtcManager = new WebRTCManager(socket);
|
|
|
|
// Set TURN credentials
|
|
const turnCreds = await fetch('/api/calls/turn-credentials');
|
|
await webrtcManager.setTurnCredentials(turnCreds.credentials);
|
|
|
|
// Get local media stream
|
|
const localStream = await webrtcManager.initializeLocalStream(true, true);
|
|
localVideoRef.current.srcObject = localStream;
|
|
|
|
// Setup event handlers
|
|
webrtcManager.onRemoteStream = (userId, stream) => {
|
|
remoteVideoRef.current.srcObject = stream;
|
|
};
|
|
|
|
// Initiate call
|
|
webrtcManager.currentCallId = callId;
|
|
webrtcManager.isInitiator = true;
|
|
await webrtcManager.initiateCallToUser(targetUserId);
|
|
|
|
// Toggle audio/video
|
|
webrtcManager.toggleAudio(false); // mute
|
|
webrtcManager.toggleVideo(false); // video off
|
|
|
|
// Screen sharing
|
|
await webrtcManager.startScreenShare();
|
|
webrtcManager.stopScreenShare();
|
|
|
|
// Cleanup
|
|
webrtcManager.cleanup();
|
|
```
|
|
|
|
### Call Component Usage
|
|
|
|
```javascript
|
|
import Call from './components/Call';
|
|
|
|
function App() {
|
|
const [showCall, setShowCall] = useState(false);
|
|
|
|
return (
|
|
<div>
|
|
{showCall && (
|
|
<Call
|
|
socket={socket}
|
|
conversationId="uuid"
|
|
participants={[
|
|
{ userId: 'uuid', userName: 'John Doe' }
|
|
]}
|
|
onCallEnd={(data) => {
|
|
console.log('Call ended:', data);
|
|
setShowCall(false);
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
## TURN Server Setup (Coturn)
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
# Ubuntu/Debian
|
|
sudo apt-get update
|
|
sudo apt-get install coturn
|
|
|
|
# Enable service
|
|
sudo systemctl enable coturn
|
|
```
|
|
|
|
### Configuration
|
|
|
|
Edit `/etc/turnserver.conf`:
|
|
|
|
```conf
|
|
# Listening port
|
|
listening-port=3000
|
|
tls-listening-port=5349
|
|
|
|
# External IP (replace with your server IP)
|
|
external-ip=YOUR_SERVER_IP
|
|
|
|
# Relay IPs
|
|
relay-ip=YOUR_SERVER_IP
|
|
|
|
# Realm
|
|
realm=turn.yourdomain.com
|
|
|
|
# Authentication
|
|
use-auth-secret
|
|
static-auth-secret=YOUR_TURN_SECRET
|
|
|
|
# Logging
|
|
verbose
|
|
log-file=/var/log/turnserver.log
|
|
|
|
# Security
|
|
no-multicast-peers
|
|
no-cli
|
|
no-loopback-peers
|
|
no-tlsv1
|
|
no-tlsv1_1
|
|
|
|
# Quotas
|
|
max-bps=1000000
|
|
user-quota=12
|
|
total-quota=1200
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
Add to `.env`:
|
|
|
|
```env
|
|
# TURN Server Configuration
|
|
TURN_SERVER_HOST=turn.yourdomain.com
|
|
TURN_SERVER_PORT=3000
|
|
TURN_SECRET=your-turn-secret-key
|
|
TURN_TTL=86400
|
|
```
|
|
|
|
### Firewall Rules
|
|
|
|
```bash
|
|
# Allow TURN ports
|
|
sudo ufw allow 3000/tcp
|
|
sudo ufw allow 3000/udp
|
|
sudo ufw allow 5349/tcp
|
|
sudo ufw allow 5349/udp
|
|
|
|
# Allow UDP relay ports
|
|
sudo ufw allow 49152:65535/udp
|
|
```
|
|
|
|
### Start Service
|
|
|
|
```bash
|
|
sudo systemctl start coturn
|
|
sudo systemctl status coturn
|
|
```
|
|
|
|
### Testing TURN Server
|
|
|
|
Use the [Trickle ICE](https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/) test page:
|
|
|
|
1. Add your TURN server URL: `turn:YOUR_SERVER_IP:3000`
|
|
2. Generate TURN credentials using the HMAC method
|
|
3. Click "Gather candidates"
|
|
4. Verify `relay` candidates appear
|
|
|
|
## Media Codecs
|
|
|
|
### Audio
|
|
- **Codec**: Opus
|
|
- **Sample Rate**: 48kHz
|
|
- **Bitrate**: 32-128 kbps (adaptive)
|
|
- **Features**: Echo cancellation, noise suppression, auto gain control
|
|
|
|
### Video
|
|
- **Codecs**: VP8, VP9, H.264 (fallback)
|
|
- **Clock Rate**: 90kHz
|
|
- **Resolutions**:
|
|
- 1280x720 (HD) - default
|
|
- 640x480 (SD) - low bandwidth
|
|
- 320x240 (LD) - very low bandwidth
|
|
- **Frame Rate**: 30 fps (ideal), 15-60 fps range
|
|
- **Bitrate**: 500kbps-2Mbps (adaptive)
|
|
|
|
## Connection Quality Monitoring
|
|
|
|
The system monitors connection quality based on:
|
|
|
|
1. **Round Trip Time (RTT)**
|
|
- Good: < 100ms
|
|
- Fair: 100-300ms
|
|
- Poor: > 300ms
|
|
|
|
2. **Packet Loss**
|
|
- Good: < 2%
|
|
- Fair: 2-5%
|
|
- Poor: > 5%
|
|
|
|
3. **Available Bitrate**
|
|
- Good: > 500kbps
|
|
- Fair: 200-500kbps
|
|
- Poor: < 200kbps
|
|
|
|
Quality is checked every 3 seconds and displayed to users.
|
|
|
|
## Error Handling
|
|
|
|
### Common Errors
|
|
|
|
1. **Media Access Denied**
|
|
```
|
|
Failed to access camera/microphone: NotAllowedError
|
|
```
|
|
- User denied browser permission
|
|
- Solution: Request permission again, show help dialog
|
|
|
|
2. **ICE Connection Failed**
|
|
```
|
|
Connection failed with user: ICE connection failed
|
|
```
|
|
- NAT/firewall blocking connection
|
|
- Solution: Ensure TURN server is configured and reachable
|
|
|
|
3. **Peer Connection Closed**
|
|
```
|
|
Connection closed with user: Connection lost
|
|
```
|
|
- Network interruption or user disconnected
|
|
- Solution: Notify user, attempt reconnection
|
|
|
|
4. **Turn Credentials Expired**
|
|
```
|
|
TURN credentials expired
|
|
```
|
|
- Credentials have 24-hour TTL
|
|
- Solution: Fetch new credentials automatically
|
|
|
|
## Security Considerations
|
|
|
|
1. **TURN Authentication**: Time-limited credentials using HMAC-SHA1
|
|
2. **DTLS**: WebRTC encrypts all media streams with DTLS-SRTP
|
|
3. **JWT Auth**: All API calls require valid JWT token
|
|
4. **Rate Limiting**: Protect against DoS attacks
|
|
5. **User Verification**: Verify users are in conversation before allowing calls
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] 1-on-1 audio call works
|
|
- [ ] 1-on-1 video call works
|
|
- [ ] Mute/unmute audio works
|
|
- [ ] Toggle video on/off works
|
|
- [ ] Screen sharing works
|
|
- [ ] Call can be answered
|
|
- [ ] Call can be rejected
|
|
- [ ] Call can be ended
|
|
- [ ] Connection quality indicator updates
|
|
- [ ] Call duration displays correctly
|
|
- [ ] Multiple participants can join (group call)
|
|
- [ ] Participant joins/leaves notifications work
|
|
- [ ] Media state changes propagate
|
|
- [ ] TURN server fallback works (test behind NAT)
|
|
- [ ] Call persists after page refresh (reconnection)
|
|
- [ ] Missed call notifications work
|
|
- [ ] Call history is recorded
|
|
|
|
## Performance Optimization
|
|
|
|
### Bandwidth Usage
|
|
|
|
**Audio Only (per participant)**:
|
|
- Opus @ 32kbps: ~15 MB/hour
|
|
- Opus @ 64kbps: ~30 MB/hour
|
|
|
|
**Video + Audio (per participant)**:
|
|
- 480p @ 500kbps: ~225 MB/hour
|
|
- 720p @ 1Mbps: ~450 MB/hour
|
|
- 1080p @ 2Mbps: ~900 MB/hour
|
|
|
|
### Recommendations
|
|
|
|
1. **Start with audio only** for low bandwidth users
|
|
2. **Use VP9** if supported (better compression than VP8)
|
|
3. **Enable simulcast** for group calls (SFU)
|
|
4. **Adaptive bitrate** based on network conditions
|
|
5. **Limit group calls** to 20 participants max
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] **Mediasoup SFU**: Implement actual SFU for efficient group calls
|
|
- [ ] **Call Recording**: Record and store calls in cloud storage
|
|
- [ ] **Background Blur**: Virtual backgrounds using ML
|
|
- [ ] **Noise Cancellation**: Advanced audio processing
|
|
- [ ] **Grid/Speaker View**: Different layouts for group calls
|
|
- [ ] **Reactions**: Emoji reactions during calls
|
|
- [ ] **Hand Raise**: Signal to speak in large calls
|
|
- [ ] **Breakout Rooms**: Split large calls into smaller groups
|
|
- [ ] **Call Scheduling**: Schedule calls in advance
|
|
- [ ] **Call Analytics**: Detailed quality metrics and reports
|
|
|
|
## Troubleshooting
|
|
|
|
### No Audio/Video
|
|
|
|
1. Check browser permissions
|
|
2. Verify camera/microphone is not used by another app
|
|
3. Test with `navigator.mediaDevices.enumerateDevices()`
|
|
4. Check browser console for errors
|
|
|
|
### Connection Fails
|
|
|
|
1. Test TURN server with Trickle ICE
|
|
2. Verify firewall allows UDP ports 49152-65535
|
|
3. Check TURN credentials are not expired
|
|
4. Ensure both users are online
|
|
|
|
### Poor Quality
|
|
|
|
1. Check network bandwidth
|
|
2. Monitor packet loss and RTT
|
|
3. Reduce video resolution
|
|
4. Switch to audio-only mode
|
|
|
|
### Echo/Feedback
|
|
|
|
1. Ensure `echoCancellation: true` in audio constraints
|
|
2. Use headphones instead of speakers
|
|
3. Reduce microphone gain
|
|
4. Check for multiple audio sources
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
- Check logs in browser console
|
|
- Review `/var/log/turnserver.log` for TURN issues
|
|
- Monitor backend logs for signaling errors
|
|
- Test with multiple browsers (Chrome, Firefox, Safari)
|
|
|
|
---
|
|
|
|
**Phase 4 Complete** ✓
|