WebRTC / WHEP Proxy
App Container includes an embedded WHEP (WebRTC HTTP Egress Protocol) server engine inside its Go backend daemon running directly on the touch panel. This service bridges local RTSP IP camera streams to native browser-compatible WebRTC, allowing web applications to stream live video directly inside any standard HTML <video> element - without plugins or external media servers.
⚡ Key Highlights
- Direct RTP Packet Forwarding: Zero video transcoding overhead. Packets are rewritten (SSRC/PT/SN) and forwarded directly to WebRTC peer connections for minimal CPU consumption.
- H.264 Video Support: Fully compatible with H.264 network camera streams. (Note: H.265 is currently unsupported over WebRTC).
- High Concurrency: Supports up to 8 simultaneous active stream sessions per device.
- Zero Configuration ICE: Operates on localhost ICE candidate loops - because both the web application runtime and the Go container backend run locally on the same device, external STUN or TURN servers are completely unnecessary.
📡 WHEP API Endpoints
The WHEP proxy service runs at the container base address:
| Method | Endpoint | Description |
|---|---|---|
POST | /api/stream/whep/{stream-id} | Start a new WHEP WebRTC stream session |
DELETE | /api/stream/whep/{stream-id} | Stop an active WHEP stream session |
GET | /api/stream/sessions | List all active stream sessions (max 8) |
🚀 Starting a WHEP Session
To establish a stream connection:
- Create a local browser
RTCPeerConnection. - Generate an SDP offer with video/audio receive-only transceivers.
- Send the SDP offer along with the target RTSP stream URL to
POST /api/stream/whep/{stream-id}. - Set the returned SDP answer as the remote description on your peer connection.
Request Format
POST /api/stream/whep/cam1
Content-Type: application/json
{
"url": "rtsp://192.168.1.100:554/stream",
"sdp": "v=0\r\no=- 123456 2 IN IP4 127.0.0.1..."
}
Response (200 OK)
{
"sdp": "v=0\r\no=- 654321 2 IN IP4 127.0.0.1..."
}
💻 Full Browser JavaScript Example
Below is a production-ready JavaScript snippet for mounting an RTSP camera stream directly onto an HTML <video> element using WHEP:
async function startWhepStream(streamId, rtspUrl, videoElement) {
// 1. Create RTCPeerConnection
const pc = new RTCPeerConnection({
iceServers: [] // Localhost ICE connection only
});
// 2. Add receive-only transceivers for video and audio
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addTransceiver('audio', { direction: 'recvonly' });
// 3. Attach incoming media track to <video> element
pc.ontrack = (event) => {
if (event.streams && event.streams[0]) {
videoElement.srcObject = event.streams[0];
}
};
// 4. Create and set local SDP offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 5. Exchange SDP offer/answer with App Container WHEP backend
const response = await fetch(`http://localhost:8080/api/stream/whep/${streamId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: rtspUrl,
sdp: offer.sdp
})
});
if (!response.ok) {
throw new Error(`WHEP session failed: ${response.statusText}`);
}
// 6. Apply returned SDP answer
const { sdp } = await response.json();
await pc.setRemoteDescription({
type: 'answer',
sdp: sdp
});
return pc;
}
// Example usage:
const videoEl = document.getElementById('camera-preview');
startWhepStream('cam1', 'rtsp://192.168.1.100:554/stream', videoEl)
.then(() => console.log('WHEP stream connected successfully'))
.catch(err => console.error('WHEP stream error:', err));
🛑 Terminating a Session
To stop a stream and free backend resources:
async function stopWhepStream(streamId, pc) {
if (pc) {
pc.close();
}
await fetch(`http://localhost:8080/api/stream/whep/${streamId}`, {
method: 'DELETE'
});
}